DRY (Don't Repeat Yourself) to reguła mówiąca o tym, że każdy fragment wiedzy powinien mieć jedną i jednoznaczną reprezentacje w systemie. Zaleca unikanie wszelkiego rodzaju powtórzeń czynności wykonywanych przez zespół deweloperski w procesie wytwarzania oprogramowania. Dotyczy zatem nie tylko powtórzeń kodu źródłowego lecz także innych zadań zawierających się w procesie tworzenia i konfiguracji projektu np. w trakcie kompilacji czy tworzenia dokumentacji. Może być rozpatrywana w jeszcze szerszym kontekście dążenia do automatyzacji procesów, które pozwalają na redukcje powtarzających się czynności manualnych. W przypadku kodu źródłowego stosowane są różne techniki eliminujące powtórzenia na wielu poziomach struktury kodu. Złamanie zasady DRY dotyczy przede wszystkim duplikacji dziedziny wiedzy, niekoniecznie duplikatu samego kodu. System niespełniający reguły DRY w sposób zadowalający może być nazywany terminem WET (Write Everything Twice, We Enjoy Typing).
Zalety
Stosowanie się do zasady DRY umożliwia przede wszystkim uniknięcie problemów wynikających z błędów popełnionych w powtarzającym się kodzie. W przypadku znalezienia wadliwego fragmentu wystarczy zmiana w jednym miejscu zamiast w każdym powtarzającym się bloku. Podobna zależność zachodzi dla modyfikacji wybranego bloku kodu. Pozwala to na optymalizację kosztów poniesionych w procesach diagnozy, naprawy, rozwoju i testowania oprogramowania. Ponadto implikacja reguły wymusza w pewnym stopniu specyfikacje funkcjonalności i interfejsów dążąc do rozwiązań uniwersalnych.
Zagrożenia
Nie warto na siłę próbować implementować zasadę DRY ponieważ może się zdarzyć, że na pozór identyczne kody różnią się szczegółem lub realizują różną funkcjonalność, która możę w przyszłości ulec zmianie. Dlatego przed podjęciem dalszych kroków należy upewnić się o słuszności decyzji. Dobrą praktyką jest pisanie czytelnego i rozwijalnego kodu o krótkich funkcjach z jedną odpowiedzialnością. Jednakże pomimo tego należy odpowiednio wyważyć jego atomowość unikając zbyt dużego podziału, który może zwiększyć jego złożoność i powiązanie. Jeśli dany kod istnieje w jednym miejscu być może dobrym rozwiązaniem będzie wstrzymanie się z wydzieleniem go do osobnej funkcji do momentu hipotetycznego drugiego wystąpienia.
Funkcje
Oczywistym sposobem na zmniejszenie ilości powtórzeń jest umieszczanie bloku kodu w ciele funkcji, która może zostać użyta wielokrotnie w wielu miejscach aplikacji. Należy także wziąć pod uwagę czy istnieje możliwość i zachodzi sens szukania rozwiązania generalnego poprzez funkcję z parametrami wejściowymi.
Walidacja hasła zachodzi w ten sam sposób w conajmniej dwóch miejscach: RegisterView, ChangePasswordView. Ponadto widok rejestracji sprawdza poprawność wpisanego adresu email. Można przypuszczać, że istnieje spora szansa na ponowne wykorzystanie metod walidacyjnych w innych miejscach aplikacji. W związku z tym warto przenieść metody walidacyjne do jednej klasy pomocniczej Validator dodatkowo zachowując ich spójność w całym kodzie.
Stałe
Warto rozważyć zastosowanie zarówno stałych lokalnych jak i globalnych, które poza redukcją powielania zwiększają czytelność kodu w wyniku zastępienia niekoniecznie zrozumiałych nazw zmiennych.
Metody klasy pomocnicznej DateUtils wykorzystują w swoich obliczeniach mnożnik reprezentujący ilość milisekund w dniu. Aby zaoszczędzić powielenia kodu i uniknąć pomyłki należy stworzyć stałą MILISECONDS_IN_DAYS i wykorzystać ją w metodach.
Typy
Wykonanie żądanej operacji może się wiązać z przekazaniem argumentów i zwróceniem wyniku, które mogą się składać z kilku obiektów. W takiej sytuacji zasadnym może być stworzenie nowego typu danych zawierającego obiekty typów argumentów wejściowych lub wyjściowych.
Metody klasy PlayerManager operują na wielu parametrach różnego typu w celu zastosowania operacji na danych zawodnika. Wymienione argumenty są ze sobą logicznie powiązane i mogą składać się na pola klasy Player. Wówczas część odpowiedzialności z klasy PlayerManager mogłaby zostać przeniesiona do klasy Player.
Polimorfizm i dziedziczenie
Polimorfizm umożliwia spójne wykorzystanie elementów na kilka różnych sposobów niezależnie od ich typów za pomocą jednego wspólnego interfejsu. Eliminuje instrukcje warunkowe zależne od typu i redukuje powtarzający się kod. Mechanizm dziedziczenia pozwala na wykorzystanie współdzielonego kodu typu bazowego przez jego podtypy.
Klasy Book, Article, Film, Music posiadają właściwości i cechy wspólne, które powinny zostać wyabstrachowane do typu wspólnego Item. Ponadto klient wykorzystujący te obiekty jest zmuszony do sprawdzania typów i rzutowania, aby dokonać wydruku elementów papierowych. Zachowanie opisujące możliwość wydruku może zostać przeniesione do interfejsu Printable i jego implementacji w wybranych klasach.
Moduły
Dzielenie kodu na moduły umożliwia nie tylko wyróżnienie części logicznych i funkcjonalnych aplikacji, ale może ułatwić ponowne użycie danej funkcjonalności w innym projekcie. W szczególności są to zewnętrzne biblioteki i wtyczki.
Klasa Calculator dostarcza zbiór metod wykonujących obliczenia matematyczne. Część definicji zawiera się jednak w pakiecie matematycznym Math w związku z czym mogą one zostać wykorzystane zamiast autorskiej implementacji.