Czy mój kod powinien być SUCHY lub czytelny, jeśli nie może to być jedno i drugie?

14

Piszę kod Ruby dla prostego ćwiczenia szyfrowania i często napotykałem ten dylemat (ćwiczenie to szyfr pasjansa, jeśli musisz wiedzieć). Pytanie dotyczy tego, czy powinienem uzupełnić moją logikę o zmienne opisowe i instrukcje jednoetapowe, które czynią tę funkcję czytelną zamiast zwięzłych, a nawet gęstych instrukcji, które eliminują powtarzanie i / lub minimalizują błędy.

Mój najnowszy przykład: Mój program pobiera dane wejściowe, a ze względu na sztywne wytyczne formatowania może łatwo określić, czy dane wejściowe powinny być szyfrowane, czy odszyfrowywane. Aby uprościć, po przekonwertowaniu / wygenerowaniu klucza szyfrującego i wiadomości w celu ich zgodności, należy odjąć klucz od zaszyfrowanej wiadomości lub dodać klucz do niezaszyfrowanej wiadomości, aby uzyskać pożądany wynik (myśl o kluczu jako o szyfrowaniu, wiadomość + szyfrowanie = kod; kod - szyfrowanie = wiadomość). Pozycja DRY mówi mi, że powinienem przekonwertować zaszyfrowaną wiadomość inaczej niż wiadomość niezaszyfrowaną, aby funkcja, która pobiera klucz szyfrowania i stosuje go do wiadomości, nigdy nie musiała się rozróżniać. Odkryłem, że oznacza to, że potrzebuję zagnieżdżonych instrukcji if w funkcji, ale logika wydaje się być solidna. Ten kod nie jest jednak łatwy do odczytania.

Z drugiej strony mógłbym napisać dwie różne funkcje, które są wywoływane na podstawie flagi ustawianej, gdy aplikacja określa szyfrowanie lub deszyfrowanie. Byłoby to łatwiejsze do odczytania, ale powielałoby funkcję wysokiego poziomu stosowania klucza szyfrowania do wiadomości (powodując, że jest ona szyfrowana lub odszyfrowywana).

Czy powinienem skłaniać się w kierunku czytelnego kodu lub zwięzłego kodu? A może przegapiłem inny sposób na uzyskanie tej funkcjonalności i spełnienie obu zasad? Czy jest to pozycja w skali, w której należy wziąć pod uwagę cel projektu i podjąć najlepsze decyzje, aby mu służyć?

Do tej pory kładę nacisk na zwięzły, suchy kod zamiast czytelnego kodu.

lutze
źródło
2
Można używać najpierw, DRY drugi, czytelny trzeci. Wysoce użyteczny kod skutkuje wysoce czytelnym kodem konsumpcyjnym, który łatwiej spełnia OSUSZANIE i jest czytelny. Właśnie dlatego chcesz wziąć paskudne zawiłości i umieścić je gdzieś z ładnym API, jeśli nie można ich ulepszyć; przynajmniej kod, który wchodzi w interakcję z nimi, zostanie oszczędzony przed złym podejściem do tego podejścia.
Jimmy Hoffa
4
niektóre rzeczywiste przykłady kodu mogą pomóc, podejrzewam, że brakuje jakiegoś trzeciego sposobu, takiego jak funkcje wyższego rzędu, aby mieć suchy i czytelny kod
jk.
2
Nie duplikat. Zwięzłe v. Czytelne i DRY v. Czytelne to dwa bardzo różne porównania. Bycie SUCHEM jest o wiele bardziej niebezpieczne niż brak zwięzłości.
djechlin
Tylko nie wpadnij w pułapkę, że przyjęcie mocno zduplikowanego kodu jest bardziej czytelne, ponieważ wpada w przewidywalny wzorzec. Podejrzewam, że tak łatwo jest myśleć, dopóki nie utrzymasz systemu, który powiela się tak mocno, że znajdziesz subtelne błędy w jednej gałęzi, których nie ma w drugiej, prawie identycznych gałęziach.
KChaloux
Nie rozumiem, co rozumiesz przez „funkcję wysokiego poziomu stosowania klucza szyfrowania do wiadomości”. Jeśli masz na myśli, że szyfrowanie i deszyfrowanie zachodzą na siebie, rozważ zastosowanie refaktoryzacji metodą wyodrębniania w celu wyodrębnienia części wspólnych.
Aaron Kurtzhals,

Odpowiedzi:

20

DRY jest wytyczną, a nie religią. Ci, którzy nadeszli do SUSZENIA ponad wszystko, posunęli się za daleko.

Przede wszystkim użyteczny kod jest najważniejszy. Jeśli kod nie jest użyteczny i użyteczny, to nie jest ... i nie ma sensu pisać go w pierwszej kolejności.

Po drugie, pewnego dnia ktoś będzie musiał zachować Twój kod. Jeśli kodu nie da się utrzymać, złamią twój „piękny” gęsty, zwięzły, SUCHY projekt, przeklinając twoje imię. Nie rób tego Byłem tą osobą i za każdym razem, gdy widzę pewne imię w adnotacji kodu, wzdrygam się.

Tworzenie gęstego kodu pozbawionego „zmiennych opisowych” i umieszczanie wszystkiego w zagnieżdżonych wyrażeniach trójskładnikowych za pomocą lambd bez dokumentacji jest sprytne. Miło wiedzieć, że możesz to zrobić - ale nie rób tego. Sprytny kod jest bardzo trudny do debugowania. Unikaj pisania sprytnego kodu .

Większość czasu poświęcanego oprogramowaniu spędza się na jego konserwacji - nie na pisaniu jej za pierwszym razem. Napisz kod, abyś (lub ktoś inny) mógł szybko i łatwo naprawić błędy i dodawać do niego funkcje zgodnie z wymaganiami za pomocą zaledwie kilku idealnych zmian w projekcie.

Społeczność
źródło
Intuicyjnie czy nie, natrafiłeś na problem, który przeoczyłem. W tym ćwiczeniu piszę „sprytny” kod, tak jak mogłem, tylko po to, aby wiedzieć, że mogę to zrobić, ale zgadzam się, że miło to wiedzieć, ale zgadzam się z tobą tutaj, że jest to zła praktyka. Teraz mam wiele do zrobienia.
lutze
1
@lutze Jestem programistą perlowym, a czasem rubinowym i na pewno rozumiem pokusę napisania w nim sprytnego kodu. Wstępna wersja tej odpowiedzi zawierała cytat z Larry'ego Walla na temat hubrisa - uważam, że jest to bardzo ważna zaleta podczas pracy z kodem, który pewnego dnia zostanie zachowany. Z powodu możliwej zwięzłości języków czasami trudno jest uzyskać przewagę, pisząc więcej kodu zamiast próbować sprytnego, gęstego kodu ... dopóki nie będziesz musiał go utrzymać.
17

Nie jestem pewien na podstawie pytania, które rozumiesz DRY. Kod DRY to nie to samo, co zwięzłe. Dość często jest odwrotnie.

Tutaj nie jestem pewien, o co chodzi w tym przykładzie. Stwórz funkcję do szyfrowania, funkcję do odszyfrowania, pomocniki dla wspólnej funkcjonalności (mix bajtów) i prosty interfejs do pobierania danych i określania szyfrowania / deszyfrowania ... Nie powtarzaj się.

Ogólnie rzecz biorąc, zarówno DRY, jak i czytelność istnieją, aby pomóc w utrzymaniu i rozszerzaniu kodu. Nie wszystkie scenariusze są sobie równe, duże trafienie w czytelność w celu usunięcia małej powtórki nie jest dobre, podobnie jak garść duplikacji w celu zwiększenia czytelności.

Po naciśnięciu wolałbym czytelność. Duplikat kodu może być nadal testowany - nieczytelny kod prowadzi do zrobienia (i przetestowania) niewłaściwej rzeczy.

Telastyn
źródło
Świetne punkty, naprawdę połączyłem trochę zasadę SUCHEGO z celem zwięzłego kodu.
lutze
12

Nie znam twojego konkretnego przypadku, ale mogę podać kilka ogólnych wskazówek.

Celem zarówno czytelności, jak i DRY jest łatwość konserwacji .

Utrzymanie jest ważne w większości sytuacji, w których spędzasz więcej czasu na utrzymywaniu kodu niż na pisaniu. Jest to szczególnie ważne, jeśli uważasz pisanie kodu za specjalny rodzaj konserwacji.

Niestety DRY jest często źle rozumiany. Wynika to częściowo z tego, że wydaje się takie proste, ale podobnie jak wiele prostych rzeczy, może być… cóż… skomplikowane.

Zamiarem DRY jest to, aby każda jednostka funkcjonalności istniała tylko w jednym miejscu. Jeśli zasada ta jest przestrzegana, opiekun kodu, którego zadaniem jest zmiana lub weryfikacja tej funkcjonalności, może natychmiast poradzić sobie z kodem. Jeśli DRY nie będzie przestrzegane, istnieje bardzo realne niebezpieczeństwo, że niektóre kopie funkcji nie będą właściwie utrzymywane.

Najbardziej rażącym naruszeniem DRY jest kodowanie z kopiowaniem i wklejaniem, w którym całe bloki kodu są powtarzane dosłownie w bazie kodu. Jest to zaskakująco powszechne w moim doświadczeniu i w żaden sposób nie zwiększa czytelności. Dlatego refaktoryzacja kodu w celu wprowadzenia wspólnej metody niezmiennie zwiększa zgodność z DRY i czytelność.

Drugim najbardziej rażącym naruszeniem jest „kopiuj-wklej i zmień to trochę”. Ponownie refaktoryzacja w celu wprowadzenia wspólnej metody z parametrami lub rozbicie funkcji na etapy i wyodrębnienie podobieństw prawie zawsze zwiększa czytelność.

Potem są bardziej subtelne naruszenia, w których funkcjonalność jest powielana, ale kod jest inny. Nie zawsze jest to łatwe do wykrycia, ale kiedy go zobaczysz i refaktoryzujesz, aby pobrać wspólny kod do jednej metody / klasy / funkcji, wówczas kod jest zwykle bardziej czytelny niż był wcześniej.

Wreszcie istnieją przypadki powtórzenia projektu. Na przykład mógłbyś kilkakrotnie użyć wzorca stanu w swojej bazie kodu i rozważasz refaktoryzację w celu wyeliminowania tego powtarzania. W takim przypadku należy zachować ostrożność. Być może zmniejszasz czytelność, wprowadzając więcej poziomów abstrakcji. Jednocześnie tak naprawdę nie masz do czynienia z powielaniem funkcjonalności, ale z powielaniem abstrakcji. Czasem warto ... ale często nie jest. Waszą naczelną zasadą będzie pytanie „który z nich jest łatwiejszy do utrzymania”.

Za każdym razem, gdy wykonuję te oceny, staram się wziąć pod uwagę czas, jaki ludzie spędzą na utrzymywaniu kodu. Jeśli kod jest podstawową funkcjonalnością biznesową, prawdopodobnie będzie wymagał większej konserwacji. W takich przypadkach staram się, aby kod był łatwy w utrzymaniu dla osób, które znają bazę kodu i związane z nim abstrakcje. Z przyjemnością wprowadzę trochę więcej abstrakcji, aby ograniczyć powtarzanie. Natomiast skrypt, który jest rzadko używany i rzadko obsługiwany, może być trudniejszy do zrozumienia dla opiekunów, jeśli wymaga zbyt dużej abstrakcji. W takim razie popełniłbym błąd po stronie powtórzeń.

Rozważam także poziom doświadczenia innych członków mojego zespołu. Unikam „fantazyjnych” abstrakcji z niedoświadczonymi programistami, ale wykorzystuję uznane wzorce projektowe z bardziej dojrzałymi grupami.

Podsumowując, odpowiedzią na twoje pytanie jest zrobienie czegokolwiek, co czyni twój kod najbardziej łatwym w utrzymaniu. To, co oznacza to w twoim scenariuszu, zależy od ciebie.

Kramii
źródło
Myślę, że dokładnie trafiłeś w jeden z moich problemów. Podany przeze mnie przykład funkcjonalności, której starałem się nie powielać, jest bardziej prawdopodobne, że powielenie abstrakcji.
lutze
+1. Obecnie mam do czynienia z systemem, który nadużywał kopiowania i wklejania w absurdalnym stopniu. Wyodrębnienie jednej metody z kodu kopiowania / wklejenia usunęło dziś ponad 400 wierszy z jednej z moich klas. Niestety znalazłem to w metodzie 900-liniowej ...
KChaloux
1
Jeśli dwa konstrukty kod obecnie zrobić to samo, ale zmiana jednego powinno generalnie nie pociąga za sobą zmiany na drugą, kopiuj / wklej może być lepiej niż faktoringiem wspólnej funkcjonalności. Jeśli kod wykorzystuje dwie identyczne metody znak po znaku, robi to samo dla różnych celów i konsekwentnie opiera wybór metody na wymaganym celu, to jeśli później trzeba będzie nieco zrobić coś dla jednego z tych celów, zmieniając tylko odpowiednie metoda wpłynie tylko na te zachowania, na które należy mieć wpływ. Zastosowanie jednej wspólnej metody mogłoby znacznie utrudnić zmianę .
supercat
7

Jeśli twoje funkcje stają się bardziej skomplikowane i dłuższe (a przez to mniej czytelne), kiedy próbujesz je osuszyć, oznacza to, że robisz to źle. Próbujesz umieścić zbyt wiele funkcji w jednej funkcji, ponieważ uważasz, że jest to jedyny sposób uniknięcia powtarzania tych samych fragmentów kodu w drugiej funkcji.

Tworzenie kodu DRY prawie zawsze oznacza refaktoryzację wspólnej funkcjonalności do mniejszych funkcji. Każda z tych funkcji powinna być prostsza (a przez to bardziej czytelna) niż pierwotna. Aby utrzymać wszystko w pierwotnej funkcji na tym samym poziomie abstrakcji, może to również oznaczać refaktoryzację dodatkowych części, które nie są używane gdzie indziej. Oryginalna funkcja staje się wtedy „funkcją wysokiego poziomu”, wywołującą mniejsze, a także staje się mniejsza i prostsza.

Konsekwencją tego jest przede wszystkim różne poziomy abstrakcji w kodzie - a niektórzy uważają, że tego rodzaju kod jest mniej czytelny. Z mojego doświadczenia wynika, że ​​jest to błąd. Kluczową kwestią jest nadanie mniejszym funkcjom dobrych nazw, wprowadzenie dobrze nazwanych typów danych i abstrakcji, które druga osoba może łatwo uchwycić. W ten sposób „OSUSZANIE” i „czytelność” powinny bardzo, bardzo rzadko wchodzić w konflikt.

Doktor Brown
źródło
2

Chociaż nie jestem pewien, co dokładnie powoduje ten problem, moje doświadczenie jest takie, że gdy zauważysz, że DRY i czytelność są sprzeczne, oznacza to, że nadszedł czas, aby coś zreformować.

Loren Pechtel
źródło
0

Wybrałbym opcję odczytu (= konserwowalną) zamiast DRY w dowolnym dniu tygodnia. W rzeczywistości oba często się wyrównują, ktoś, kto zna koncepcję DRY, generalnie generuje (głównie) czytelny kod.

Zdravko Danev
źródło
2
bez wyjaśnienia odpowiedź ta może stać się bezużyteczna w przypadku, gdy ktoś inny wyrazi przeciwną opinię. Na przykład, jeśli ktoś opublikuje takie stwierdzenie: „Wybrałbym opcję DRY (= utrzymywalną) zamiast czytelnej z dowolnego dnia tygodnia”, w jaki sposób ta odpowiedź pomogłaby czytelnikowi wybrać dwie sprzeczne opinie? Rozważ edycję go w lepszym stanie
komnata
0

Bardzo, bardzo, bardzo, bardzo, bardzo rzadko w mojej karierze znalazłem uzasadniony przypadek, który dowodzi czytelności przeciwko DRY. Najpierw uczyń to czytelnym. Nazwij wszystko dobrze. Skopiuj i wklej w razie potrzeby.

Teraz cofnij się i spójrz na całość, którą stworzyłeś, jak artysta cofający się, by spojrzeć na swoje malarstwo. Teraz możesz poprawnie zidentyfikować powtórzenie.

Powtarzany kod powinien zostać przekształcony we własną metodę lub wyciągnięty do własnej klasy.

Powtarzanie kodu powinno być ostatecznością. Najpierw do odczytu i wysychania, w przeciwnym razie nie do odczytania, jeśli ograniczysz zakres powtarzanego kodu.

Greg Burghardt
źródło