Jednowarstwowe a czytelność: kiedy przestać redukować kod? [Zamknięte]

14

Kontekst

Ostatnio zainteresowałem się tworzeniem lepiej sformatowanego kodu. Mówiąc lepiej, mam na myśli „przestrzeganie zasad zatwierdzonych przez wystarczającą liczbę osób, aby uznać to za dobrą praktykę” (ponieważ oczywiście nigdy nie będzie jednego unikalnego „najlepszego” sposobu kodowania).

Obecnie głównie piszę w Ruby, więc zacząłem używać linijki (Rubocop), aby dostarczyć mi informacji o „jakości” mojego kodu (ta „jakość” jest zdefiniowana w przewodniku projektu w stylu ruby prowadzonym przez społeczność ).

Zauważ, że użyję „jakości” jak w „jakości formatowania”, nie tyle o wydajności kodu, nawet jeśli w niektórych przypadkach na efektywność kodu ma wpływ sposób jego pisania.

W każdym razie, robiąc to wszystko, uświadomiłem sobie (a przynajmniej pamiętałem) kilka rzeczy:

  • Niektóre języki (w szczególności Python, Ruby i tym podobne) pozwalają tworzyć świetne jednowierszowe kody
  • Przestrzeganie kilku wskazówek dotyczących kodu może znacznie go skrócić, a jednocześnie nadal bardzo jasne
  • Jednak zbyt ścisłe przestrzeganie tych wskazówek może sprawić, że kod będzie mniej przejrzysty / czytelny
  • Kod może prawie całkowicie przestrzegać niektórych wytycznych i nadal być niskiej jakości
  • Czytelność kodu jest w większości subiektywna (jak w „to, co według mnie może być całkowicie niejasne dla innych programistów”)

To tylko obserwacje, a nie bezwzględne reguły. Zauważysz również, że czytelność kodu i przestrzeganie wytycznych może wydawać się w tym momencie niezwiązane, ale tutaj wytyczne są sposobem na zawężenie liczby sposobów przepisania jednego fragmentu kodu.

Teraz kilka przykładów, aby wszystko to wyjaśnić.

Przykłady

Weźmy prosty przypadek użycia: mamy aplikację z Usermodelem „ ”. Użytkownik ma opcjonalny firstnamei surnameobowiązkowy emailadres.

Chcę napisać metodę „ name”, która zwróci następnie nazwę ( firstname + surname) użytkownika, jeśli przynajmniej jego firstnamelub surnamejest obecny, lub jego emailwartość zastępczą, jeśli nie.

Chcę również, aby ta metoda przyjmowała use_emailparametr „ ” (boolean), pozwalając na użycie adresu e-mail użytkownika jako wartości zastępczej. Ten use_emailparametr „ ” powinien być domyślnie (jeśli nie przekazany) jako „ true”.

Najprostszym sposobem na napisanie tego w języku Ruby byłoby:

def name(use_email = true)
 # If firstname and surname are both blank (empty string or undefined)
 # and we can use the email...
 if (firstname.blank? && surname.blank?) && use_email
  # ... then, return the email
  return email
 else
  # ... else, concatenate the firstname and surname...
  name = "#{firstname} #{surname}"
  # ... and return the result striped from leading and trailing spaces
  return name.strip
 end
end

Ten kod jest najprostszym i najłatwiejszym do zrozumienia sposobem na zrobienie tego. Nawet dla kogoś, kto nie mówi „Ruby”.

Teraz spróbujmy to skrócić:

def name(use_email = true)
 # 'if' condition is used as a guard clause instead of a conditional block
 return email if (firstname.blank? && surname.blank?) && use_email
 # Use of 'return' makes 'else' useless anyway
 name = "#{firstname} #{surname}"
 return name.strip
end

Jest to krótszy, wciąż łatwy do zrozumienia, a nawet łatwiejszy (klauzula ochronna jest bardziej naturalna do odczytania niż blok warunkowy). Klauzula ochronna sprawia, że ​​jest ona bardziej zgodna z wytycznymi, których używam, więc korzystajcie z tego wszyscy. Zmniejszamy również poziom wcięcia.

Teraz użyjmy trochę magii Ruby, aby była jeszcze krótsza:

def name(use_email = true)
 return email if (firstname.blank? && surname.blank?) && use_email
 # Ruby can return the last called value, making 'return' useless
 # and we can apply strip directly to our string, no need to store it
 "#{firstname} #{surname}".strip
end

Jeszcze krótszy i idealnie zgodny z wytycznymi ... ale o wiele mniej wyraźny, ponieważ brak deklaracji zwrotnej sprawia, że ​​jest to nieco mylące dla tych, którzy nie znają tej praktyki.

To tutaj możemy zacząć zadawać pytanie: czy naprawdę jest tego warte? Jeśli powiemy „nie, uczyń to czytelnym i dodaj return” ”(wiedząc, że nie będzie to zgodne z wytycznymi). A może powinniśmy powiedzieć: „W porządku, to po rubinsku, naucz się tego cholernego języka!”?

Jeśli weźmiemy opcję B, to dlaczego nie uczynić jej jeszcze krótszą:

def name(use_email = true)
 (email if (firstname.blank? && surname.blank?) && use_email) || "#{firstname} #{surname}".strip
end

Oto on-lineer! Oczywiście jest krótszy ... tutaj wykorzystujemy fakt, że Ruby zwróci wartość lub inną w zależności od tego, która z nich jest zdefiniowana (ponieważ e-mail zostanie zdefiniowany pod tymi samymi warunkami, co wcześniej).

Możemy również napisać:

def name(use_email = true)
 (email if [firstname, surname].all?(&:blank?) && use_email) || "#{firstname} #{surname}".strip
end

Jest krótki, niezbyt trudny do odczytania (to znaczy wszyscy widzieliśmy, jak może wyglądać brzydki jednowarstwowy), dobry Ruby, jest zgodny z wytycznymi, których używam ... Ale nadal, w porównaniu z pierwszym sposobem pisania to jest o wiele mniej łatwe do odczytania i zrozumienia. Możemy również argumentować, że ta linia jest za długa (ponad 80 znaków).

Pytanie

Niektóre przykłady kodu mogą pokazać, że wybór między kodem „pełnowymiarowym” a wieloma jego zredukowanymi wersjami (aż do słynnego jednowierszowego) może być trudny, ponieważ, jak widzimy, jednowierszowy może nie być aż tak przerażający, ale jednak nic nie przebije kodu „pełnowymiarowego” pod względem czytelności ...

Oto prawdziwe pytanie: gdzie się zatrzymać? Kiedy jest krótki, wystarczająco krótki? Jak się dowiedzieć, kiedy kod staje się „zbyt krótki” i mniej czytelny (pamiętając, że jest dość subiektywny)? I jeszcze więcej: jak zawsze odpowiednio kodować i unikać mieszania jednowarstwowych z „pełnowymiarowymi” fragmentami kodu, kiedy tylko mam na to ochotę?

TL; DR

Najważniejsze pytanie brzmi: jeśli chodzi o wybór między „długim, ale jasnym, czytelnym i zrozumiałym fragmentem kodu” a „mocnym, krótszym, ale trudniejszym do odczytania / zrozumienia linkiem”, wiedząc, że te dwa elementy są najważniejsze dno skali, a nie dwie jedyne opcje: jak określić, gdzie jest granica między „wystarczająco wyraźnym” a „nie tak wyraźnym, jak powinna być”?

Głównym pytaniem nie jest klasyczne „One-linery vs. czytelność: który z nich jest lepszy?” ale „Jak znaleźć równowagę między tymi dwoma?”

Edytuj 1

Komentarze w przykładach kodu mają być „ignorowane”, mają na celu wyjaśnienie, co się dzieje, ale nie należy ich brać pod uwagę przy ocenie czytelności kodu.

Sudiukil
źródło
7
Zbyt krótka odpowiedź: powtarzaj refaktoryzację, dopóki nie masz pewności, że jest lepsza niż poprzednia iteracja, a następnie zatrzymaj i odwróć ostatnią refaktoryzację.
Dom
8
Wolę wariant 3 z returndodanym słowem kluczowym . Te siedem postaci dodaje mi trochę jasności w oczach.
cmaster
2
Jeśli czujesz się naprawdę okropnie, możesz napisać całość jako [firstname,surname,!use_email].all?(&:blank?) ? email : "#{firstname} #{surname}".strip... ponieważ false.blank?zwraca true, a operator potrójny oszczędza ci kilka znaków ... ¯ \ _ (ツ) _ / ¯
DaveMongoose
1
OK, muszę zapytać: jaką przejrzystość returnma dodać słowo kluczowe ?! Nie zawiera żadnych informacji . To czysty bałagan.
Konrad Rudolph
2
Pogląd, że zwięzłość rodzi jasność, nie tylko podlega prawu malejących powrotów, ale odwraca się, gdy popychany jest do skrajności. Jeśli przepisujesz, aby skrócić krótką funkcję, marnujesz czas, to samo dotyczy próby uzasadnienia tej praktyki.
sdenham

Odpowiedzi:

26

Bez względu na kod, który piszesz, najlepiej jest go odczytać. Krótki jest drugim najlepszym. A czytelność oznacza zwykle wystarczająco krótko, aby można było zrozumieć kod, dobrze nazwane identyfikatory i przestrzegać wspólnych idiomów języka, w którym kod jest napisany.

Gdyby był to język niezależny od języka, myślę, że zdecydowanie byłby oparty na opiniach, ale w granicach języka Ruby myślę, że możemy na to odpowiedzieć.

Po pierwsze, cechą i idiomatycznym sposobem pisania Rubiego jest pominięcie returnsłowa kluczowego podczas zwracania wartości, chyba że wraca wcześniej z metody.

Kolejną połączoną cechą i idiomem jest używanie końcowych ifinstrukcji w celu zwiększenia czytelności kodu. Jednym z głównych pomysłów w Rubim jest pisanie kodu, który brzmi jak język naturalny. W tym celu przechodzimy do _why's Poignant Guide to Ruby, rozdział 3 .

Przeczytaj sobie na głos następujące informacje.

5.times { print "Odelay!" }

W zdaniach angielskich znaki interpunkcyjne (takie jak kropki, wykrzykniki, nawiasy) są ciche. Interpunkcja dodaje znaczenia słowom, pomaga wskazać, co autor zamierzał w zdaniu. Przeczytajmy więc powyżej: Pięć razy drukuj „Odelay!”.

Biorąc to pod uwagę, przykład kodu nr 3 jest najbardziej idiomatyczny dla Ruby:

def name(use_email = true)
  return email if firstname.blank? && surname.blank? && use_email

  "#{firstname} #{surname}".strip
end

Teraz, gdy czytamy kod, mówi:

Zwróć e-mail, jeśli imię jest puste, a nazwisko jest puste i użyj adresu e-mail

(powrót) imię i nazwisko pozbawione

Co jest dość cholernie zbliżone do faktycznego kodu Ruby.

To tylko 2 wiersze rzeczywistego kodu, więc jest dość zwięzły i przestrzega idiomów języka.

Greg Burghardt
źródło
Niezły punkt. To prawda, że ​​pytanie nie było przeznaczone dla języka Ruby, ale zgadzam się, że nie jest możliwe uzyskanie odpowiedzi agnostycznej z języka.
Sudiukil
8
Uważam, że pomysł, aby kod brzmiał jak język naturalny, jest przereklamowany (a czasem nawet problematyczny). Ale nawet bez tej motywacji dochodzę do tego samego wniosku co ta odpowiedź.
Konrad Rudolph
1
Jest jeszcze jedna poprawka, którą chciałbym rozważyć w kodzie. To znaczy postawić use_emailprzed innymi warunkami, ponieważ jest to zmienna, a nie wywołanie funkcji. Ale i tak interpolacja łańcuchów i tak zamienia różnicę.
John Dvorak
Struktura kodu zgodna ze strukturami języka naturalnego może sprawić, że wpadniesz w pułapki językowe. Na przykład, kiedy czytasz kolejne wymagania do send an email if A, B, C but no D, przestrzeganie twojego założenia byłoby naturalne, aby wpisać 2 bloki if / else , kiedy prawdopodobnie łatwiej byłoby napisać kod if not D, send an email. Zachowaj ostrożność w trakcie czytania języka naturalnego i przekształć go w kod, ponieważ może on skłonić Cię do napisania nowej wersji „Niekończącej się historii” . Z klasami, metodami i zmiennymi. W końcu nie jest to wielka sprawa.
Laiv
@Laiv: Czytanie kodu jak język naturalny nie oznacza dosłownie tłumaczenia wymagań. Oznacza to pisanie kodu, tak aby czytany na głos umożliwiał czytelnikowi zrozumienie logiki bez odczytywania każdego kawałka kodu, znaku dla znaku, konstrukcji języka dla konstrukcji języka. Jeśli kodowanie if !Djest lepsze, to w porządku, tak długo, jak Dma sensowną nazwę. A jeśli !operator zgubi się wśród innych kodów, NotDodpowiednie byłoby posiadanie identyfikatora o nazwie .
Greg Burghardt,
15

Nie sądzę, że uzyskasz lepszą odpowiedź niż „użyj najlepszej oceny”. Krótko mówiąc, powinieneś dążyć raczej do jasności niż do krótkości . Często najkrótszy kod jest również najczystszy, ale jeśli skupisz się tylko na osiągnięciu skrótu, może to ucierpieć. Widać to wyraźnie w dwóch ostatnich przykładach, które wymagają więcej wysiłku, aby zrozumieć niż trzy poprzednie przykłady.

Ważną kwestią jest odbiorca kodu. Czytelność jest oczywiście całkowicie zależna od osoby czytającej. Czy ludzie, których spodziewasz się przeczytać kod (oprócz ciebie) faktycznie znają idiomy języka Ruby? Cóż, to pytanie nie jest czymś, na co przypadkowi ludzie w Internecie mogą odpowiedzieć, to tylko twoja decyzja.

JacquesB
źródło
Zgadzam się z punktem widzenia publiczności, ale jest to część mojej walki: ponieważ moje oprogramowanie jest często typu open source, publiczność może składać się zarówno z początkujących, jak i „rubinowych bogów”. Mógłbym w prosty sposób udostępnić go większości ludzi, ale wydaje mi się, że to marnowanie zalet oferowanych przez język.
Sudiukil
1
Jako ktoś, kto musiał przejąć, rozszerzyć i utrzymywać naprawdę okropny kod, jasność musi wygrać. Zapamiętaj stare przysłowie - Napisz kod tak, jakby opiekun był mściwym Piekielnym Aniołem, który wie, gdzie mieszkasz i gdzie dzieci chodzą do szkoły.
u
2
@Sudiukil: To ważny punkt. Sugeruję, aby w tym przypadku dążyć do kodu idiomatycznego (tzn. Założyć dobrą znajomość języka), ponieważ jest mało prawdopodobne, aby początkujący przyczynili się do kodu open source. (Lub jeśli to zrobią, będą przygotowani do podjęcia wysiłku, aby nauczyć się języka.)
JacquesB
7

Częściowym problemem tutaj jest „czym jest czytelność”. Dla mnie patrzę na twój pierwszy przykład kodu:

def name(use_email = true)
 # If firstname and surname are both blank (empty string or undefined)
 # and we can use the email...
 if (firstname.blank? && surname.blank?) && use_email
  # ... then, return the email
  return email
 else
  # ... else, concatenate the firstname and surname...
  name = "#{firstname} #{surname}"
  # ... and return the result striped from leading and trailing spaces
  return name.strip
 end
end

Trudno mi ją przeczytać, ponieważ jest pełna „głośnych” komentarzy, które po prostu powtarzają kod. Usuń je:

def name(use_email = true)
 if (firstname.blank? && surname.blank?) && use_email
  return email
 else
  name = "#{firstname} #{surname}"
  return name.strip
 end
end

i teraz jest o wiele bardziej czytelny. Czytając to, myślę „hmm, zastanawiam się, czy Ruby obsługuje operatora trójskładnikowego? W C # mogę napisać to jako:

string Name(bool useEmail = true) => 
    firstName.Blank() && surname.Blank() && useEmail 
    ? email 
    : $"{firstname} {surname}".Strip();

Czy coś takiego jest możliwe w rubinie? Po przejrzeniu twojego postu widzę, że są:

def name(use_email = true)
 (email if (firstname.blank? && surname.blank?) && use_email) || "#{firstname} #{surname}".strip
end

Wszystkie dobre rzeczy. Ale to nie jest dla mnie czytelne; po prostu dlatego, że muszę przewijać, aby zobaczyć całą linię. Naprawmy to:

def name(use_email = true)
 (email if (firstname.blank? && surname.blank?) && use_email) 
 || "#{firstname} #{surname}".strip
end

Teraz jestem szczęśliwy. Nie jestem całkowicie pewien, jak działa składnia, ale rozumiem, co robi kod.

Ale to tylko ja. Inni ludzie mają bardzo różne pomysły na to, co sprawia, że ​​czytanie kodu jest przyjemne. Dlatego podczas pisania kodu musisz znać swoich odbiorców. Jeśli jesteś absolutnym początkującym nauczycielem, zechcesz to uprościć i być może napisać to tak, jak na pierwszym przykładzie. Jeśli pracujesz w grupie profesjonalnych programistów z wieloletnim doświadczeniem w języku Ruby, napisz kod, który korzysta z języka i krótko go używaj. Jeśli jest gdzieś pomiędzy, to celuj gdzieś pomiędzy.

Powiem jednak jedno: strzeż się „sprytnego kodu”, takiego jak w twoim ostatnim przykładzie. Zadaj sobie pytanie, czy [firstname, surname].all?(&:blank?)dodaje coś innego niż sprawia, że ​​czujesz się sprytny, ponieważ popisujesz się swoimi umiejętnościami, nawet jeśli teraz jest trochę trudniej czytać? Powiedziałbym, że ten przykład prawdopodobnie pasuje do tej kategorii. Jeśli porównasz pięć wartości, uważam to za dobry kod. Więc znowu, nie ma tutaj absolutnej linii, pamiętaj tylko, że jesteś zbyt sprytny.

Podsumowując: czytelność wymaga znajomości odbiorców i odpowiedniego ukierunkowania kodu oraz pisania zwięzłego, ale przejrzystego kodu; nigdy nie pisz „sprytnego” kodu. Mów krótko, ale nie za krótko.

David Arno
źródło
2
Cóż, zapomniałem o tym wspomnieć, ale komentarze miały być „zignorowane”, są tutaj tylko po to, aby pomóc tym, którzy nie znają dobrze Ruby. Ważny punkt na temat publiczności, nie myślałem o tym. Jeśli chodzi o wersję, która sprawia, że ​​jesteś szczęśliwy: jeśli liczy się długość linii, trzecia wersja mojego kodu (ta z tylko jedną instrukcją return) robi to i jest jeszcze bardziej zrozumiała, prawda?
Sudiukil
1
@Sudiukil, nie będąc programistą ruby, uznałem, że jest najtrudniejszy do czytania i nie pasuje do tego, czego szukałem (z perspektywy innego języka) jako „najlepszego” rozwiązania. Jednak dla kogoś, kto zna fakt, że ruby ​​jest jednym z tych języków, który zwraca wartość ostatniego wyrażenia, prawdopodobnie reprezentuje najprostszą, najłatwiejszą do odczytania wersję. Znów chodzi o twoją publiczność.
David Arno
Nie jestem programistą Ruby, ale ma to dla mnie o wiele więcej sensu niż odpowiedź, która jest najczęściej głosowana, i brzmi: „Oto, co wrócę [przypis: pod konkretnym długim warunkiem]. Ponadto, oto ciąg, który spóźnił się na imprezę." Logika, która jest w zasadzie tylko instrukcją przypadku, powinna być zapisywana jak jedna jednolita instrukcja sprawy, a nie rozłożona na wiele pozornie niezwiązanych instrukcji.
Paul
Osobiście poszedłbym z twoim drugim blokiem kodu, z wyjątkiem tego, że połączyłbym dwie instrukcje z twojej innej gałęzi w jedną:return "#{firstname} #{surname}".strip
Paul
2

Jest to prawdopodobnie pytanie, w którym trudno nie udzielić odpowiedzi opartej na opiniach, ale oto moje dwa centy.

Jeśli okaże się, że skrócenie kodu nie wpływa na czytelność, a nawet poprawia go, idź do niego. Jeśli kod staje się mniej czytelny, musisz rozważyć, czy istnieje dość dobry powód, aby go tak zostawić. Robienie tego tylko dlatego, że jest krótsze lub fajne, albo tylko dlatego, że możesz, to przykłady złych powodów. Musisz także rozważyć, czy skrócenie kodu może uczynić go mniej zrozumiałym dla innych osób, z którymi współpracujesz.

Jaki byłby dobry powód? To naprawdę wezwanie do osądu, ale przykładem może być optymalizacja wydajności (po testach wydajności oczywiście nie wcześniej). Coś, co daje ci jakąś korzyść, za którą jesteś gotów zapłacić przy zmniejszonej czytelności. W takim przypadku możesz złagodzić tę wadę, dostarczając pomocnego komentarza (który wyjaśnia, co robi kod i dlaczego musiał zostać nieco zaszyfrowany). Co więcej, możesz wyodrębnić ten kod do oddzielnej funkcji o sensownej nazwie, aby była to tylko jedna linia w witrynie wywoływania, która wyjaśnia, co się dzieje (poprzez nazwę funkcji) bez wchodzenia w szczegóły (jednak ludzie różnią się opinie na ten temat, więc jest to kolejne wezwanie do osądu).

Filip Milovanović
źródło
1

Odpowiedź jest nieco subiektywna, ale musisz zadać sobie całą uczciwość, jaką możesz zdobyć, czy będziesz w stanie zrozumieć ten kod, kiedy wrócisz do niego za miesiąc lub dwa.

Każda zmiana powinna poprawić zdolność przeciętnego użytkownika do zrozumienia kodu. Aby kod był zrozumiały, pomocne są następujące wytyczne:

  • Przestrzegaj idiomów języka . C #, Java, Ruby, Python mają swoje preferowane sposoby robienia tego samego. Konstrukcje idiomatyczne pomagają zrozumieć kod, którego nie znasz.
  • Zatrzymaj, gdy kod stanie się mniej czytelny . W podanym przykładzie miało to miejsce, gdy trafiłeś ostatnie pary kodu redukującego. Straciłeś idiomatyczną przewagę z poprzedniego przykładu i wprowadziłeś wiele symboli, które wymagają dużo myślenia, aby naprawdę zrozumieć, co się dzieje.
  • Komentarze należy wykorzystywać tylko wtedy, gdy trzeba uzasadnić coś nieoczekiwanego . Wiem, że twoje przykłady miały na celu wyjaśnienie konstrukcji osobom mniej znającym Ruby, i to jest w porządku na pytanie. Wolę używać komentarzy, aby wyjaśnić nieoczekiwane reguły biznesowe i unikać ich, jeśli kod może mówić sam za siebie.

To powiedziawszy, są chwile, w których rozszerzony kod pomaga zrozumieć, co się dzieje lepiej. Jeden przykład z tego pochodzi z C # i LINQ. LINQ jest doskonałym narzędziem i może poprawić czytelność w niektórych sytuacjach, ale natknąłem się również na wiele sytuacji, w których było to znacznie bardziej mylące. Miałem pewne opinie w recenzjach, które sugerowały przekształcenie wyrażenia w pętlę z odpowiednimi instrukcjami if, aby inni mogli go lepiej utrzymywać. Kiedy się zastosowałem, mieli rację. Technicznie, LINQ jest bardziej idiomatyczny dla C #, ale są przypadki, w których obniża to zrozumiałość, a bardziej szczegółowe rozwiązanie go poprawia.

Mówię to wszystko, aby to powiedzieć:

Popraw, kiedy możesz ulepszyć swój kod (bardziej zrozumiały)

Pamiętaj, że ty lub ktoś taki jak ty będziesz musiał zachować ten kod później. Następnym razem, gdy natkniesz się na to, może upłynąć kilka miesięcy. Zrób sobie przysługę i nie gonić za zmniejszaniem liczby wierszy kosztem zrozumienia kodu.

Berin Loritsch
źródło
0

Czytelność to właściwość, którą chcesz mieć, posiadanie wielu linijek nie jest. Zatem zamiast „jednokreskowych kontra czytelność” pytanie powinno brzmieć:

Kiedy jednolinijki zwiększają czytelność i kiedy to szkodzą?

Uważam, że wkładki jednowarstwowe poprawiają czytelność, gdy spełniają te dwa warunki:

  1. Są zbyt szczegółowe, aby można je było wyodrębnić do funkcji.
  2. Nie chcesz przerywać „przepływu” odczytu otaczającego kodu.

Załóżmy na przykład, że namenie była to dobra ... nazwa dla twojej metody. To połączenie imienia i nazwiska lub użycie adresu e-mail zamiast imienia nie było czymś naturalnym. Zamiast nametego najlepsza rzecz, jaką można wymyślić, okazała się długa i kłopotliwa:

puts "Name: #{user.email_if_there_is_no_name_otherwise_use_firstname_and_surname(use_email)}"

Tak długa nazwa wskazuje, że jest to bardzo specyficzne - gdyby było bardziej ogólne, można by znaleźć bardziej ogólną nazwę. Tak więc zawinięcie go w metodę nie pomaga ani w czytelności (jest za długa), ani w OSUSZANIU (zbyt specyficznym, aby można go było użyć gdzie indziej), więc lepiej po prostu zostawić tam kod.

Nadal - dlaczego warto uczynić z niego jedną wkładkę? Są zwykle mniej czytelne niż kod wielowierszowy. Tutaj powinniśmy sprawdzić mój drugi warunek - przepływ otaczającego kodu. Co jeśli masz coś takiego:

puts "Group: #{user.group}"
puts "Title: #{user.title}"
if user.firstname.blank? && user.surname.blank?) && use_email
  name = email
else
  name = "#{firstname} #{surname}"
  name.strip
end
puts "Name: #{name}"
puts "Age: #{user.age}"
puts "Address: #{user.address}"

Sam kod wielowierszowy jest czytelny - ale gdy próbujesz odczytać otaczający kod (drukując różne pola), konstrukcja wielowierszowa zakłóca przepływ. Łatwiej to odczytać:

puts "Group: #{user.group}"
puts "Title: #{user.title}"
puts "Name: #{(email if (user.firstname.blank? && user.surname.blank?) && use_email) || "#{user.firstname} #{user.surname}".strip}"
puts "Age: #{user.age}"
puts "Address: #{user.address}"

Twój przepływ nie jest przerywany i możesz skupić się na konkretnym wyrażeniu, jeśli potrzebujesz.

Czy to twoja sprawa? Absolutnie nie!

Pierwszy warunek jest mniej istotny - już uznałeś, że jest wystarczająco ogólny, aby zasłużyć na metodę, i wymyśliłeś nazwę tej metody, która jest znacznie bardziej czytelna niż jej implementacja. Oczywiście nie wyodrębnisz go ponownie do funkcji.

Co do drugiego warunku - czy zakłóca przepływ otaczającego kodu? Nie! Otaczający kod jest deklaracją metody, która wybieraname jest jedynym celem. Logika wybierania nazwy nie zakłóca przepływu otaczającego kodu - to jest właśnie cel otaczającego kodu!

Wniosek - nie rób całego ciała funkcji jednowierszowym

Jednowarstwowe są dobre, gdy chcesz zrobić coś trochę skomplikowanego bez zakłócania przepływu. Deklaracja funkcji już zakłóca przepływ (tak, że nie zostanie przerwana, gdy wywołasz tę funkcję), więc uczynienie całego korpusu funkcji jednowierszowym nie poprawia czytelności.

Uwaga

Mam na myśli funkcje i metody „w pełni rozwinięte” - nie funkcje wbudowane ani wyrażenia lambda, które zwykle częścią otaczającego kodu i muszą mieścić się w jego przepływie.

Idan Arye
źródło