Nazwane argumenty (parametry) jako pomoc w czytelności

11

Dawno temu dużo programowałem w ADA i normalne było nazywanie argumentów podczas wywoływania funkcji - SomeObject.DoSomething (SomeParameterName => someValue);

Teraz, gdy C # obsługuje nazwane argumenty, myślę o powrocie do tego nawyku w sytuacjach, w których może nie być oczywiste, co oznacza argument.

Możesz argumentować, że zawsze powinno być oczywiste, co oznacza argument, ale jeśli masz argument boolowski, a osoby wywołujące przekazują „prawda” lub „fałsz”, wówczas kwalifikacja wartości za pomocą nazwy sprawia, że ​​strona wywoływania jest bardziej czytelna.

contentFetcher.DownloadNote (uwaga, instrukcja: prawda);

Chyba mógłbym stworzyć Enums zamiast używać true lub false (w tym przypadku ręczne, automatyczne).

Co sądzisz o sporadycznym używaniu nazwanych argumentów, aby ułatwić czytanie kodu?

Damian
źródło
Pomaga także komentarz do parametru nad metodami.
Amir Rezaei
1
@ amir-rezaei: Komentarze parametrów nad metodami pomagają tylko wtedy, gdy można zaufać komentarzom. Jeśli nie masz dobrych programistów i dobrych procesów sprawdzania kodu, nie ufam komentarzom.
btilly
Jako sam jednorazowy użytkownik Ada jestem od czasu do czasu do użytku, jak opisujesz. Tak pamiętam, że były używane w Adzie, BTW - nie nazwałbym tego „nienormalnym”, ale słowo „normalne” wydaje się sugerować, że większość wywołań określałaby nazwy parametrów, co nie jest tym, co pamiętam. Oczywiście konwencje są różne, ale niepotrzebny bałagan jest złą konwencją w każdym języku IMO.
Steve314
Zgadzam się co do twojego wykorzystania w Adzie - nie robiliśmy tego przez cały czas, ale to pomogło.
Damian

Odpowiedzi:

7

Zostało to zasugerowane w rozwoju C ++, a Stroustrup omawia to w swoim „Design and Evolution of C ++”, na stronach 153 i następnych. Propozycja była dobrze sformułowana i opierała się na wcześniejszych doświadczeniach z Adą. Nie został przyjęty.

Najważniejszym powodem było to, że nikt nie chciał zachęcać do funkcji o dużej liczbie parametrów. Każda dodatkowa funkcja w języku kosztuje coś, a nie było potrzeby dodawania funkcji ułatwiającej pisanie złych programów.

Pojawiły się także pytania o to, jakie były kanoniczne nazwy parametrów, szczególnie w zwykłej konwencji plików nagłówkowych i kodowych. Niektóre organizacje miały dłuższe i bardziej opisowe nazwy parametrów w pliku .h oraz krótsze i łatwiejsze do wpisania nazwy w pliku .cpp (sufiksy plików zastępczych według potrzeb). Wymaganie, aby były takie same, wiązałoby się z dodatkowymi kosztami kompilacji, a pomieszanie nazw między plikami źródłowymi może powodować subtelne błędy.

Można to również obsłużyć, używając obiektów zamiast wywołań funkcji. Zamiast wywołania GetWindow z tuzinem parametrów, utwórz klasę Window z tuzinem prywatnych zmiennych i dodaj settery w razie potrzeby. Łącząc setery, można napisać coś takiego my_window.SetColor(green).SetBorder(true).SetBorderSize(3);. Możliwe jest również posiadanie różnych funkcji z różnymi ustawieniami domyślnymi, które wywołują funkcję, która faktycznie działa.

Jeśli martwisz się tylko efektem dokumentacji contentFetcher.DownloadNote(note, manual : true);, zawsze możesz napisać coś takiego contentFetcher.DownloadNote(note, /* manual */ true);, więc nie jest to nawet bardzo pomocne w dokumentacji.

David Thornley
źródło
Co zabawne, kiedy przeniosłem się z Ady do C, zacząłem stosować konwencję, którą opisujesz - jednak recenzenci kodu go nie znosili. Zgadzam się, że nie używasz go w przypadku dużej liczby parametrów.
Damian
7

Myślę, że jest to kwestia uczynienia złego kodu bardziej czytelnym, niż „najlepszą praktyką”.

Posiadanie metody (lub wykonawcy), która przyjmuje 20 parametrów, to „nieprzyjemny zapach” i prawdopodobnie jest to spowodowane problemem w twoim projekcie. Jednak jeśli jestem zmuszony do pracy nad kodem, gdy metody wymagają wielu parametrów, wówczas nazwany parametr sprawia, że ​​kod jest trudniejszy do zrozumienia.

Gdy metody mają tylko 1 lub 2 parametry i z nazwy metody jasno wynika, jaki jest parametr, wówczas nazwany parametr nic nie dodaje. To idealny przypadek.

Jeśli cały kod, nad którym pracujesz, jest zapisany jako książka „ czystego kodu ”, to będziesz miał bardzo małe zastosowanie dla nazwanych parametrów, jednak żyjemy w prawdziwym świecie.

Ian
źródło
1
Funkcja z dwoma parametrami może być myląca, jeśli oba parametry są tego samego typu i bez pojęcia, który jest który. Oczywiście możesz nazwać funkcję w sposób zapewniający tę wskazówkę, ale tak naprawdę robisz to, co robią nazwy parametrów - z wyjątkiem tego, że obowiązkowe jest dołączanie tej gadatliwości nawet wtedy, gdy kontekst (np. Wyrażenia dostarczające parametry) wyraźnie pokazują, który parametr jest który.
Steve314
1
@ Steve314 Przykład:void TrackDataChange(Data oldData, Data newData)
Alexander
3

Zgadzam się, że dodanie nazwy parametru czyni ją bardziej czytelną. Jednak większość książek, które przeczytałem, wydaje się uważać przełączniki logiczne za złą praktykę. Czasami to robię:

public Content DownloadNote(Note note)
{
    return downloadNote(note, manual: false);
}

public Content DownloadNoteManually(Note note)
{
    return downloadNote(note, manual: true);
}

Zapewnia to większą elastyczność podczas wdrażania interfejsu API. Pozwala także kontrolować przypadek, w którym masz wiele przełączników boolowskich, ale nie wszystkie z nich mogą być aktywne jednocześnie.

Scott Whitlock
źródło
3
Jeśli masz x opcji, czy stworzysz 2 ^ x nakładki?
apoorv020
@ apoorv020 - gdybym miał wystarczającą liczbę parametrów do wywołania metody, która 2 ^ x stałaby się znacząca, stworzyłbym nową klasę do przechowywania wartości parametrów i przekazałbym tylko jeden parametr.
Scott Whitlock
@ scott-whitlock: Opcja 1 - utwórz obiekt, ustaw właściwości x, wywołaj metodę z obiektem raz. Opcja 2 - wywołanie metody o nazwanych parametrach x. Co jest lepsze, zależy od twojego języka. W języku dynamicznie wpisywanym opcja 1 wymaga znacznie więcej płyty kotłowej bez wzmocnienia, a więc jest gorzej. W języku o typie statycznym nadal dodajesz podstawkę, ale testy niepoprawnie nazwanych kluczy należy wykonywać w czasie kompilacji, a nie w czasie wykonywania. Dlatego opcja 1 jest lepsza w C #, ale opcja 2 jest wyraźną wygraną w Pythonie.
btilly
@btilly - jak zauważył Ian, Clean Code wyraźnie mówi ci, abyś nie miał funkcji z więcej niż 2 lub 3 parametrami. Szczerze mówiąc, chodziło o Javę, która jest wpisana statycznie. OP pyta również o C #, który jest wpisany statycznie. Nadal wolałbym używać przeciążeń funkcji i / lub innych precyzyjnie nazwanych nazw funkcji niż długiej listy parametrów.
Scott Whitlock,
@ scott-whitlock: Czy faktycznie się nie zgadzamy? Zgadzamy się, co jest najlepsze w C #. Oboje wiemy, co mówi Clean Code. Chodzi mi o to, że ważne jest, aby zrozumieć, dlaczego jest to dobre, abyś wiedział, kiedy rada pasuje lub nie pasuje do innego środowiska.
btilly
3

Jestem wielkim zwolennikiem nazwanych parametrów w sytuacjach, w których typy i semantyka nie są jasne w nazwie metody. Z mojego doświadczenia wynika, że ​​niewielu ludzi czyta dokumentację.

Biorąc to pod uwagę, nazwane parametry nie powinny stanowić alternatywy dla tworzenia rozsądnych list argumentów, używania obiektów pomocniczych (do „wiązania” ze sobą semantycznie powiązanych argumentów) i używania wyliczeń, w stosownych przypadkach.

Uri
źródło
0

Myślę, że jest to bardziej przydatne w językach innych niż OO, w których możesz mieć jedną funkcję, która musi coś robić na kilka nieco innych sposobów, a sposób, w jaki określa, co należy zrobić, jest oparty na wartościach parametrów. W świecie OOP po prostu przeciążymy funkcję, ale jeśli nie jest to możliwe, kończysz przekazywanie wiązki flag (lub wiązki wartości, a to, czy są one przekazywane, jest flagą).

Myślę, że jest do pewnego stopnia bardziej czytelna; ale jak wspomnieli inni, posiadanie wielu parametrów jest zapachem kodu, więc nie widzę w tym większego zastosowania w języku obiektowym, takim jak C #.

TMN
źródło