Dlaczego DRY jest ważne?


81

Całkiem proste, dlaczego miałbym chcieć pisać kod, który działa dla wszystkich przypadków i skalowalnych danych, gdy wszystko, co muszę zrobić, to powtórzyć ten sam proces kilka razy z kilkoma drobnymi poprawkami?

Prawdopodobnie nie będę musiał ponownie tego edytować w najbliższym czasie.

Wygląda na to, że po prostu dużo pracy jest mniej ...

function doStuff1(){/*.a.*/}
function doStuff2(){/*.b.*/}
function doStuff3(){/*.c.*/}

A jeśli kiedykolwiek będę musiał coś dodać ...

function doStuff4(){/*.d.*/}

A jeśli muszę to usunąć, usuwam to.

Trudniej jest wymyślić, jak połączyć je wszystkie w jeden prosty wzorzec, do którego mogę po prostu wprowadzić dane i poradzić sobie ze wszystkimi przypadkami, i wprowadzić szereg zmian, których nie wydaje mi się, żebym kiedykolwiek miał do zrobienia.

Po co wysychać, kiedy wygląda na to, że szybkie cięcie i wklejanie będzie znacznie mniej pracy?


11
ponieważ wysychanie jest jeszcze szybsze, gdy zrobisz to dobrze, a co jeśli popełnisz błąd w. wpływa to na wszystkie pozostałe
Daniel Little

97
„Prawdopodobnie nie będę musiał ponownie tego edytować w najbliższym czasie” - możesz mieć nadzieję, ale najprawdopodobniej popełniasz tutaj błąd. A jeśli zamierzasz ponownie popracować nad tym kodem, ale nie tak szybko, tylko pogorszy to sytuację; zapomnisz, gdzie są duplikaty, a duplikaty będą narastały subtelne, ale zdradzieckie rozbieżności. „Pisz tak, jakby osoba, która zachowa Twój kod, to niebezpieczny maniak, który wie, gdzie mieszkasz”, cytując klasykę.
9000

14
Myślę, że można to podsumować: Łatwiej jest utrzymać pojedynczy punkt zmiany.
Falcon

17
Jeśli nie potrafisz sam na to odpowiedzieć, musisz zdobyć więcej doświadczenia w zakresie rozwoju i utrzymania w świecie rzeczywistym.
David Heffernan

15
@Wayne Poczułem wielkie zakłócenia w źródle, tak jakby miliony programistów nagle krzyknęły z przerażenia.
Incognito

Odpowiedzi:


121

Jeśli się powtórzysz, możesz stworzyć problemy z utrzymaniem. Jeśli wszystkie doStuff1-3 mają kod o podobnej strukturze, a problem został rozwiązany w jednym, łatwo można zapomnieć o rozwiązaniu problemu w innym miejscu. Ponadto, jeśli musisz dodać nowy przypadek do obsługi, możesz po prostu przekazać różne parametry do jednej funkcji zamiast kopiować i wklejać w dowolnym miejscu.

Jednak sprytni programiści często podchodzą do skrajności. Czasami, aby się nie powtarzać, trzeba tworzyć abstrakcje tak tępe, że członkowie drużyny nie mogą ich przestrzegać. Czasami struktura dwóch rzeczy jest tylko nieco podobna, ale wystarczająco inna. Jeśli doStuff1-4 są na tyle różne, że przefakturowanie ich w taki sposób, aby się nie powtarzało, powoduje, że musisz napisać nienaturalny kod lub przejść sprytne kodowanie w tył, co spowoduje, że twój zespół będzie na ciebie patrzył, to może się powtórzyć. Pochyliłem się, żeby nie powtórzyć się kilka razy w nienaturalny sposób i żałowałem produktu końcowego.

Zawsze mylę się po stronie DRY, w rzadkich przypadkach powtarzam się, gdy uważam, że korzyści z czytelności są warte ryzyka, że ​​ktoś zapomni naprawić błąd w wielu miejscach.

Biorąc to pod uwagę, brzmi to jak w twoim przypadku

powtórz ten sam proces kilka razy z kilkoma drobnymi poprawkami

Na pewno ciężko pracowałbym, aby nie powtórzyć się w twoim przypadku. Zakładając minimalne „poprawki” - można sobie z nimi poradzić przy użyciu różnych parametrów, które wpływają na zachowanie lub być może wstrzykiwanych w celu wykonania różnych podzadań.

Po co wysychać, kiedy wygląda na to, że szybkie cięcie i wklejanie będzie znacznie mniej pracy?

Znane ostatnie słowa. Będziesz żałować, że kiedy młodszy inżynier poprawia / naprawia / refaktoryzuje jeden doStuff i nawet nie zdaje sobie sprawy, że inne istnieją. Następuje wesołość. Nie następuje głównie zgaga. Każda linia kodu kosztuje więcej. Ile ścieżek kodu musisz przetestować przy tak wielu powtarzanych funkcjach? Jeśli jedna funkcja, wystarczy przetestować jedną główną ścieżkę z kilkoma modyfikacjami behawioralnymi. W przypadku wklejenia kopii należy przetestować każdy doStuff osobno. Możliwe, że umknie Ci jeden, a klient może mieć niepożądany błąd i może mieć niepożądane wiadomości e-mail w skrzynce odbiorczej.


2
Eee, dla jasności, nie sugeruję, że wytrawność jest zła, to pytanie jest bardziej od diabłów. Naprawdę szukam logicznej odpowiedzi, którą mogę połączyć z ludźmi, którzy uważają, że cięcie + wklejanie + poprawianie kodu jest w porządku.
Incognito

2
Biorąc to pod uwagę, najbardziej podoba mi się twoja odpowiedź, ponieważ obejmuje ona zarówno niepowodzenia DRY: nie zawracanie sobie głowy i przesadzanie, a także wyjaśnianie skutków. - W jednym punkcie dotyczącym poświęcenia czytelności błędów, twierdzę, że powtarzalny kod jest mniej czytelny z tego samego powodu, na który wskazałeś, gdzie łatwo jest zapomnieć o rzeczach.
Incognito

16
Chciałbym podkreślić jedną łatwą pułapkę DRY: podobny kod został scalony. Jeśli masz dwa przypadki użycia, które są funkcjonalnie niezwiązane, ale zdarza się, że mają bardzo podobny kod, łatwo je połączyć, ponieważ DRY jest dobry . Niestety, gdy trzeba ewoluować, często stajesz przed nieprzyjemnym zadaniem podziału funkcji, a następnie przechodzisz przez wszystkie strony wywołań i dokładnie zastanawiasz się, która z nich powinna zostać tutaj wywołana ... Przykład: Typowanie strukturalne LLVM (wszystkie podobne typy są połączone w jeden) sprawia, że ​​mapowanie IR z powrotem do oryginalnego kodu jest prawie niemożliwe.
Matthieu M.

1
Bingo Jeśli dwa lub więcej fragmentów kodu zostanie zmienionych, a zmiany będą zawsze takie same dla wszystkich kawałków, należy je scalić. Żaden utwór, który będzie wymagał zmiany w inny sposób niż inne, nie powinien być łączony. Jeśli kod nigdy się nie zmieni, nie ma znaczenia, czy zostanie scalony, czy nie. Pytanie, czy zmiany powinny być blokowane czy odłączane, jest znacznie ważniejsze niż rozmiar danego kodu.
supercat

1
@ MatthieuM to trochę niesprawiedliwy przykład. LLVM stosuje typowanie strukturalne jako optymalizację ; to znaczy, ludzie LLVM postanowili zapłacić cenę trudnej do zrozumienia podczerwieni za korzyści związane z wydajnością. DRY jest zwykle problemem związanym z utrzymaniem, ale w tym przypadku była to wyraźnie celowa decyzja o zmniejszeniu możliwości utrzymania.
Benjamin Hodgson

47

Ponieważ DRY będzie później mniej pracy.


DRY: (nie powtarzaj się)

Jedna funkcja bierze argument.

def log(arg):
    print(arg)

C&P: (Kopiuj i wklej)

26 funkcji gazillionowych robi to samo, ale z różnicą 2 znaków.

def logA():
    print('a')

def logB():
    print('b')

...ad infinitum...

A może zaktualizujemy nasze drukowanie, aby określić, co dokładnie drukuje?

SUCHY:

def log(arg):
    print(arg + "Printed from process foo")

Gotowy.

C&P:

Musisz wrócić i zmienić każdą funkcję .


Jak myślisz, który łatwiej byłoby debugować?


10
Ponadto musisz napisać tyle podobnych pakietów testów, ile masz zduplikowanych funkcji.
9000

Odpowiednio zilustrowałeś tę koncepcję, ale w praktyce nikt nie zrobiłby tego, co opisałeś za pomocą funkcji gazillionu, i tak nie w ten sposób.
Robert Harvey

@Robert Mam nadzieję, że nie! Wybrałem bardzo proste zadanie, aby lepiej zilustrować koncepcję i dlaczego może to być dobra rzecz.
John

11
@Robert - czy przeczytałeś któryś z artykułów na thedailywtf.com ;) - są tacy, którzy by to zrobili
HorusKol

1
@ 9000 Nie, jeśli nie masz żadnych zestawów testowych: p (co często może się zdarzyć w niektórych projektach ... niestety ...)
Svish

16

Ponieważ zastosowane w twoim przykładzie:

  • + czytelność

    Mniej kodu często przekłada się na mniej hałasu . (nie zawsze...)

  • + elastyczność

    Jeśli kiedykolwiek będziesz musiał zmienić zachowanie doStuffX, będziesz chciał zabić siebie lub kogokolwiek, kto to napisał,

  • + rozszerzalność

    Jeśli wyodrębniłeś poszczególne części do wybranej struktury danych, a następnie po prostu wykonałeś iterację, wywołując ogólny doStuff, możesz równie dobrze dodać jeden wiersz w strukturze danych, w którym chcesz nowy wpis, lub usunąć jeden, i zmiana zachowania oznacza po prostu edycję doStuff. Łatwiejsze w utrzymaniu .

  • + efektywność kosztowa

    mniej kodu oznacza tutaj:

    • => mniej rozwoju => obniżony koszt
    • => mniejsze prawdopodobieństwo błędów => mniej czasu wsparcia => obniżony koszt
  • + (możliwa) optymalizacja zarządzana

    W zależności od języka kompilator / tłumacz może mieć większą szansę na ustalenie, że generyczny doStuffzawsze wykonuje prawie identyczne rzeczy, które często wywołują jeden po drugim, i może go wstawić lub spróbować zoptymalizować . Prawdopodobnie nie dla wariacji X w doStuffX.

  • + testy i jakość

    Testowanie jest łatwiejsze: doStuffwymaga testowania i to wszystko. Cóż, niezupełnie, ale to już obejmuje więcej . Różnią się tylko oczekiwania dotyczące IO i muszą być testowane w różnych warunkach, ale nadal jest o wiele łatwiejsze do przetestowania i łatwiejsze w utrzymaniu niż wszystkie odmiany doStuffX.

Ogólnie rzecz biorąc, zapewnia to łatwiejszy do utrzymania kod i lepszą wydajność programistyczną dla zespołu, i jest to jedna z wielu dobrych praktyk, które pomagają w tworzeniu bardziej niezawodnego i niezawodnego oprogramowania.


13

Ponieważ wszyscy inni wykonali świetną robotę w wyjaśnianiu problemów związanych z utrzymywaniem ze zduplikowanym kodem, powiem tylko:

Wiele programów wymaga myślenia o przyszłości, a nie tylko o bezpośredniej teraźniejszości. Masz rację, że kopiowanie i wklejanie jest teraz łatwiejsze, ale stwierdzenie, że raczej nie będę musiał ponownie tego edytować w najbliższym czasie „ pokazuje, że nie myślisz poprawnie. Tak, możesz kupić sobie trochę czasu za szybkie i brudne kopiowanie / wklejanie, ale robiąc to, pokazujesz, że nie możesz patrzeć poza swój bezpośredni problem i myśleć o jutrze. Czy jesteś pewien, że nigdy nie będziesz musiał ponownie odwiedzać tego kodu? Czy wiesz na pewno, że są żadnych błędów? Czy możesz w 100% zagwarantować, że nie będziesz musiał ponownie go odwiedzać, gdy zaimplementowany zostanie kolejny zestaw funkcji? Są to problemy na jutro, które należy wziąć pod uwagę przy projektowaniu dzisiaj.

Oczywiście zdarzają się sytuacje, w których konieczne będzie kopiowanie / wklejanie. Jako programista interfejsu użytkownika zauważyłem, że muszę naruszać zasadę DRY. Jest do bani, kulę się za każdym razem, kiedy to się dzieje, i na szczęście jest to rzadkie. Ale tak się dzieje.

Różnica polega na tym, że naruszając DRY, powinieneś mieć bardzo ważny powód, aby to zrobić, a stwierdzenie, że trudniej jest dowiedzieć się, jak zrobić z nich wszystko w jeden prosty wzór, wcale nie jest jednym z nich. Chyba że masz do czynienia z ogromnym kryzysem czasowym i twój szef krzyczy, aby dostać coś w ciągu następnych kilku godzin lub stracisz pracę, nie sądzę, aby było to uzasadnione uzasadnienie.

Nie bierz tego w niewłaściwy sposób: nie próbuję cię karać ani karać, ale raczej próbuję sprawić, byś zobaczył, gdzie twoja mentalność jest zła. Programiści inwestują w przyszłe lenistwo; DRY jest sposobem na osiągnięcie tego. Praca, którą dziś wykonujesz w celu rozwiązania trudnego problemu projektowego, jutro się opłaci.


Nie jestem pewien, czy zgadzam się z szefem mówiącym, że „załatw sprawę, bo zostaniesz zwolniony” to świetny powód, by naruszyć SUCHO. Dług techniczny, kiedy masz czas, aby zrobić to dobrze, czy naprawdę oszczędza czas itp.?
Incognito

1
@Incognito, starałem się być trochę ironiczny :)
bedwyr

7

Prawdopodobnie nie będę musiał ponownie tego edytować w najbliższym czasie.

Jeśli tak jest naprawdę, być może uda ci się to uniknąć, ale częściej będziesz pracować nad kodem, który należy utrzymać. Oznacza to rozszerzenie funkcjonalności, naprawianie błędów i inne ulepszenia. Jeśli masz małe odmiany tego samego kodu w 10 różnych miejscach, a pewnego dnia wrócisz do tego kodu i musisz wprowadzić zmiany, masz teraz zadanie podatne na błędy polegające na wprowadzeniu tej samej zmiany w 10 różnych miejscach (przepraszam, istnieje było 11 miejsc, zapomniałeś jednego, a teraz masz błąd).

Jeśli potrafisz uogólnić problem, który próbujesz rozwiązać, możesz ułatwić rozszerzenie i naprawienie kodu, jeśli pojawią się błędy.


Fajna odpowiedź, ale nawet jeśli mi się uda, to co z biednym sokiem, który może go po mnie utrzymać? ;).
Incognito

Zobacz: „częściej będziesz pracować nad kodem, który należy utrzymywać” :)
Alex

Nawet wtedy dzieje się tak tylko wtedy, gdy kopiowanie i wklejanie jest w 100% wolne od błędów. I tak nie jest i przestańcie myśleć, że tak może być.
Dan Ray

Przyznam, że w przeszłości kopiowałem i wklejałem rzeczy, ale nigdy nie miałem zamiaru zatrzymać się na dłużej niż jeden dzień. Czasami potrzebujesz tylko szybkiego i brudnego skryptu wyrzucania.
Alex

6

Jak powiedziałem w odpowiedzi na inne pytanie, moje podejście jest następujące:

  1. Gdy pierwszy raz rozwiązuję pewien problem, po prostu go załatwiam.
  2. Za drugim razem (kiedy rozwiązuję podobny problem) myślę: hm, może się powtarzam, ale na razie wybiorę szybkie kopiowanie i wklejanie.
  3. Za trzecim razem myślę: Hm, powtarzam się -> uczyń to ogólnym!

Tj. Do 2, inna zasada (YAGNI) wygrywa z SUCHEM. Ale zaczynając od 3 (lub 4, jeśli jestem naprawdę leniwy!) Wydaje się, że będę go potrzebować, więc podążam za OSUSZANIEM.

Aktualizacja

Kilka dalszych pomysłów z mojego ostatniego doświadczenia. Musiałem dostosować / zintegrować dwa komponenty A i B opracowane przez inny zespół z naszym produktem. Po pierwsze: dwa komponenty A ib B są do siebie bardzo podobne, więc już mnie niepokoiło to, że miały nieco inną architekturę. Po drugie: musiałem je dostosować, więc chętnie skorzystam z podklas i zastąpię tylko to, czego naprawdę potrzebowałem.

Zacząłem więc refaktoryzować te dwa komponenty (z których każdy składa się z około 8 klas C ++): Chciałem mieć wspólną architekturę dla A i B, a następnie dodać potrzebne funkcje, definiując podklasy. W ten sposób nasze dwa nowe komponenty A 'i B' zostałyby wyprowadzone z istniejących.

Po dwóch tygodniach próby wyciągnięcia wspólnej i dobrze zdefiniowanej struktury z istniejącego kodu i konieczności wyjaśniania podczas naszych codziennych spotkań, że robię niewielkie postępy, ponieważ oryginalny kod był zbyt niechlujny, rozmawiałem z moim szefem. Zauważyliśmy, że nie będziemy potrzebować niczego więcej niż te dwa nowe komponenty A 'i B' (nie będzie ich czterech czy sześciu, tylko te dwa).

Ok, niech tak będzie: Zrobiłem masową kopię i zmieniłem nazwy klas z A i B i zacząłem dostosowywać kopię kodu. Dostałem go do pracy za dwa tygodnie (wciąż jeszcze trochę naprawiam błędy).

Zalety: Mamy już prawie ukończoną funkcjonalność, a kiedy naprawimy wszystkie błędy, jesteśmy skończeni. Zapisaliśmy wszystkie refaktoryzacje i testy A i B.

Wady: Dwa tygodnie temu drugi zespół zmienił inny komponent C, z którego korzystają A i B. Dostosowali A i B, ale A 'i B' również zostały zepsute i sami musieliśmy je zmienić. Wprowadziło to nowy błąd, który musieliśmy naprawić. Ta dodatkowa praca prawdopodobnie byłaby niepotrzebna, gdyby A 'i B' współdzieliły większość swojego kodu z A i B.

Tak więc: powielanie kodu jest zawsze niebezpieczne. Myślę, że zawsze chodzi o znalezienie kompromisów i często nie jest to łatwe.


5

Tylko dla wyjaśnienia, ponieważ nie znajduję tego w żadnej z pozostałych odpowiedzi:

DRY to zasada opracowywania oprogramowania mająca na celu ograniczenie powtarzania wszelkiego rodzaju informacji .

Każda wiedza musi mieć jedną, jednoznaczną, autorytatywną reprezentację w systemie.

Zasada DRY, o której wspominają Andy Hunt i Dave Thomas, nie ogranicza się do zapobiegania powielaniu kodu. Opowiada się także za generowaniem kodu i wszelkimi procesami automatyzacji. Jak na ironię, wyniki generowania kodu mogą być nawet duplikatem kodu ...

Powód, dla którego został już dokładnie wyjaśniony w innych odpowiedziach, ale komentarz Falcona podsumowuje to wystarczająco dobrze IMHO:

Łatwiej jest utrzymać pojedynczy punkt zmiany.


Och wow, myślałem, że w tagu są jakieś dane. Umieszczę tam trochę informacji.
Incognito

3

Jest coś takiego jak zbyt dużo SUCHEGO. Kiedy tak się dzieje, dwie koncepcje, które w pewnym momencie wydają się wystarczająco podobne, aby uzasadnić kod faktoringowy (1), mogą później okazać się na tyle różne, że zasługują na osobne implementacje.

Innymi słowy, OSUSZANIE i luźne sprzęganie czasami powodują konflikty. Jeśli oczekujesz, że doStuff1 i znajomi będą się różnić z każdą nową wersją oprogramowania, możesz powielić ich kod.

Z mojego doświadczenia wynika, że ​​trudno jest ocenić, dokąd zmierza twoje oprogramowanie, dlatego DRY jest często bezpiecznym wyborem.

Kod, który został nadmiernie „wysuszony” zazwyczaj ma złożony przepływ sterowania i zbyt wiele parametrów. To, co początkowo było prostą funkcją, zostało później rozszerzone o obsługę nowej funkcjonalności kontrolowanej przez dodatkowy parametr. Po dwóch lub trzech iteracjach funkcja nie jest już możliwa do utrzymania. Napraw błąd występujący w ustawieniu, a nowe błędy wprowadzasz w innych ustawieniach.

Zrozumiałe jest, że jakość kodu często spada wraz z ewolucją kodu, ale widziałem przypadki, w których funkcja wieloparametrowa z spaghetti „jeśli-to-inaczej” w ciele była wynikiem dobrze pomyślanego, ale źle przeprowadzonego procesu refaktoryzacji.

(1) Używam słowa „kod”, ale dotyczy to również projektowania.


Przydałoby się podać przykład „zbyt suchego”, ponieważ jest to mniej widoczny koniec spektrum.
Incognito,

@Incognito: Zredagowałem swoją odpowiedź. Nie ma konkretnego przykładu, ale mam nadzieję, że to, co miałem na myśli, jest wystarczająco jasne.
Joh

2

Muszę wspomnieć o problemach z DRY w świecie relacyjnych baz danych. Bazy danych zaprojektowano tak, aby działały szybko i dobrze przy użyciu logiki opartej na zestawach i za pomocą zapytań sargable. Zasady DRY często powodują, że programiści piszą zapytania nie podlegające wymianie lub używają logiki Row-by-agonizing-Row do wykorzystania istniejącego kodu w wielu sytuacjach. DRY i optymalizacja wydajności są często sprzeczne, a w świecie baz danych wydajność jest zwykle o wiele ważniejsza niż utrzymanie. Nie oznacza to, że nie powinieneś w ogóle stosować zasad DRY, tylko powinieneś być świadomy tego, jak wpłynie to na ogólną użyteczność bazy danych. Deweloperzy aplikacji najpierw DRY, a po drugie wydajność, twórcy baz danych uważają, że najpierw integralność danych, po drugie wydajność, bezpieczeństwo danych (wydajność i bezpieczeństwo mogą zamieniać się miejscami w niektórych systemach).

Zauważyłem ogólnie, że im więcej warstw abstrakcji umieszczanych w bazie danych, tym wolniej się one stają. Nie twierdzę, że nie chciałem, aby osoby, które same projektowały programy baz danych, nie wykonały lepszej pracy, pozwalając programistom na korzystanie z DRY bez wpływu na wydajność bazy danych, ale nie projektuję oprogramowania bazodanowego na tym poziomie , więc być może konflikt między abstrakcją a wydajnością w bazie danych jest trudniejszy do rozwiązania niż przypuszczam. Musimy jednak współpracować z systemami, które są obecnie budowane. Możemy poprosić o lepszą implementację zasad DRY w przyszłych wydaniach, które nie spowodują także spadku wydajności (i z biegiem lat poprawiły się, ale nadal są problematyczne), ale w międzyczasie musimy rozważyć, czy DRY jest właściwym posunięciem dla tej bazy danych w tym czasie.

Ale często te same funkcje, które chcesz zastosować, aby zapewnić spełnienie zasady DRY, powodują ogromne problemy w bazie danych. Nie twierdzę, że nigdy nie używaj SUCHEGO, ale nie przesadzaj z tym.

Przykłady tego, o czym mówię. Raz w miesiącu musisz wykonać import danych w wysokości miliona rekordów. Rekordy można już ręcznie dodawać poprzez interfejs użytkownika wywołujący zapisany proc. Ten proces, ponieważ został zaprojektowany do importu pojedynczych rekordów, dodaje tylko jeden rekord na raz. Używając DRY, aby uniknąć umieszczania kodu wstawiania w dwóch miejscach, piszesz kursor, aby wielokrotnie wywoływać proc, zamiast pisać importów opartych na zestawie, których potrzebujesz. Czas na import trwa od 30 minut, które zajęłoby przy użyciu logiki opartej na zestawie, do 18 godzin. Teraz właściwym sposobem na przestrzeganie DRY w tym przypadku byłoby naprawienie proc w celu obsługi importu wielu rekordów. Niestety często jest to niemożliwe lub bardzo trudne, aby wysłać tablicę do proc (w zależności od zaplecza db) i zmieniając proc, kończy się uszkodzenie aplikacji.

Funkcje skalarne i funkcje o wartościach przechowywanych w tabeli są również używane do implementacji zasad DRY i ponownie mogą poważnie wpłynąć na wydajność, szczególnie jeśli trzeba ich używać w sposób, który uniemożliwia wykorzystanie indeksów.

Widoki są również dobre do implementacji DRY. Jeśli jednak zaimplementujesz DRY za pomocą widoków wywołujących widoki wywołujące inne widoki, szybko dojdziesz do punktu, w którym zapytania przekroczą limit czasu pod obciążeniem. W rzeczywistości możesz potrzebować wygenerować zestawy danych milionów rekordów, gdy potrzebujesz tylko trzech na końcu. Tak więc jednopoziomowy widok złożonego zestawu połączeń do implementacji DRY może być doskonały (mam taki sam, którego używamy, aby upewnić się, że wszystkie sprawozdania finansowe używają tego samego podstawowego zestawu tabel i obliczeń niektórych rzeczy), więcej niż dwa poziomy i musisz rozważyć, czy tworzysz bałagan wydajności.


1

Nie widzę kluczowych punktów mojej odpowiedzi powyżej, więc proszę. Nie patrz na SUCHO jako zasadę przeciwkorobić coś. Może być tak sformułowany, ale może naprawdę służyć całkiem innemu i pozytywnemu celowi. Jest to sygnał do zatrzymania, przemyślenia i znalezienia lepszej odpowiedzi. Rzuca mi wyzwanie szukanie możliwości zaprojektowania lepszego rozwiązania. To dobra strona nieprzyjemnego zapachu w moim kodzie, która skłania mnie do przemyślenia mojego projektu i sprawia, że ​​robię to o wiele lepiej. DRY to nie tylko drobiazgowe naruszenie składni. Rzuca mi wyzwanie modularyzacji. Rzuca mi wyzwanie na komponowanie. Sygnalizuje powtarzanie, które przypomina mi o myśleniu o użyciu szablonów i generowaniu kodu zamiast brutalnej siły i ignorancji. Pomaga mi zorientować się, że powinienem znaleźć czas na automatyzację mojej automatyzacji. Prowadzi cię do oszczędnego stylu życia! Pomaga ci spędzać więcej czasu, robiąc fajniejsze nowe rzeczy, niż nudne stare nudne szczegóły. I zapewnia dobre maniery, dobry oddech i zdrowy styl życia! Cóż, może trochę zbaczam z tropu ...


DRY ma dla mnie bardzo różne skutki, jednak jeśli są to jego skutki dla ciebie, raczej podoba mi się filozofia czegoś, co jest „sygnałem do zatrzymania, myślenia i znalezienia lepszej odpowiedzi” oraz wyzwaniem.
n611x007,

1

Mam stary projekt, w którym niektórzy z poprzednich programistów wcale nie dbali o DRY. Zatem cała baza kodu była zaśmiecona metodami pomocniczymi, takimi jak GetSystemTimeAsString (), LogToFile () i wieloma innymi rzeczami. Niektóre metody zostały nieco dostosowane do specjalnych potrzeb, ale większość z nich to po prostu kopiowanie i wklejanie.

Niestety niektóre metody miały subtelne błędy, takie jak tablica char, w niektórych przypadkach niewystarczająco długie, wykorzystujące niepewne rzeczy, takie jak strcpy () itp.

Tak więc prawdziwą PITA było znaleźć wszystkie fragmenty kodu, zharmonizować je i naprawić błędy. Nadal harmonizujemy i naprawiamy rzeczy.

Nigdy nie wiadomo, czy popełniłeś błąd w pierwszej metodzie, a następnie musiałeś ją naprawić wiele razy, ponieważ właśnie ją skopiowałeś. A jeśli chcesz później skorzystać z niektórych metod, skąd wiesz, która z 5 metod w bazie kodów jest teraz odpowiednia dla twojego przypadku? Więc wystarczy skopiować jeden, dostosować go i tutaj zaczyna się od nowa ...



1

Frazeologia „nie powtarzaj się” jest nieco zbyt uproszczona. Co ważne, „unikaj umieszczania jednego fragmentu potencjalnie zmieniających się informacji w dwóch niezależnych miejscach”.

Jeśli program ma przetwarzać widżety, każdy z trzema dodatkami, i ma wiele pętli tego formularza

for (i=0; i<3; i++)
  thisWidget.processWoozle(i);

wówczas oczekiwanie, że widżety zawierają trzy woozle, zostanie zawarte w każdej z tych pętli, a aktualizacja kodu w celu dostosowania do dowolnej innej liczby woozles na widżet może być trudna. Natomiast gdyby tak powiedzieć

#define WOOZLES_PER_WIDGET 3

i każda pętla została przepisana

for (i=0; i<WOOZLES_PER_WIDGET; i++) ...

taki projekt może bardzo łatwo zmienić liczbę woozles na widżet.

Należy jednak zauważyć, że chociaż pożądane jest konsolidowanie informacji, takich jak liczba woozli na widżet w jednym punkcie, nie zawsze jest to praktyczne. Czasami konieczne może być zaprogramowanie logiki na stałe, która zadziała tylko wtedy, gdy rzeczy będą miały określony rozmiar. Na przykład, jeśli każdy woozle ma wartość i chce się znaleźć medianę związaną z konkretnym widgetem, może być możliwe posortowanie wartości i wybranie środkowej, a takie podejście działałoby z dowolną liczbą woozle, ale logiką który jest napisany ręcznie, aby znaleźć medianę trzech elementów, może być znacznie szybszy.

Mimo że stała WOOZLES_PER_WIDGET może uczynić kod bardziej czytelnym, należy go skomentować, aby wyjaśnić, że jego wartości nie można zmienić bez dokonywania innych korekt w logice programu. W takim przypadku logika, która jest zakodowana na stałe dla trzech elementów i stała WOOZLES_PER_WIDGET, powielałaby informację „każdy widżet ma trzy woozle”, ale korzyści z takiego powielenia (większa szybkość wykonania) mogłyby przeważyć koszt.


0

Chociaż faktycznie zgadzam się z innymi plakatami, komentarze dotyczące łatwości konserwacji itp., Wszystkie są poprawne.

Chciałbym dodać do debaty niewielki głos sprzeciwu.

  • Jest to ważne tylko dla programistów. Ludzie, którzy płacą twoje wynagrodzenie, nie mogą się tym przejmować, o ile oprogramowanie przechodzi UAT.
  • Pod względem znaczenia plasuje się znacznie poniżej pozycji, takich jak uzyskanie właściwych wymagań, wysłuchanie sponsorów projektu i terminowe dostarczenie.

ponieważ ta strona jest przeznaczona dla „programistów”, myślę, że można bezpiecznie powiedzieć, że pytanie jest skierowane do „punktu widzenia programistów”. Twoje stwierdzenia na temat płatników płac, UAT i rangi ważności są oczywiście ważne, ale nie odnoszą się do tego konkretnego pytania.
ozz

1
Całkowicie się nie zgadzam. Dobre zarządzanie zrozumie zasady i powody ich realizacji, jeśli zostaną szczegółowo wyjaśnione. To powinna być poważna, dogłębna rozmowa, a nie 5 minut po minucie.
Michael Durrant

2
Twój drugi punkt jest całkowicie poprawny.
Jim G.

1
@Ozz, duma z twojego rzemiosła jest ważna, nawet konieczna dla dobrego programisty, ale być może „punkt widzenia programistów” powinien zawierać odrobinę troski o „zadowolenie klienta”.
James Anderson

0

<tl;dr>

Nie mogłem odczytać wszystkich powtórzonych odpowiedzi, więc mogłem coś przeoczyć (i powtórzę to sam <= zobaczyć, co tutaj zrobiłem?).

Oto lista rzeczy, które są niesamowite w zapobieganiu duplikacji kodu!

  1. Łatwiejszy test: wystarczy przetestować tylko jedną „kopię” kodu.
  2. Łatwiej to naprawić: wystarczy znaleźć błąd w jednej „kopii” kodu i naprawić go raz.
  3. Łatwiejsza aktualizacja (to samo jak powyżej): potrzebne zmiany, często można sobie poradzić modyfikując kod w bardzo niewielu miejscach, ponieważ poświęciłeś czas na prawidłowe ponowne użycie kodu i nie skopiowałeś tych samych wierszy do setek lub tysięcy różnych miejsc w źródle.
  4. Łatwiejsze do ponownego użycia: gdy (nie jest powielone w kilku miejscach) i jest trzymane w ogólnych, odpowiednio nazwanych metodach, łatwo je znaleźć i użyć zamiast pisać własne.
  5. Łatwiejszy do odczytania: zduplikowany kod jest trudny do odczytania, ponieważ jest niepotrzebnie gadatliwy; zawiera wiele wierszy, które nie są częścią logiki i konkretnej zamierzonej funkcjonalności (na przykład ogólne polecenia używane do skonfigurowania sceny dla akcji lub ogólne proste powtarzane zadania, które są potrzebne w wielu miejscach). Czysty kod sprawia, że ​​logika i funkcjonalność są wyskakujące, ponieważ nie ma powtórzeń zaśmiecających przestrzeń kodu.
  6. Łatwiej jest debugować z powodu (1) i (5).
  7. Oszczędza czas i pieniądze i robi więcej fajnych rzeczy w przyszłości; w szczególności stwórz lepszy, bardziej niezawodny kod. To jest sedno i jest to podsumowanie prawie wszystkich powyższych. Jeśli wiele osób korzysta z tej samej funkcji doFoo1(a, b), istnieje większa szansa, że ​​wiele jej irytujących błędów i przypadków na krawędzi zostanie odkrytych i rozwiązanych. Jeśli wszyscy skopiują kod i utworzą doFoo2(specialA)... doFuu2^n(a, b, c), zduplikowali problemy doFoo1i konkretnie stworzyli o wiele więcej pracy.

</tl;dr>

Długa wersja:

Problem z duplikacją kodu polega na tym, że „rośnie wykładniczo” (innymi słowy, szybko się rozwija), ponieważ gdy duplikujesz kod, nieświadomie udzielasz innym zgody (na przykład, nie jesteś już w stanie ich ocenić) i zachęcasz ich do robienia tego samego. Sprawiasz, że trudniej jest tego nie robić, ponieważ trudniej jest wykryć i ponownie użyć przydatnego kodu, gdy w źródle występuje wiele mylących, zbędnych powtórzeń. Zwłaszcza jeśli kod nie został jeszcze wyodrębniony do funkcji o odpowiedniej nazwie. Więc jeśli napotkasz wspólny prosty problem do rozwiązania, prawdopodobnie sam napiszesz fragment kodu, który go rozwiąże… I prawdopodobnie nie uda ci się sprawdzić kilku przypadków brzegowych, dodając więcej błędnych, nieprzetestowanych kodów.

Inną rzeczą jest to, że dla początkującego może to brzmieć jak problem, który wpłynie tylko na duże firmy, ale zauważyłem, że bardzo źle dotyka on małych startupów (jak w przypadku 10 000 wierszy zdublowanego kodu po stronie serwera). To jest stan umysłu. Powinniście nie tylko opanować SUSZENIE, ale starać się zachęcić innych do zrobienia tego samego; bo w przeciwnym razie skazacie się głównie na powielanie kodu. Gdy dostępne są środki DRY i są one egzekwowane, o wiele łatwiej jest je zastosować. W przypadku dużej liczby zduplikowanych kodów znacznie łatwiej jest zastosować rozwiązania kopiowania i wklejania.

Co uważam za szkodliwe w powielaniu kodu:

  1. Czy ta funkcja jest użyteczna? Załóżmy, że znajdujesz funkcję, która działa (lub wydaje się, że robi to, czego potrzebujesz), skąd wiesz, czy powinna ona nawet działać poprawnie, czy jest to po prostu kod, który został zduplikowany i porzucony.
  2. Nadmiarowy kod. Czasami ludzie duplikują kod, używają go i zapominają (zawsze mogą go powtórzyć w przyszłości). W pewnym momencie ktoś usuwa wywołania zduplikowanej funkcji w niektórych miejscach, aby zrefaktoryzować, ale nieużywana funkcja pozostaje, nawet jeśli nie jest aktywnie używana.
  3. Trudno znaleźć to, czego szukasz. Powielony kod zajmuje miejsce i sprawia, że ​​znalezienie użytecznych i potrzebnych rzeczy (przy użyciu narzędzi takich jak grep) jest trudniejszym zadaniem, niż musi być, ponieważ otrzymujesz dziesiątki lub tysiące wyników, w których powinieneś dostać tylko garstkę.
  4. (Zostało wcześniej wspomniane): Trudne w utrzymaniu, ale także trudne w użyciu do celów utrzymania i regresji. Jeśli kod testowy zostanie zduplikowany i niepoprawnie wyodrębniony do funkcji, inni go skopiują. Czy ktoś będzie kłopotał się pisaniem łatwego w użyciu, łatwego do odczytania API, aby poprawić QoL? Z mojego doświadczenia wynika, że ​​nie, ludzie często uważają coś za bardziej palącą materię, dopóki nie wymknie się ona spod kontroli.
  5. Duplikacja kodu jest trudniejsza do odczytania, ponieważ sprawia, że ​​kod jest pełniejszy tam, gdzie nie musi być, w miejscach, w których gadatliwość nie dodaje informacji o zamierzonej funkcjonalności: na przykład ogólne wywołania metod, które są używane w kółko ustanowić grunt pod wiele rodzajów zamierzonej funkcjonalności, utrudnia to wyskakiwanie tej faktycznej funkcjonalności.
  6. Zostało to wiele wspomniane. Jeśli kod jest nieprawidłowy, jakiś biedny facet lub facet będzie musiał przeszukać i zmienić każde użycie tego kodu. Na przykład, jeśli ktoś użył niebezpiecznego wywołania iniekcji SQL do mysql_query w bardzo niewielu miejscach w zorganizowanej klasie, gdzie jest to potrzebne, łatwo byłoby to naprawić i zamiast tego użyć PHP PDO, ale gdyby użył go w ponad tysiącu miejsc kopiując wywołanie i na koniec, naprawienie go będzie praktycznie wymagało outsourcingu, a czasem bardziej niebezpiecznie, kod będzie musiał zostać przepisany od nowa.
  7. Powielanie kodu to zły nawyk. Jeśli ćwiczysz coś, powoli staje się ono drugą naturą i wpływa na ludzi wokół ciebie. Młodzi deweloperzy widzą, że to robisz i robisz to także. Powinieneś praktykować to, co głosisz i nawyk robić właściwe rzeczy. Dowiesz się więcej. Pisanie niepublicznego kodu jest trudniejsze i trudniejsze. Jest to satysfakcjonujący nawyk.

Ostatnie uwagi na temat nadgorliwego zapobiegania duplikacjom kodu i podsumowywania:

To zostało już powiedziane wcześniej, ale czasami unikanie powielania powoduje, że „pochylasz się do tyłu” i robisz rzeczy, które są zbyt wyrafinowane (lub nieważne), aby inni mogli je zrozumieć. Pisanie nieczytelnego kodu (lub żartobliwie nazywamy go kodem zabezpieczającym zadania) samo w sobie jest problemem, nawet jeśli nie jest zaangażowane zapobieganie powielaniu kodu. Uważam jednak, że jeśli od samego początku zaszczepiona zostanie odpowiednia infrastruktura i najlepsze praktyki, o wiele łatwiej jest zapobiegać powielaniu kodu, a ludzie często mogą unikać robienia nieintuicyjnych rzeczy, aby zapobiec przyszłym niepotrzebnym i nieczytelnym stosom pracy związanym z zapobieganiem powielaniu kodu, jeśli robisz rzeczy od samego początku.

Co robi dobrze? Trudno odpowiedzieć na to pytanie, ale jedną z nich jest określenie, jakie metody są potrzebne w projekcie i zobaczenie, co zostało już wdrożone przez innych (na zewnątrz i) wewnątrz firmy i ponowne wykorzystanie, jeśli to możliwe; dokumentowanie wszystkiego, co dodajesz do bazy kodu i próbowanie uczynienia go o jeden stopień bardziej ogólnym, niż to musi być, ale to wszystko. Nie przesadzaj z wzorami projektowymi, aby kod był elastyczny tam, gdzie nie musi.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.