Co to jest argument wyjściowy, o którym mowa w Czystym Kodzie Martina?

14

Na stronie 45 Czystego kodu Roberta C. Martina: Podręcznik do spraw zwinnego oprogramowania Martin pisze, że należy unikać argumentów wyjściowych. Mam problem ze zrozumieniem znaczenia „argumentu wyjściowego” i dlaczego należy go unikać.

Przykład Martina dla argumentu wyjściowego appendFooter(s);wywołuje funkcję public void appendFooter(StringBuffer report). Jego poprawa kodu toreport.appendFooter();

Może wynika to z braku kontekstu kodu, ale nie rozumiem, jak używanie argumentów wyjściowych jest uważane za złe kodowanie. Czy ktoś mógłby wyjaśnić tę koncepcję lub podać dodatkowy przykład kodu, aby to zrozumieć?

Czy powyższa zasada byłaby również uważana za przykład nieczystego kodu na podstawie powyższej zasady?

int[] numberArray = {3, 5, 7, 1};
sortArray(numberArray);

Jeśli powyższe jest pogwałceniem zasady Martina, że ​​nie używa argumentów wyjściowych, czy lepiej byłoby mieć obiekt, który ma tablicę jako pole i funkcję, którą można wywołać w celu posortowania tablicy?

ObjectWithArrayField numberArray = new ObjectWithArrayField(3, 5, 7, 1);
numberArray.sort();
WP0987
źródło

Odpowiedzi:

11

Bob Martin po prostu mówi o czytelności .

Problem w tym appendFooterprzykładzie polega na tym, że jeśli znajdziesz linię kodu appendFooter(s)gdzieś w programie, nie jest od razu oczywiste, czy to wywołanie przyjmuje sjako dane wejściowe i gdzieś je dołącza, czy też sjest po prostu przekazywane, aby pobrać wynik tej funkcji. Aby się upewnić, musisz sprawdzić dokumentację funkcji. Jednak takie połączenie report.appendFooter()pozwala uniknąć tego problemu: teraz o wiele bardziej oczywiste jest, co się dzieje.

Zauważ jednak, że Bob Martin nie mówi „nigdy nie używaj argumentów wyjściowych”, mówi „ogólnie, powinieneś tego unikać, ponieważ pomoże ci to utrzymać trochę czystszy kod”. Dlatego nie jest to zasada kultu ładunków, którą należy ślepo przestrzegać.

Sortmetody standardowych tablic i kolekcji są nieco inne. Posiadanie sortmetody członka funkcji każdego standardowego typu danych macierzy miałoby kilka wad z punktu widzenia projektanta języka, na przykład posiadanie metody podobnej Array.sortdo pozwalającej zachować to w standardowej bibliotece poza środowiskiem wykonawczym Java. Ale jeśli utworzysz indywidualny typ kolekcji, który czasem trzeba posortować, dodanie sortjako funkcji członka może być lepszym pomysłem niż umieszczenie go w osobnej klasie.

Doktor Brown
źródło
2
sortArray(numberArray), oczywiście, układa się numberArrayna miejscu. Czy też tworzy kopię numberArray, sortuje kopię i zwraca posortowaną kopię bez żadnych zmian numberArray?
8bittree,
@ 8bittree: to prawda, ale nie o to tu chodzi w dyskusji - sort()metoda kontenera może również działać w miejscu, bez użycia „argumentu wyjściowego”. Zatem tylko dlatego, sortArray(numberArray)że metoda na miejscu nie ma absolutnie żadnego powodu, który uzasadniałby „wyjściową formę argumentu”.
Doc Brown
1
Chodzi mi o to, że nie do końca wiadomo, co sortArray(numberArray)się dzieje. Może być oczywiste, że jeśli nie zwróci tego samego typu, który akceptuje, to musi być na miejscu. Ale bez zobaczenia typu zwracanego lub jeśli typ zwracany pasuje do typu wejściowego, jest niejasny bez patrzenia na definicję.
8bittree,
1
@ 8bittree: Ok, dostałeś mnie, usunąłem z odpowiedzi oświadczenie. Problem, który opisujesz, nie znika jednak przy użyciu funkcji członka - nawet funkcja członka „sortuj” może zachowywać się w ten sposób.
Doc Brown
11

Jest to kwestia użycia nieoczekiwanego mechanizmu do zwracania wartości z funkcji, co zwykle wynika z nadmiernego wykonywania funkcji lub niewłaściwej odpowiedzialności. Zdecydowanie najlepszym sposobem na przekazanie wyniku funkcji jest użycie wartości zwracanej. Mam nadzieję, że to oczywiste. W językach obiektowych drugą najlepszą metodą jest mutowanie obiektu.

Pomiędzy tymi dwiema opcjami istnieje tak wiele czystych, oczywistych sposobów komunikowania wyniku funkcji, że jeśli kiedykolwiek chcesz zmutować argumenty jako jedyny środek, coś poszło nie tak w twojej architekturze. Musisz zmienić swoje obowiązki klasowe, aby osoba dokonująca mutacji była w pierwszej kolejności danymi.

Jedynym wyjątkiem są bardzo ogólne algorytmy. Na przykład algorytm sortowania może słusznie być oddzielny od sortowanych kontenerów, jeśli można go ogólnie zastosować do dowolnego typu kontenera przy użyciu jego publicznego interfejsu. Funkcja jednego strzału appendFooternie ma takiej wymówki.

Karl Bielefeldt
źródło