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
źródło
Odpowiedzi:
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.
źródło
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ę.
źródło
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.Długie metody są zawsze złe, ale czasami są lepsze niż alternatywy.
źródło
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!
źródło
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.)
źródło
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.
źródło
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.
źródło
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.
źródło
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.
źródło
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.
źródło
Chciałem odnieść się do przykładu, który podałeś:
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ć.
źródło
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ę:
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
jest z pewnością bardziej czytelny niż
Dobrze?
Zgadzam się, że stwierdzenia absolutne są złe, a także zgadzam się, że zwykle długa metoda jest zła.
źródło
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.
źródło
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.
źródło
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:
do
a następnie do
jesteś teraz na dobrej drodze do nie tylko znacznie krótszej metody, ale także o wiele bardziej użytecznej i zrozumiałej.
źródło
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
a ciało pętli nie jest bardzo zlokalizowane (tzn. możesz wysłać mniej niż 4 parametry), prawdopodobnie lepiej jest przekonwertować ją na:
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.
źródło
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:
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):
Należy to zmienić na:
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)
źródło
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.
źródło