Kod, który jest modułowy, elastyczny i którego zależności są dobrze zarządzane, jest uważany za wysoce spójny i luźno powiązany. Terminy te są najczęściej używane w środowiskach zorientowanych obiektowo, ale ogólnie mają zastosowanie do każdego systemu. Wysoce spójne oznacza, że rzeczy, które mają być razem, są. Luźno powiązane oznacza, że rzeczy, które nie powinny być razem, nie są. Poniższy rysunek przedstawia te cechy, w których każdy z okręgów może być funkcją lub ogólnie obiektem. To są podstawy zarządzania zależnościami. Wiele książek poświęconych tym zagadnieniom zostało i nadal jest publikowanych.
Najważniejsze zasady dotyczące kodu wysokiej jakości to:
- Zrób rzeczy małe i skupione na jednej odpowiedzialności.
- Niech konkrety zależą od abstrakcji (nie odwrotnie).
- Twórz rzeczy, które są wysoce spójne i luźno powiązane.
Zaczynamy od utworzenia dwóch plików: functions.R i main.R. Plik function.R zawiera funkcje wysokiego poziomu (głównie wywoływane z pliku main.R), a także funkcje niskiego poziomu (używane w innych funkcjach). Czytając plik main.R, powinniśmy mieć jasne wyobrażenie o tym, co robi analiza (taki jest cel funkcji wysokiego poziomu), a wykonanie jej powinno odtworzyć naszą analizę dla wszelkich danych, które pasują do naszych podstawowe założenia (w tym przykładzie są to głównie struktury danych). Powinniśmy zawsze utrzymywać powiązany kod na tym samym poziomie abstrakcji. Oznacza to, że nie chcemy programować rzeczy na ogólnym poziomie i implementować ich z mieszanymi szczegółami, a rozdzielanie naszego kodu na main.R i function.R jest pierwszy krok w tym kierunku. Ponadto żaden kod w pliku main.R nie powinien zależeć od szczegółów implementacji. To czyni go modułowym w tym sensie, że jeśli chcemy zmienić sposób implementacji czegoś, możemy to zrobić bez konieczności zmiany kodu wysokiego poziomu. Jednak sposób, w jaki implementujemy rzeczy, zależy od tego, co chcemy, aby analiza ostatecznie zrobiła, co oznacza, że konkretne implementacje powinny zależeć od implementacji abstrakcyjnych, które z kolei zależą od celu naszej analizy (określonego jako kod w pliku main.R). Kiedy przenosimy wiedzę z jednego zestawu kodu do innego, generujemy zależność, ponieważ kod, który wie o innym kodzie, zależy od tego, czy działa poprawnie. Chcemy jak najbardziej unikać tych zależności, a co najważniejsze – kierować ich kierunkiem. Jak stwierdzono wcześniej, streszczenie nie powinno zależeć od konkretu, ani inaczej mówiąc, konkret powinien zależeć od abstraktu. Ponieważ analiza (main.R) jest po stronie abstrakcyjnej nie powinno zależeć od szczegółów implementacji konkretnych funkcji. Ale jak można przeprowadzić naszą analizę bez znajomości funkcji, które ją wdrażają? Cóż, nie może. Dlatego potrzebujemy pośrednika, funkcji abstrakcyjnych. Funkcje te mają na celu main.R dostarczenie stabilnej wiedzy i zagwarantowanie, że analiza, której poszukuje, zostanie wykonana, a także eliminują zależność implementacji od szczegółów implementacji poprzez samodzielne zarządzanie tą wiedzą. Może się to wydawać zawiłym sposobem pracy i trudną koncepcją do zrozumienia, ale kiedy to zrobisz, przekonasz się, że jest to bardzo proste i będziesz w stanie stworzyć kod, który można podłączyć, co jest dużym wzrostem wydajności. Możesz zajrzeć do wcześniej wspomnianych książek, aby uzyskać głębszy sens tych koncepcji.