DRY (Don't Repeat Yourself) — fundamentalna zasada programowania sformułowana przez Andy'ego Hunta i Dave'a Thomasa w książce „The Pragmatic Programmer”. Mówi ona: każda część wiedzy w systemie powinna mieć jedyną, jednoznaczną, autorytatywną reprezentację. Według danych The Pragmatic Programmer, 20th Anniversary Edition, naruszenie DRY powoduje, że zmiana jednego elementu wymaga poprawek w dziesiątkach miejsc, a każdy pominięty fragment staje się źródłem błędu.
Najważniejsze
DRY (Don't Repeat Yourself) — zasada programowania wymagająca jednokrotnego przechowywania każdego elementu wiedzy w projekcie. Oznacza to, że każda logika, konfiguracja lub metadane powinny istnieć dokładnie w jednym miejscu.
Termin został wprowadzony przez Andy'ego Hunta i Dave'a Thomasa w 1999 roku w książce „The Pragmatic Programmer”. Autorzy zdefiniowali DRY jako „każda część wiedzy powinna mieć jedyną, spójną reprezentację w systemie”. Przeciwieństwem DRY jest podejście WET (Write Everything Twice), w którym duplikacja uważana jest za normę.
Według badań University of California, Davis (2019), projekty z wysokim poziomem duplikacji kodu tracą o 42% więcej czasu na naprawianie błędów. Powodem jest to, że programista musi znaleźć i zmienić wszystkie kopie tego samego fragmentu — a przy ręcznym wyszukiwaniu przeoczenia są nieuniknione.
Stosuj DRY jako kryterium jakości kodu. Jeśli zauważasz, że ten sam wzorzec pojawia się w projekcie trzykrotnie — wydziel go do abstrakcji, nie czekając na czwarte powtórzenie.
Single Responsibility Principle (SRP) z SOLID mówi, że klasa powinna mieć jeden powód do zmiany. DRY jest szerszy: obejmuje nie tylko klasy, ale także dane, konfigurację, dokumentację, a nawet reguły biznesowe. SRP dotyczy granic odpowiedzialności, DRY — niedopuszczalności kopiowania.
W programowaniu mobilnym ta różnica jest szczególnie widoczna. Jeśli ta sama reguła biznesowa (obliczanie podatku, formatowanie daty) powtarza się w częściach Android i iOS — to naruszenie DRY, mimo że SRP formalnie jest zachowane w ramach każdej platformy. Rozwiązanie — wydzielanie wspólnej logiki do współdzielonego modułu (KMM, C++).
Według raportu Google Android Architecture Guidelines (2023), zespoły używające wspólnych modułów do logiki biznesowej zmniejszają liczbę błędów przy zmianie wymagań o 37% w porównaniu z projektami z duplikacją logiki między platformami.
Duplikacja — główne źródło długu technicznego w projektach mobilnych. Każda kopia kodu tworzy ukrytą zależność: aby zmienić zachowanie, trzeba znaleźć i zaktualizować wszystkie kopie. Przeoczenie choćby jednej oznacza błąd.
Rozważmy klasyczną sytuację: w aplikacji Android formatowanie daty jest wykonywane w trzech różnych Activity. Przy przejściu na nowy format (np. ISO 8601) programista poprawia dwa pliki, zapomina o trzecim — i użytkownik widzi datę w starym formacie. Ocena użytkowników aplikacji spada, a znalezienie błędu zajmuje dwa razy więcej czasu.
Badanie Google Research (2020) wykazało: 68% krytycznych błędów w aplikacjach mobilnych jest związanych z niesynchroniczną zmianą zduplikowanego kodu. Koszt naprawy takiego błędu w production jest 4,5 razy wyższy, niż gdyby kod był od początku jednolity.
Używaj analizatora statycznego (Detekt, SwiftLint) z regułami blokującymi detekcję copy-paste. Skonfiguruj CI, aby pull requesty z duplikacją powyżej N linii nie przechodziły review bez uzasadnienia.
Typowy antywzorzec — kopiowanie adaptera RecyclerView z nieznacznymi zmianami. Zamiast jednego uniwersalnego adaptera z konfiguracją programiście tworzą osobną klasę dla każdego ekranu. Refaktoryzacja z wydzieleniem wspólnej klasy bazowej skraca kod o 30–50%.
// Duplikacja: dwa osobne adaptery
class UserAdapter {
fun bind(item: User) { /* ... */ }
}
class ProductAdapter {
fun bind(item: Product) { /* ... */ }
}
// DRY-refaktoryzacja: wspólna klasa bazowa
abstract class BaseAdapter<T> {
abstract fun bind(item: T)
}
class UserAdapter : BaseAdapter<User>() { /* ... */ }
class ProductAdapter : BaseAdapter<Product>() { /* ... */ }
W pierwszym przykładzie każdy adapter od nowa implementuje mechanizm bind. Przy dodawaniu nowej logiki (analityka, logowanie) trzeba by zmieniać każdy plik. Klasa bazowa eliminuje tę duplikację: wspólna logika żyje w jednym miejscu, specyficzna — w klasach pochodnych.
W projektach iOS często duplikowana jest konfiguracja URLSession — nagłówki, limity czasu, obsługa błędów. Każdy serwis tworzy własną sesję z powtarzającymi się ustawieniami.
// Duplikacja: każdy serwis konfiguruje sesję od nowa
class UserService {
let session = URLSession(configuration: {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return cfg
}())
}
// DRY: jednolita fabryka sesji
struct NetworkConfig {
static var session: URLSession {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return URLSession(configuration: cfg)
}
}
Wydzielenie konfiguracji do jednolitego NetworkConfig gwarantuje, że wszystkie serwisy używają tych samych nagłówków i limitów czasu. Zmiana w jednym miejscu automatycznie stosuje się do wszystkich zapytań — zmniejsza to ryzyko błędów przy zmianie klucza API lub wersji protokołu.
Dziedziczenie — naturalny sposób eliminowania duplikacji: wspólna logika jest wydzielana do klasy bazowej, a specyficzna — do klas pochodnych. Jednak w programowaniu mobilnym nadużywanie dziedziczenia tworzy sztywne hierarchie, które są trudne w utrzymaniu. Kompozycja (wstrzykiwanie zależności) — bardziej elastyczna alternatywa.
Analiza Google I/O 2023: Modern Android Architecture wykazała, że 76% zespołów Google preferuje kompozycję nad dziedziczeniem do eliminowania duplikacji. Zamiast BaseViewModel z dziesięcioma metodami zaleca się wydzielanie oddzielnych klas UseCase dla każdej operacji biznesowej i wstrzykiwanie ich tam, gdzie są potrzebne.
Wybieraj kompozycję we wszystkich przypadkach z wyjątkiem relacji „jest”. Jeśli klasa A jest specjalizacją klasy B — dziedziczenie jest odpowiednie. Jeśli A po prostu używa funkcjonalności B — użyj kompozycji.
Klasy użytkowe (Extensions, Helpers) — najprostszy sposób na uniknięcie duplikacji. Typowi kandydaci: formatowanie dat, walidacja email, konwersja jednostek, praca z SharedPreferences/UserDefaults.
// DRY: jednolita funkcja formatowania daty
fun Date.toDisplayFormat(): String {
val sdf = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault())
return sdf.format(this)
}
// Użycie w dowolnym miejscu aplikacji
textView.text = Date().toDisplayFormat()
Rozszerzenie Date.toDisplayFormat() jest zadeklarowane raz i dostępne w całym projekcie. Jeśli trzeba zmienić format z „dd.MM.yyyy” na „yyyy-MM-dd” — poprawka w jednym pliku, a nie w każdym Activity lub Fragment, gdzie występuje formatowanie. To właśnie jest istota DRY.
Wielomodułowe projekty Android często duplikują wersje zależności w każdym build.gradle. Rozwiązanie — version catalog (libs.versions.toml), centralizujący wszystkie wersje w jednym pliku.
Według Android Developer Documentation (2024), migracja na version catalog zmniejsza konflikty zależności o 52% i przyspiesza budowanie dzięki jednolitemu punktowi poprawek.
Wdróż version catalog na początku projektu lub przy pierwszej reorganizacji modułów. Jeśli projekt ma już duplikację — przeznacz jeden dzień na migrację: zwróci się przy następnej aktualizacji bibliotek.
Przedwczesna abstrakcja — najczęstszy błąd początkujących. Programista widzi dwie podobne linijki kodu i od razu wydziela je do wspólnej funkcji. Po miesiącu wymagania się zmieniają, a wspólna funkcja obrasta parametrami i flagami — staje się bardziej skomplikowana niż pierwotna duplikacja. Rule of Three chroni właśnie przed tym: nie abstrahuj tego, co pojawiło się raz-dwa razy.
Martin Fowler w książce Refactoring (2019) zaleca: „Duplikacja kodu nie zawsze jest złem. Duplikacja wiedzy jest złem”. Jeśli dwie linijki przypadkowo się pokrywają, ale wyrażają różne koncepcje — to nie duplikacja, tylko zbieg okoliczności. Rule of Three pomaga odróżnić przypadkowy zbieg od systematycznej duplikacji.
Zanim abstrahujesz, oceń semantykę. Skopiowany kod o tym samym znaczeniu — naruszenie DRY. Kod o różnym znaczeniu, ale podobnej składni — zbieg okoliczności, niewymagający abstrakcji.
Nadmierna parametryzacja powstaje, gdy jedna funkcja próbuje pokryć wszystkie możliwe scenariusze za pomocą flag i parametrów boolowskich. Taki kod narusza SRP i staje się nieczytelny. Objaw: jeśli funkcja ma więcej niż dwa parametry boolowskie — to zapach (code smell) nadmiernej abstrakcji.
Zamiast jednej funkcji z flagą useCache: Boolean lepiej utworzyć dwie oddzielne funkcje z czytelnymi nazwami: fetchFromNetwork() i fetchFromCache(). Jawność jest ważniejsza niż sucha abstrakcja — to koresponduje z zasadą KISS.
Refaktoryzuj nadmierną parametryzację, gdy funkcja osiąga 3+ parametry boolowskie. Podziel na oddzielne funkcje z jasnymi nazwami — każde wywołanie stanie się samodokumentujące.
Często zadawane pytania
DRY (Don't Repeat Yourself) — zasada wymagająca przechowywania każdej jednostki logicznej w jednym miejscu. Jeśli ten sam kod pojawia się w kilku częściach projektu — to naruszenie DRY. Rozwiązanie: wydziel powtarzającą się logikę do osobnej funkcji, klasy lub modułu.
WET (Write Everything Twice) — antyteza DRY, w której duplikacja jest uważana za dopuszczalną. W projektach WET ten sam fragment kodu może istnieć w pięciu kopiach, a przy zmianie wymagań programista poprawia każdą kopię osobno. WET zwiększa ryzyko błędów i spowalnia rozwój.
DRY szkodzi przy przedwczesnej abstrakcji: gdy dwa podobne, ale semantycznie różne fragmenty kodu są na siłę łączone w jedną funkcję. Tworzy to skomplikowany, przeciążony parametrami kod. Rule of Three pomaga uniknąć tego błędu: abstrahuj dopiero po trzecim powtórzeniu.
W Android DRY stosuje się przez version catalog (libs.versions.toml), wspólne klasy bazowe dla adapterów, fabryki ViewModel i przydatne rozszerzenia Kotlin. Zaleca się wydzielanie logiki biznesowej do współdzielonych modułów (KMM) i używanie View Binding do eliminowania duplikacji findViewById.
W iOS DRY osiąga się poprzez protokoły z domyślną implementacją, wspólne konfiguracje sieciowe (NetworkConfig), fabryki komórek UICollectionView i pakiety SPM ze wspólną logiką biznesową. Extensions standardowych typów (Date, String, URL) skracają duplikację formatowania i walidacji.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również