Czy długie metody są zawsze złe? [Zamknięte]


64

Rozglądając się wcześniej, zauważyłem kilka uwag na temat złych praktyk będących długimi metodami.

Nie jestem pewien, czy zawsze zgadzam się, że długie metody są złe (i chcieliby opinii innych).

Na przykład mam kilka widoków Django, które przetwarzają obiekty przed wysłaniem ich do widoku, przy czym długa metoda to 350 linii kodu. Mam napisany kod, który zajmuje się parametrami - sortowaniem / filtrowaniem zestawu zapytań, a następnie krok po kroku wykonuje pewne przetwarzanie na obiektach, które zwróciło moje zapytanie.

Przetwarzanie jest więc głównie agregacją warunkową, która ma wystarczająco złożone reguły, których nie można łatwo wykonać w bazie danych, więc mam pewne zmienne zadeklarowane poza główną pętlą, a następnie zmienione podczas pętli.

variable_1 = 0
variable_2 = 0
for object in queryset :
     if object.condition_condition_a and variable_2 > 0 :
     variable 1+= 1
     .....
     ...    
     . 
      more conditions to alter the variables

return queryset, and context 

Tak więc zgodnie z teorią powinienem rozłożyć cały kod na mniejsze metody, aby mieć metodę wyświetlania jako maksymalnie jedną stronę.

Jednakże, pracując w przeszłości na różnych podstawach kodu, czasami okazuje się, że kod ten jest mniej czytelny, gdy trzeba ciągle przeskakiwać z jednej metody do drugiej, obmyślając wszystkie jej części, zachowując przy tym najbardziej zewnętrzną metodę.

Uważam, że mając długą metodę, która jest dobrze sformatowana, możesz łatwiej zobaczyć logikę, ponieważ nie ukrywa się ona w wewnętrznych metodach.

Mógłbym rozdzielić kod na mniejsze metody, ale często stosuje się wewnętrzną pętlę dla dwóch lub trzech rzeczy, więc spowodowałoby to bardziej złożony kod lub metody, które nie robią jednej rzeczy, ale dwie lub trzy (alternatywnie Mógłbym powtarzać wewnętrzne pętle dla każdego zadania, ale wtedy będzie hit wydajności).

Czy jest więc przypadek, że długie metody nie zawsze są złe? Czy zawsze istnieje uzasadnienie dla metod pisania, gdy będą one używane tylko w jednym miejscu?

AKTUALIZACJA: Wygląda na to, że zadałem to pytanie ponad rok temu.

Ponownie przebudowałem kod po (mieszanej) odpowiedzi tutaj, podzieliłem go na metody. Jest to aplikacja Django pobierająca złożone zestawy powiązanych obiektów z bazy danych, więc argument testowy jest wyłączony (prawdopodobnie zajęłoby to większą część roku, aby utworzyć odpowiednie obiekty dla przypadków testowych. Mam typ „tego wymagano wczoraj” środowisko pracy, zanim ktokolwiek narzeka). Naprawianie błędów w tej części kodu jest teraz marginalnie łatwiejsze, ale nie tak ogromne.

przed :

#comment 1 
bit of (uncomplicated) code 1a  
bit of code 2a

#comment 2 
bit of code 2a
bit of code 2b
bit of code 2c

#comment 3
bit of code 3

teraz:

method_call_1
method_call_2
method_call_3

def method_1 
    bit of (uncomplicated) code 1a  
    bit of code 2a

def method_2 
    bit of code 2a
    bit of code 2b
    bit of code 2c

def method_3
    bit of code 3

156
Wszystkie absoluty są złe. Zawsze.
Joachim Sauer

30
Widzę argument „wyodrębnij metody, jeśli możesz ich ponownie użyć” dość często (w tej lub podobnej formie), ale nie kupuję go: jeśli metoda robi więcej niż jedną rzecz, powinieneś wyodrębnić z niej metody dla czytelności / łatwość konserwacji, nawet jeśli te nowe metody są wywoływane tylko z jednego miejsca w kodzie.
Joachim Sauer

4
@gnat: wow, czy jakieś ręczne wstawianie (za pomocą preprocesora) w kroku kompilacji nie byłoby tutaj lepszym rozwiązaniem?
Joachim Sauer

11
Jeden z najlepszych programistów, których znam, skomentował, że jeśli naprawdę chcesz miary, liczba zmiennych lokalnych jest lepsza niż rzeczywista długość. Pracował nad złożonym optymalizatorem ścieżki, w którym wnętrzności były jedną metodą o długości kilkuset linii, ale utrzymywana ilość stanu (zmienne lokalne) była bardzo mała.
Chuu,

2
Długie metody nie zawsze są złe; ale są czymś, na co zawsze należy spojrzeć i zadać sobie pytanie „czy to źle?”
Carson63000,

Odpowiedzi:


80

Nie, długie metody nie zawsze są złe.

W książce Code Complete mierzy się, że długie metody są czasem szybsze i łatwiejsze do napisania i nie prowadzą do problemów konserwacyjnych.

W rzeczywistości naprawdę ważne jest, aby pozostać SUCHYM i szanować rozdzielenie obaw. Czasami obliczenia są po prostu długie, ale naprawdę nie będą powodować problemów w przyszłości.

Jednak z mojego osobistego doświadczenia wynika, że ​​w większości długich metod brakuje oddzielenia troski. W rzeczywistości długie metody są łatwym sposobem na wykrycie, że coś może być nie tak w kodzie, i że należy tutaj zachować szczególną ostrożność podczas przeglądania kodu.

EDYCJA: Gdy pojawiają się komentarze, dodam interesujący punkt do odpowiedzi. W rzeczywistości sprawdziłbym również wskaźniki złożoności funkcji (NPATH, cykliczność złożoności lub nawet lepszy CRAP).

W rzeczywistości zalecam, aby nie sprawdzać takich wskaźników w przypadku długich funkcji, ale włączać alert o nich za pomocą zautomatyzowanych narzędzi (takich jak na przykład styl sprawdzania java) NA KAŻDEJ FUNKCJI.


41
+1: „Nie, długie metody nie zawsze są złe”, ale prawie zawsze są złe
Binary Worrier

67
Długie ciała metod są klasycznym zapachem kodu : sam w sobie nie stanowi problemu, ale wskazuje na to, że prawdopodobnie jest tam problem.
Joachim Sauer

6
+1, ale nadal zalecałbym sprawdzenie cyklicznej złożoności długiej metody. Wysokie wartości wskazują metody, których testowanie jednostkowe jest niemożliwe (a metody długie bardzo rzadko pozbawione są logiki sterowania przepływem).
Daniel B

11
Używam nazw metod, aby zminimalizować komentarze. Co czasami prowadzi do takich rzeczy jak „getSelectedNodeWithChildren”, ale mój kolega ciągle mi mówi, że mój kod jest dobrze czytelny. Staram się też unikać skrótów, są fajne do pisania, ale nie tak miłe do czytania.
K ..

4
@ da_b0uncer Jest to również polityka, którą przestrzegam. Trudniej jest odczytać kod niż go napisać, więc dodatkowy wysiłek podczas pisania, aby uczynić kod bardziej czytelnym, zwraca się.
deadalnix

55

Większość z naciskiem tutaj wydaje się wokół słowa zawsze . Tak, absoluty są złe, a inżynieria oprogramowania to prawie tyle samo sztuki, co nauka, i tak dalej ... ale muszę powiedzieć, że w podanym przez ciebie przykładzie metoda byłaby lepsza, gdyby została podzielona w górę. Oto argumenty, których zwykle używam do uzasadnienia podziału twojej metody:

Czytelność: Nie jestem pewien co do innych, ale nie mogę szybko odczytać 350 linii kodu. Tak, jeśli jest to mój własny kod i mogę poczynić wiele założeń na jego temat, mógłbym przejrzeć go bardzo szybko, ale to poza tym. Zastanów się, o ile łatwiej byłoby odczytać tę metodę, gdyby składała się z 10 wywołań innych metod (każda o nazwie opisowej). Robiąc to, wprowadziłeś warstwowanie w kodzie, a metoda wysokiego poziomu daje czytelnikowi krótki, słodki zarys tego, co się dzieje.

Edycja - aby wyrazić to inaczej, zastanów się nad tym: jak wytłumaczysz tę metodę nowemu członkowi zespołu? Z pewnością ma pewną strukturę, którą można streścić w stylu „no cóż, zaczyna się od A, potem B, potem C, itd.”. Posiadanie krótkiej metody „przeglądu” wywołującej inne metody czyni tę strukturę oczywistą. Niezwykle rzadko można znaleźć 350 linii kodu, które nie przynoszą korzyści; ludzki mózg nie powinien zajmować się listami o długości setek przedmiotów, grupujemy je.

Wielokrotnego użytku: Długie metody mają zwykle niską spójność - często robią więcej niż jedną rzecz. Niska kohezja jest wrogiem ponownego użycia; jeśli połączysz wiele zadań w jedną metodę, zostanie ona ponownie wykorzystana w mniejszej liczbie miejsc niż powinna.

Testowalność i spójność: Wspomniałem o cykliczności złożoności w powyższym komentarzu - to całkiem niezła miara stopnia złożoności twojej metody. Reprezentuje dolną granicę liczby unikalnych ścieżek w twoim kodzie, w zależności od danych wejściowych (edycja: poprawiona zgodnie z komentarzem MichaelT). Oznacza to również, że aby poprawnie przetestować metodę, musisz mieć co najmniej tyle przypadków testowych, ile cykliczna liczba złożoności. Niestety, kiedy tworzysz fragmenty kodu, które tak naprawdę nie są od siebie zależne, nie ma sposobu, aby być pewnym tego braku zależności, a złożoność ma tendencję do mnożenia się. Możesz myśleć o tym środku jako o wskazaniu liczby różnych rzeczy, które próbujesz zrobić. Jeśli jest za wysoko, czas podzielić się i podbić.

Refaktoryzacja i struktura: Długie metody są często oznaką braku jakiejś struktury w kodzie. Często deweloper nie mógł ustalić, jakie są podobieństwa między różnymi częściami tej metody i gdzie można by narysować linię między nimi. Uświadomienie sobie, że długa metoda jest problemem, i próba podzielenia jej na mniejsze metody jest pierwszym krokiem na dłuższej drodze do faktycznego zidentyfikowania lepszej struktury dla całości. Być może musisz stworzyć klasę lub dwie; w końcu niekoniecznie będzie to bardziej skomplikowane!

Myślę również, że w tym przypadku usprawiedliwieniem posiadania długiej metody jest „... niektóre zmienne zadeklarowane poza główną pętlą, a następnie zmienione podczas pętli”. Nie jestem ekspertem w Pythonie, ale jestem całkiem pewien, że ten problem można w prosty sposób rozwiązać za pomocą jakiejś formy przekazywania przez referencję.


13
+1 za zrzeczenie się zawsze części pytania i skupienie się na mięsie: czy długie metody są złe. Myślę, że OP szuka uzasadnienia, jak gdyby jego scenariusz był skrajnym przypadkiem, chociaż zwykle, gdy słyszę, jak ludzie wyjaśniają swoje złe praktyki jako konieczne w przypadku rzadkiego scenariusza, to tylko dlatego, że nie bardzo starali się zastosować dobre praktyki. Niezwykłe scenariusze są naprawdę bardzo rzadkie, długie metody są niestety dość powszechne.
Jimmy Hoffa

2
OK, patrząc na powyższą listę: czytelność powiedziałbym z wcześniejszych doświadczeń, że metoda jest dłuższa, zawiera dużo komentarzy i jest dobrze sformatowana, zamiast przeskakiwać kod z metody do metody. Jest to prawdopodobnie dość subiektywne. Nie oczekuję, że części kodu zostaną ponownie wykorzystane. Większość ponownego użycia kodu jest obecnie uzyskiwana z dziedziczenia.
wobbily_col

1
@wobbily_col BTW, jeśli szukasz dobrze napisanego tekstu, który uzasadnia stosowanie krótszych metod, przeczytaj kilka pierwszych rozdziałów czystego kodu , który całkiem dobrze wyjaśnia.
Daniel B

5
@wobbily_col Mówisz, że zbytnie przeskakiwanie, aby zrozumieć kod za pomocą wielu metod, jest mylące, myślę, że brak tutaj jest znaczenia nazewnictwa. Jeśli metoda jest dobrze nazwana, nie trzeba na nią patrzeć, aby dowiedzieć się, co ona robi, metoda wywołująca powinna być całkowicie zrozumiała bez jakiejkolwiek podstawowej wiedzy o metodach, które wywołuje, na przykład czy kiedykolwiek używałeś someVar.toString()i czułeś musiałeś zobaczyć kod toString, aby wiedzieć, co on robi? Po prostu czytasz tuż obok niego z powodu dobrego nazewnictwa metod.
Jimmy Hoffa

4
Na marginesie, posiadanie metody, która wymaga n parametrów, jest również zapachem kodu i wskazuje, że metoda może zrobić więcej niż jedną rzecz. To samo dotyczy metody trudnej do nazwania. A jeśli naprawdę potrzebuje wszystkich tych parametrów, zwykle są one częścią większej koncepcji, która może i powinna być zamknięta w swojej klasie. Oczywiście możemy wymyślić przykład, w którym lepiej nie używać tej reguły - mam na myśli to, że jeśli widzisz taką metodę, dokładnie ją zbadaj, prawdopodobnie jest to w jakiś sposób złe.
KL

28

Długie metody są zawsze złe, ale czasami są lepsze niż alternatywy.


5
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 twierdzenie typu „Długie metody nigdy nie są złe, ale czasami gorsze niż alternatywy”. , w jaki sposób ta odpowiedź pomoże czytelnikowi wybrać dwie przeciwstawne opinie? Rozważmy zmienił ing go w lepszej formie
gnat

9

Długie metody to zapach kodu . Zazwyczaj wskazują, że coś jest nie tak, ale nie jest to reguła trudna i szybka. Zazwyczaj przypadki, w których są uzasadnione, obejmują wiele państwowych i dość skomplikowanych reguł biznesowych (jak ustalono).

Co do drugiego pytania, często pomocne jest podzielenie fragmentów logiki na osobne metody, nawet jeśli są one wywoływane tylko raz. Ułatwia to dostrzeżenie logiki wysokiego poziomu i może sprawić, że obsługa wyjątków będzie trochę czystsza. Tak długo, jak nie musisz przekazywać dwudziestu parametrów, aby reprezentować stan przetwarzania!


7

Długie metody nie zawsze są złe. Zazwyczaj są znakiem, że może występować problem.

W systemie, nad którym pracuję, mamy około pół tuzina metod o długości ponad 10 000 linii. Jedna ma obecnie 54 830 linii. I jest OK.

Te absurdalnie długie funkcje są bardzo proste i są generowane automatycznie. Ten wielki potwór o długości 54 830 linii zawiera codzienne dane dotyczące ruchu biegunowego od 1 stycznia 1962 r. Do 10 stycznia 2012 r. (Nasze ostatnie wydanie). Zwalniamy również procedurę, dzięki której nasi użytkownicy mogą aktualizować ten automatycznie wygenerowany plik. Ten plik źródłowy zawiera dane dotyczące ruchu biegunowego z http://data.iers.org/products/214/14443/orig/eopc04_08_IAU2000.62-now , automatycznie przetłumaczone na C ++.

Czytanie tej witryny internetowej w locie nie jest możliwe w bezpiecznej instalacji. Nie ma połączenia ze światem zewnętrznym. Pobranie strony internetowej jako lokalna kopia i parsowanie w C ++ również nie jest możliwe; parsowanie jest powolne i musi być szybkie. Pobieranie, automatyczne tłumaczenie na C ++ i kompilacja: teraz masz coś, co jest szybkie. (Po prostu nie kompiluj go zoptymalizowanego. To niesamowite, ile czasu zajmuje kompilator optymalizacyjny do skompilowania 50 000 wierszy bardzo prostego kodu linii prostej. Kompilacja tego zoptymalizowanego pliku zajmuje ponad pół godziny. A optymalizacja nie osiąga absolutnie nic Nie ma nic do optymalizacji. Jest to prosty kod prosty, jedna instrukcja przypisania po drugiej.)


6
„jedna instrukcja przypisania po drugiej” ... nazwałbym to „plikiem danych”. Dlaczego to kod?
Joachim Sauer

3
@ JoachimSauer - Ponieważ parsowanie dużego pliku danych w czasie wykonywania w konfiguracji Monte Carlo jest złym pomysłem. Bardzo, bardzo zły pomysł.
David Hammen

4
@DavidHammen: następnie zrób to podczas inicjalizacji, tak jak zmuszasz swój linker / moduł ładujący. Lub zapisz plik danych jako strukturę C w pliku nagłówkowym zamiast kodu C. Przynajmniej wtedy moduł ładujący załaduje się jako blok danych.
Javier

3
@Javier: Nawet w momencie inicjalizacji może to być bardzo zły pomysł, przynajmniej w konfiguracji Monte Carlo. Symulacja, której zainicjowanie zajmuje kilka minut, ale uruchomienie jej zajmuje tylko kilka sekund, w przeciwieństwie do dziesiątek tysięcy uruchomień w ciągu jednej nocy. Zmiana kluczowych zadań związanych z czasem inicjowania w celu skompilowania zadań czasowych rozwiązuje ten problem. Wypróbowaliśmy wiele technik, w tym podejście oparte na skompilowanej strukturze danych. To po prostu nie działa lub w niektórych przypadkach byłoby bardzo trudne do wykonania (np. Ogromny model grawitacji). Metoda kodu liniowego jest łatwa do automatycznego generowania, łatwa do weryfikacji. To po prostu brzydki kod.
David Hammen

7
+1 interesująca historia. wygenerowany kod nie jest tak naprawdę kodem źródłowym, więc można argumentować, że to nie „łamie” reguły. jeden zakłada generator kodu Sam miał ładne krótkie metody
jk.

7

Powiedzmy, że istnieją dobre i złe sposoby na przełamanie długiej metody. Konieczność „[trzymania] najbardziej zewnętrznej metody w twojej głowie” jest znakiem, że nie rozbijasz jej w najbardziej optymalny sposób, lub że twoje podmetody są źle nazwane. Teoretycznie zdarzają się przypadki, w których długa metoda jest lepsza. W praktyce jest to niezwykle rzadkie. Jeśli nie możesz dowiedzieć się, jak sprawić, by krótsza metoda była czytelna, poproś kogoś, aby przejrzał twój kod i poprosił go o pomysły dotyczące skrócenia metod.

Jeśli chodzi o wiele pętli powodujących przypuszczalne pogorszenie wydajności, nie ma sposobu, aby się tego dowiedzieć bez pomiaru. Wiele mniejszych pętli może być znacznie szybszych, jeśli oznacza to, że wszystko, czego potrzebuje, może pozostać w pamięci podręcznej. Nawet jeśli wystąpi uderzenie wydajności, zwykle nie ma znaczenia na korzyść czytelności.

Powiem, że często długie metody są łatwiejsze do napisania , mimo że trudniejsze do odczytania . Dlatego rozmnażają się, chociaż nikt ich nie lubi. Nie ma nic złego w planowaniu od samego początku do refaktoryzacji, zanim go odprawisz.


1
„Nie ma nic złego w planowaniu od początku do refaktoryzacji, zanim się go zamelduje”. +1 za to. Większość IDE ma obecnie narzędzia refaktoryzacyjne, które również sprawiają, że jest to niezwykle łatwe. Ale istnieje metoda odwrotna, w której przekazujesz rzeczy nieistniejącym funkcjom, a później przechodzisz i wypełniasz metody, ale nigdy nie byłem w stanie kodować w ten sposób, tak jak próbowałem.
Tjaart

+1 za „Konieczność” [utrzymania] najbardziej zewnętrznej metody w twojej głowie ”oznacza, że ​​nie rozbijasz jej w najbardziej optymalny sposób”
Michael Shaw

4

Długie metody mogą być bardziej obliczeniowe i zajmować mało miejsca, łatwiej jest dostrzec logikę i łatwiej je debugować. Jednak te zasady obowiązują tylko wtedy, gdy tylko jeden programista dotknie tego kodu. Kod będzie trudny do rozszerzenia, jeśli nie jest atomowy, w zasadzie następna osoba będzie musiała zacząć od zera, a następnie debugowanie i testowanie zajmie to na zawsze, ponieważ nie używa żadnego znanego dobrego kodu.


34
Zawsze zaangażowanych jest co najmniej dwóch programistów: „ty” i „ty, za trzy tygodnie”.
Joachim Sauer

3

Jest coś, co nazywamy rozkładem funkcjonalnym, co oznacza rozbicie dłuższych metod na mniejsze, o ile to możliwe. Jak już wspomniałeś, że twoja metoda obejmuje sortowanie / filtrowanie, lepiej mieć oddzielne metody lub funkcje do tych zadań.

Dokładnie, twoja metoda powinna koncentrować się tylko na wykonaniu 1 zadania.

A jeśli trzeba wywołać inną metodę z jakiegoś powodu, zrób to inaczej, kontynuując tę, którą już piszesz. Również w przypadku czytelności możesz dodawać komentarze. Konwencjonalnie programiści używają komentarzy wieloliniowych (/ ** / w C, C ++, C # i Java) do opisów metod i używają komentarzy jednowierszowych (// w C, C ++, C # i Java). Dostępne są również dobre narzędzia do dokumentacji dla lepszej czytelności kodu (np. JavaDoc ). Możesz również przeglądać komentarze oparte na XML, jeśli jesteś programistą .Net.

Pętle mają wpływ na wydajność programu i mogą powodować narzut aplikacji, jeśli nie są właściwie używane. Chodzi o to, aby zaprojektować algorytm w taki sposób, aby jak najmniej wykorzystywać zagnieżdżone pętle.


Znany również jako „Funkcja powinna zrobić JEDEN RZECZ”.
lorddev

3

Długie funkcje są w porządku. Ale zależy to od kontekstu, czy naprawdę potrzebujesz, czy nie. Na przykład niektóre z najlepszych algorytmów wyrażane są najlepiej, gdy jest to kawałek. Z drugiej strony, duży procent procedur w programach obiektowych będą procedurami akcesorów, które będą bardzo krótkie. Niektóre z długich procedur przetwarzania, które mają długie skrzynki przełączników, jeśli warunki można zoptymalizować metodami opartymi na tabeli.

W Code Complete 2 znajduje się doskonała krótka dyskusja na temat długości procedur.

Teoretycznie najlepsza maksymalna długość 497 jest często opisywana jako jedna lub dwie strony listy programów, od 66 do 132 linii. Współczesne programy zawierają mieszankę bardzo krótkich procedur z kilkoma dłuższymi.

Dekady dowodów wskazują, że procedury o takiej długości nie są bardziej podatne na błędy niż procedury krótsze. Niech kwestie takie jak głębokość zagnieżdżenia, liczba zmiennych i inne kwestie związane ze złożonością dyktują 535 długość procedury, a nie nakładają długość

Jeśli chcesz pisać procedury dłuższe niż około 200 linii, bądź ostrożny. Żadne z badań, które zgłosiło obniżenie kosztów, zmniejszenie wskaźników błędów lub oba przy większych procedurach rozróżnianych wśród rozmiarów większych niż 200 wierszy, a po przekroczeniu 200 wierszy kodu z pewnością dojdzie do górnej granicy zrozumiałości. 536 ograniczenie per se.


2

Kolejny głos, że prawie zawsze jest źle. Znajduję jednak dwa podstawowe przypadki, w których jest to właściwa odpowiedź:

1) Metoda, która w zasadzie wywołuje wiele innych metod i sama nie działa. Masz proces, który wymaga 50 kroków, a otrzymasz metodę z 50 wywołaniami. Zwykle nic nie można zyskać, próbując to zerwać.

2) Dyspozytorzy. Projekt OOP pozbył się większości takich metod, ale przychodzące źródła danych są ze swej natury tylko danymi i dlatego nie mogą być zgodne z zasadami OOP. Nie jest niczym niezwykłym, że w kodzie, który obsługuje dane, istnieje jakaś procedura rozsyłająca.

Powiedziałbym również, że nie należy nawet zastanawiać się nad tym pytaniem w przypadku rzeczy generowanych automatycznie. Nikt nie próbuje zrozumieć, co robi kod wygenerowany automatycznie, nie ma znaczenia, czy człowiek jest łatwy do zrozumienia.


1
50-etapowy proces można prawdopodobnie streścić w kilka segmentów. Kroki 1–9 to sprawdzanie parametrów, więc utwórz nową metodę o nazwie sprawdzanie parametrów. (Jestem pewien, że istnieją pewne przykłady, w których nie jest to możliwe. Byłbym zainteresowany ich zobaczeniem).
sixtyfootersdude

@sixtyfootersdude: Jasne, że możesz to zerwać. Nie powiedziałem, że to niemożliwe, powiedziałem, że nie ma nic do zyskania przez zerwanie. Chociaż nie było to 50 kroków, uderzyłem w coś takiego: stworzenie świata gry. # 1 stworzył pusty świat, # 2 stworzył teren, a potem cały szereg kroków po tym masował go w ten czy inny sposób.
Loren Pechtel

& sixtyfootersdude: To niesamowite, skąd znasz kod, którego nigdy nie widziałeś i jak go poprawić.
gnasher729

2

Chciałem odnieść się do przykładu, który podałeś:

Na przykład mam kilka widoków Django, które przetwarzają obiekty przed wysłaniem ich do widoku, przy czym długa metoda to 350 linii kodu. Mam napisany kod, który zajmuje się parametrami - sortowanie / filtrowanie zestawu zapytań, a następnie krok po kroku wykonuje pewne przetwarzanie na obiektach, które zwróciło moje zapytanie.

W mojej firmie nasz największy projekt opiera się na Django i mamy również funkcje widoku długiego (wiele z nich ma ponad 350 linii). Twierdziłbym, że nasze nie muszą trwać tak długo, a nas krzywdzą.

Te funkcje widoku wykonują wiele luźno powiązanych prac, które należy wyodrębnić do modelu, klas pomocniczych lub funkcji pomocniczych. Ponadto ostatecznie wykorzystujemy widoki do robienia różnych rzeczy, które zamiast tego należy podzielić na bardziej spójne widoki.

Podejrzewam, że twoje poglądy mają podobne cechy. W moim przypadku wiem, że powoduje to problemy i pracuję nad wprowadzeniem zmian. Jeśli nie zgadzasz się, że powoduje to problemy, nie musisz tego naprawiać.


2

Nie wiem, czy ktoś już o tym wspomniał, ale jednym z powodów, dla których długie metody są złe, jest to, że zwykle obejmują one kilka różnych poziomów abstrakcji. Masz zmienne pętlowe i wszystko, co się dzieje. Rozważ fikcyjną funkcję:

function nextSlide() {
  var content = getNextSlideContent();
  hideCurrentSlide();
  var newSlide = createSlide(content);
  setNextSlide(newSlide);
  showNextSlide();
}

Gdybyś wykonywał wszystkie animacje, obliczenia, dostęp do danych itp. W tej funkcji, byłby to bałagan. Funkcja nextSlide () utrzymuje spójną warstwę abstrakcji (system stanu slajdów) i ignoruje inne. Dzięki temu kod jest czytelny.

Jeśli musisz stale wchodzić w mniejsze metody, aby zobaczyć, co robią, ćwiczenie dzielenia funkcji nie powiodło się. To, że czytany kod nie robi oczywistych rzeczy w metodach potomnych, nie oznacza, że ​​metody potomne są złym pomysłem, tylko że zostały wykonane nieprawidłowo.

Kiedy tworzę metody, zwykle dzielę je na mniejsze metody jako rodzaj strategii dzielenia i podbijania. Metoda taka jak

   if (hasMoreRecords()) { ... }

jest z pewnością bardziej czytelny niż

if (file.isOpen() && i < recordLimit && currentRecord != null) { ... } 

Dobrze?

Zgadzam się, że stwierdzenia absolutne są złe, a także zgadzam się, że zwykle długa metoda jest zła.


1

Prawdziwa historia. Kiedyś spotkałem metodę, która miała ponad dwa tysiące linii. Metoda miała regiony, które opisywały jej działanie w tych regionach. Po przeczytaniu regionu postanowiłem wykonać automatyczną metodę wyodrębniania, nadając mu nazwę zgodnie z nazwą regionu. zanim skończyłem, metoda była niczym innym jak 40 wywołaniami metod o długości około pięćdziesięciu wierszy każda i wszystko działało tak samo.

To, co jest zbyt duże, jest subiektywne. Czasami metody nie da się rozbić dalej niż obecnie. To jak pisanie książki. Większość ludzi zgadza się, że długie akapity powinny być zwykle dzielone. Ale czasami jest tylko jeden pomysł, a jego podział powoduje więcej zamieszania niż spowodowałaby długość tego akapitu.


0

Celem metody jest zmniejszenie niedomykalności kodu. Metoda powinna mieć określoną funkcję, za którą jest odpowiedzialna. Jeśli skończysz przerabianie kodu w wielu miejscach, istnieje ryzyko, że kod będzie miał nieoczekiwane wyniki, jeśli specyfikacje oprogramowania są dostosowane do zmiany.

Metoda zawierająca 350 wierszy sugerowałaby, że wiele zadań, które wykonuje, są replikowane gdzie indziej, ponieważ nie jest wymagane tak duże ilości kodu, aby wykonać wyspecjalizowane zadanie.


Pomóż zredukować kod co ?
Avner Shahar-Kashtan

@ AvnerShahar-Kashtan, prawdopodobnie oznacza „duplikację” :-)
Péter Török

0

To nie tak naprawdę długie metody, które są złą praktyką, ale raczej pozostawianie ich tak, że to jest złe.

Mam na myśli faktyczny fakt refaktoryzacji próbki z:

varaible_1 = 0
variable_2 = 0
for object in queryset :
     if object.condition_condition_a and variable_2 > 0 :
     variable 1+= 1
     .....
     ...    
     . 
      more conditions to alter the variables

return queryset, and context 

do

Status status = new Status();
status.variable1 = 0;
status.variable2 = 0;
for object in queryset :
     if object.condition_condition_a and status.variable2 > 0 :
     status.variable1 += 1
     .....
     ...    
     . 
      more conditions to alter the variables (status)

return queryset, and context 

a następnie do

class Status {
    variable1 = 0;
    variable2 = 0;

    void update(object) {
        if object.condition_condition_a and variable2 > 0 {
            variable1 += 1
        }
    }
};

Status status = new Status();
for object in queryset :
     status.update(object);
     .....
     ...    
     . 
      more conditions to alter the variables (status)

return queryset, and context 

jesteś teraz na dobrej drodze do nie tylko znacznie krótszej metody, ale także o wiele bardziej użytecznej i zrozumiałej.


0

Myślę, że fakt, że metoda jest bardzo długa, jest czymś, co warto sprawdzić, ale zdecydowanie nie jest to natychmiastowy anty-wzór. Wielką metodą, której należy szukać w ogromnych metodach, jest dużo zagnieżdżania. Jeśli masz

foreach(var x in y)
{
    //do ALL the things
    //....
}

a ciało pętli nie jest bardzo zlokalizowane (tzn. możesz wysłać mniej niż 4 parametry), prawdopodobnie lepiej jest przekonwertować ją na:

foreach(var x in y)
{
    DoAllTheThings(x);
}
...
void DoAllTheThings(object x)
{
    //do ALL the things
    //....
}

To z kolei może znacznie skrócić długość funkcji. Pamiętaj również, aby poszukać duplikatu kodu w funkcji i przenieść go do osobnej funkcji

Wreszcie, niektóre metody są po prostu długie i skomplikowane i nie można nic zrobić. Niektóre problemy wymagają rozwiązań, które nie są łatwe do kodowania. Na przykład parsowanie w oparciu o gramatykę o dużej złożoności może stworzyć naprawdę długie metody, na które tak naprawdę nie można wiele zrobić bez pogorszenia.


0

Prawda jest taka, że ​​to zależy. Jak wspomniano, jeśli kod nie rozdziela problemów i próbuje zrobić wszystko za pomocą jednej metody, to jest to problem. Rozdzielenie kodu na wiele modułów ułatwia odczytywanie kodu, a także pisanie kodu (przez wielu programistów). Dobrym pomysłem jest rozpoczęcie od jednego modułu (klasy) na plik źródłowy.

Po drugie, jeśli chodzi o funkcje / procedury:

void setDataValueAndCheckForRange(Data *data) {/*code*/} 

jest dobrą metodą, jeśli sprawdza zakres tylko „Dane”. Jest to ZŁA metoda, gdy ten sam zakres dotyczy wielu funkcji (przykład złego kodu):

void setDataValueAndCheckForRange(Data *data){ /*code */}
void addDataValuesAndCheckForRange(Data *result, Data *d1, Data *d2){ /*code*/}
void subDataValuesAndCheckForRange(Data *result, Data *d1, Data *d2){ /*code*/}
void mulDataValuesAndCheckForRange(Data *result, Data *d1, Data *d2){ /*code*/}

Należy to zmienić na:

bool isWithinRange(Data *d){ /*code*/ }
void setDataValue(Data *d) {/*code*/ if(isWithinRange(d)){/*continue*/}else{/*warn/abort*/} 
void addDataValue(Data *d, Data *d1, Data *d2) {/*code*/ if(isWithinRange(d)){/*continue*/}else{/*warn/abort*/} 
void subDataValue(Data *d, Data *d1, Data *d2) {/*code*/ if(isWithinRange(d)){/*continue*/}else{/*warn/abort*/} 
void mulDataValue(Data *d, Data *d1, Data *d2) {/*code*/ if(isWithinRange(d)){/*continue*/}else{/*warn/abort*/} 

UŻYWAJ kodu w jak największym stopniu. Jest to możliwe, gdy każda funkcja Twojego programu jest PROSTA (niekoniecznie łatwa).

QUOTE: MOUNTAIN składa się z drobnych ziaren ziemi. Ocean składa się z drobnych kropel wody. (- Sivananda)


0

Długie metody mają tendencję do „złych” w imperatywnych językach, które sprzyjają stwierdzeniom, skutkom ubocznym i zmienności, właśnie dlatego, że cechy te zwiększają złożoność, a tym samym błędy.

W funkcjonalnych językach programowania, które sprzyjają wyrażeniom, czystości i niezmienności, jest mniej powodów do niepokoju.

Zarówno w językach funkcjonalnych, jak i imperatywnych zawsze najlepiej jest rozdzielić fragmenty kodu wielokrotnego użytku we wspólne procedury najwyższego poziomu, ale w językach funkcjonalnych, które obsługują zakres leksykalny z zagnieżdżonymi funkcjami itp., Faktycznie jest lepsza enkapsulacja, aby ukryć podprogramy w górnej części -level funkcja (metoda) niż rozbicie ich na inne funkcje najwyższego poziomu.


Jednak w językach funkcjonalnych długie metody są znacznie mniej powszechne.
itsbruce
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.