Techniki minimalizowania liczby argumentów funkcji

13

W czystym kodzie napisano, że „idealna liczba argumentów dla funkcji wynosi zero”. Powody, dla których zostały wyjaśnione i mają sens. Poszukuję technik refaktoryzacji metod z 4 lub więcej argumentami, aby rozwiązać ten problem.

Jednym ze sposobów jest wyodrębnienie argumentów do nowej klasy, ale z pewnością doprowadziłoby to do eksplozji klas? Czy te klasy prawdopodobnie kończą się nazwami, które naruszają niektóre reguły nazewnictwa (kończące się na „Dane” lub „Informacje” itp.)?

Inną techniką jest przekształcenie zmiennych używanych przez wiele funkcji w zmienne prywatne, aby uniknąć ich przekazania, ale rozszerza to zakres zmiennej, być może tak, że jest ona otwarta na funkcje, które w rzeczywistości jej nie potrzebują.

Po prostu szukam sposobów na zminimalizowanie argumentów funkcji, po zaakceptowaniu powodów, dla których warto to zrobić.

Neil Barnwell
źródło
21
Tbh W ogóle nie zgadzam się z czystym kodem. Jeśli liczba argumentów funkcji wynosi zero, oznacza to, że funkcja ma skutki uboczne i prawdopodobnie gdzieś się zmienia. Chociaż zgadzam się, że mniej niż 4 argumenty mogą być dobrą regułą - wolałbym mieć funkcję z 8 argumentami, która jest statyczna i nie ma efektów ubocznych niż funkcja niestatyczna z zerowymi argumentami, która zmienia stan i ma skutki uboczne .
wasatz
4
W czystym kodzie napisano, że„ idealna liczba argumentów dla funkcji wynosi zero ”. „ Naprawdę? To takie złe! Idealna liczba parametrów to jeden, a zwracana wartość jest deterministycznie wyprowadzana z tego jednego parametru. W praktyce liczba parametrów nie ma jednak większego znaczenia; ważne jest to, że w miarę możliwości funkcja powinna być czysta (tj. oblicza wartość zwrotną tylko na podstawie parametrów bez skutków ubocznych).
David Arno,
2
Cóż, w dalszej części książki zaznaczono, że skutki uboczne nie są pożądane ...
Neil Barnwell
2
Możliwy duplikat strategii zawijania parametrów
komnata

Odpowiedzi:

16

Najważniejszą rzeczą do zapamiętania jest to, że są to wytyczne, a nie zasady.

Są przypadki, w których metoda musi po prostu wziąć argument. Pomyśl +na przykład o metodzie liczb. Lub addmetoda kolekcji.

W rzeczywistości można nawet argumentować, że to, co oznacza dodanie dwóch liczb, zależy od kontekstu, np. W ℤ 3 + 3 == 6, ale w ℤ | 5 3 + 3 == 2 , więc tak naprawdę operator dodawania powinien być metodą na obiekcie kontekstu, który przyjmuje dwa argumenty zamiast metoda na liczbach, która przyjmuje jeden argument.

Podobnie metoda porównywania dwóch obiektów musi być albo metodą jednego obiektu przyjmującego drugi jako argument, albo metodą kontekstu przyjmującą dwa obiekty jako argumenty, więc po prostu nie ma sensu mieć metody porównawczej z mniej niż jeden argument.

To powiedziawszy, istnieje kilka rzeczy, które można zrobić, aby zmniejszyć liczbę argumentów dla metody:

  • Zmniejsz samą metodę : może, jeśli metoda wymaga tak wielu argumentów, robi zbyt wiele?
  • Brakująca abstrakcja : jeśli argumenty są ściśle ze sobą powiązane, może należą do siebie i brakuje Ci abstrakcji? (Przykład kanonicznej książki tekstowej: zamiast dwóch współrzędnych podaj Pointobiekt lub zamiast nazwy użytkownika i adresu e-mail podaj IdCardobiekt.)
  • Stan obiektu : jeśli argument jest potrzebny na wiele sposobów, być może powinien on być częścią stanu obiektu. Jeśli jest to potrzebne tylko przez niektóre metody, ale nie przez inne, być może obiekt robi za dużo i powinien być naprawdę dwoma obiektami.

Jednym ze sposobów jest wyodrębnienie argumentów do nowej klasy, ale z pewnością doprowadziłoby to do eksplozji klas?

Jeśli twój model domeny ma wiele różnych rodzajów rzeczy, wtedy twój kod skończy z wieloma różnymi rodzajami obiektów. Nie ma w tym nic złego.

Czy te klasy prawdopodobnie kończą się nazwami, które naruszają niektóre reguły nazewnictwa (kończące się na „Dane” lub „Informacje” itp.)?

Jeśli nie możesz znaleźć właściwej nazwy, być może zgrupowałeś za dużo argumentów lub za mało. Więc albo masz tylko fragment klasy, albo masz więcej niż jedną klasę.

Inną techniką jest przekształcenie zmiennych używanych przez wiele funkcji w zmienne prywatne, aby uniknąć ich przekazania, ale rozszerza to zakres zmiennej, być może tak, że jest ona otwarta na funkcje, które w rzeczywistości jej nie potrzebują.

Jeśli masz grupę metod, z których wszystkie działają na tych samych argumentach, i inną grupę metod, które nie, być może należą one do różnych klas.

Zauważ, jak często używałem słowa „może”? Dlatego są to wytyczne, a nie zasady. Może Twoja metoda z 4 parametrami jest w porządku!

Jörg W Mittag
źródło
7
@ BrunoSchäpper: Pewnie: (1) „Zmniejsz samą metodę: może, jeśli metoda wymaga tak wielu argumentów, robi zbyt wiele? ”. Liczba parametrów jest złym sprawdzianem tego. Opcjonalne parametry logiczne / logiczne i wiele linii kodu są silnymi wskaźnikami metody, która robi zbyt wiele. Wiele parametrów jest co najwyżej słabym. (2) „ Stan obiektu: jeśli argument jest potrzebny na wiele sposobów, być może powinien on być częścią stanu obiektu ”. Nie, nie i trzy razy, nie. Minimalizuj stan obiektu; brak parametrów funkcji. Jeśli to możliwe, przekaż wartość do wszystkich metod za pomocą parametrów, aby uniknąć stanu obiektu.
David Arno,
Podany przykład dodania jest całkowicie błędny. addFunkcją liczb naturalnych i addfunkcja pierścienia całkowite mod n są dwie różne funkcje działania na dwóch różnych typów. Nie rozumiem też, co rozumiesz przez „kontekst”.
ogrodnik
Thx @DavidArno. 1) zgodził się, co nie jest silnym wskaźnikiem samym w sobie. Ale i tak dobry. Często widzę kilka metod, z kilkoma obiektami przekazywanymi dookoła. Brak stanu obiektu. To dobrze, ale 2) lepsza opcja IMHO refaktoryzuje te metody, przenosi stan niejawny do nowej klasy, która przyjmuje wszystkie te parametry jako jawne argumenty. W efekcie powstaje jedna publiczna metoda zerowego argumentu i wiele wewnętrznych metod argumentu zero-to-one. Stan nie jest publiczny, globalny ani nawet utrzymywany przy życiu przez długi czas, ale kod jest o wiele czystszy.
Bruno Schäpper
6

Zauważ, że zero argumentów nie oznacza skutków ubocznych, ponieważ twój obiekt jest niejawnym argumentem. Zobacz , na przykład, ile metod zerowej arity ma niezmienna lista Scali .

Jedną przydatną technikę nazywam techniką „ogniskowania obiektywu”. Po ustawieniu ostrości obiektywu aparatu łatwiej jest zobaczyć prawdziwy punkt ostrości, jeśli przesuniesz go za daleko, a następnie cofniesz do właściwego punktu. To samo dotyczy refaktoryzacji oprogramowania.

Zwłaszcza jeśli używasz rozproszonej kontroli wersji, zmiany oprogramowania są łatwe do eksperymentowania, sprawdź, czy podoba ci się ich wygląd, i wycofaj się, jeśli nie, ale z jakiegoś powodu ludzie często niechętnie to robią.

W kontekście twojego obecnego pytania oznacza to napisanie wersji zerowej lub jednej argumentacji, z kilkoma funkcjami najpierw rozdzielnymi, wtedy stosunkowo łatwo jest zobaczyć, które funkcje wymagają połączenia w celu zapewnienia czytelności.

Zauważ, że autor jest również wielkim zwolennikiem rozwoju opartego na testach, który na początku produkuje funkcje o niskiej wydajności, ponieważ zaczynasz od trywialnych przypadków testowych.

Karl Bielefeldt
źródło
1
Podobnie jak analogia do „ogniskowania obiektywu” - szczególnie przy refaktoryzacji ważne jest, aby używać obiektywu szerokokątnego zamiast zbliżeniowego. A
analiza
0

Podejście, które po prostu (i naiwnie - a nawet powinienem powiedzieć ślepo ) ma na celu ograniczenie liczby argumentów do funkcji, jest prawdopodobnie błędne. Nie ma absolutnie nic złego w funkcjach z dużą liczbą argumentów. Jeśli są one wymagane przez logikę, no cóż, są one wymagane ... Długa lista parametrów wcale mnie nie martwi - pod warunkiem, że jest odpowiednio sformatowana i skomentowana dla czytelności.

W przypadku, gdy wszystkie lub podzbiór argumentów należą do jednego unikalnego logicznego bytu i są zwykle przekazywane w grupach w całym programie, może być sensowne pogrupowanie ich w jakiś kontener - zazwyczaj obiekt strukturalny lub inny. Typowymi przykładami mogą być typy danych wiadomości lub zdarzeń .

Możesz łatwo przesadzić z tym podejściem - gdy okaże się, że pakowanie i rozpakowywanie rzeczy do iz takich pojemników transportowych generuje więcej kosztów ogólnych niż poprawia czytelność, prawdopodobnie posunąłeś się za daleko.

OTOH, duże listy parametrów mogą być znakiem, że twój program może nie mieć odpowiedniej struktury - być może funkcja, która wymaga tak dużej liczby parametrów, po prostu próbuje zrobić zbyt wiele i powinna zostać podzielona na kilka mniejszych funkcji. Wolę zacząć od martwienia się o # parametrów.

tofro
źródło
5
Oczywiście ślepe zmniejszanie liczby argumentów jest błędne. Nie zgadzam się jednak z twierdzeniem, że „funkcje mają wiele argumentów”. . Moim zdaniem, gdy trafisz na funkcję z dużą liczbą argumentów, w 99,9% wszystkich przypadków istnieje sposób na poprawienie struktury kodu w sposób umyślny, co (również) zmniejsza liczbę argumentów funkcji.
Doc Brown
@DocBrown Dlatego jest ten ostatni akapit i zalecenie, aby tam zacząć ... I jeszcze jeden: Prawdopodobnie nigdy nie próbowałeś programować przeciwko interfejsowi API MS Windows;)
tofro
„być może funkcja, która wymaga tak dużej liczby parametrów, próbuje po prostu zrobić zbyt wiele i powinna zostać podzielona na kilka mniejszych funkcji.” Całkowicie się zgadzam, chociaż w praktyce nie kończysz z inną funkcją wyższą niż stos, która wywołuje te kilka mniejszych funkcji? Następnie możesz zmienić je w obiekt, ale ten obiekt będzie miał ctor. Możesz użyć budowniczego bla bla bla. Chodzi o to, że jest to nieskończona regresja - gdzieś istnieje szereg wartości, które są potrzebne, aby oprogramowanie wykonało swoją pracę i muszą jakoś dostać się do tych funkcji.
Neil Barnwell,
1
@NeilBarnwell W idealnym przypadku (wartym refaktoryzacji) możesz być w stanie podzielić funkcję wymagającą 10 argumentów na trzy, które wymagają 3-4 argumentów każdy. W niezbyt idealnym przypadku uzyskujesz trzy funkcje, które wymagają 10 argumentów (lepiej zostaw to w spokoju). W odniesieniu do argumentu stosu z wyższym stosem: Zgadzam się - Może tak być, ale niekoniecznie - argumenty pochodzą skądś, a pobieranie może być również gdzieś w stosie i po prostu musi zostać tam umieszczone - Przebieg ma tendencję do różnic.
tofro
Logika oprogramowania nigdy nie wymaga więcej niż czterech parametrów. Może to tylko kompilator.
doktor
0

Nie ma magicznego sposobu: musisz opanować domenę problemów, aby odkryć odpowiednią architekturę. To jedyny sposób na refaktoryzację: opanowanie domeny problemu. Więcej niż cztery parametry to tylko pewność, że twoja obecna architektura jest wadliwa i zła.

Jedynymi wyjątkami są kompilatory (metaprogramy) i symulacje, w których teoretycznie limit wynosi 8, ale prawdopodobnie tylko 5.

doktor
źródło