Elegancki sposób przekazywania wielu argumentów do funkcji

Mam funkcję która wygląda tak:

bool generate_script (bool net, bool tv, bool phone,
                        std::string clientsID,
                        std::string password,
                        int index, std::string number, 
                        std::string Iport, std::string sernoID,
                        std::string VoiP_number, std::string  VoiP_pass,
                        std::string target, int slot, int port, 
                        int onu, int extra, std::string IP, std::string MAC);
Według mnie wygląda brzydko. Jaki jest właściwy sposób radzenia sobie z tym problemem? Czy powinienem utworzyć kilka wektorów z różnymi typami danych (int, string i bool) i przekazać je jako argumenty do tej funkcji?
Author: Braiam, 2014-07-25

6 answers

Jeśli wszystkie te parametry są ze sobą znacząco powiązane, zapakuj je w strukturę.

 73
Author: Quentin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-25 09:19:39

Umieścić je w struct

Utwórz strukturę

struct GenerateScriptParams { /* ... */ };

I umieścić tam wszystkie parametry. Można również podać domyślne wartości inicjalizacji struct, implementując domyślny konstruktor lub, w C++11, dostarczając domyślne wartości inicjalizacji poszczególnych elementów. Następnie można zmienić wartości, które nie powinny być domyślne. To selektywne wybieranie nie-domyślnych parametrów nie jest możliwe dla wywołania funkcji z dużą ilością parametrów w C++.

Aby interfejs był miły dla rozmówcy

Jednak użycie jest trochę brzydkie, ponieważ musisz utworzyć tymczasowy obiekt o nazwie, następnie zmienić wartości, które nie powinny być domyślne, a następnie przekazać obiekt do funkcji:

GenerateScriptParams gsp;
gsp.net = true;
gsp.phone = false;
gps.extra = 10;
generate_script( gsp );

Jeśli wywołasz tę funkcję w kilku różnych miejscach, sensowne jest uniknięcie tej brzydoty poprzez dostarczenie mutujących funkcji Członkowskich, które można przykleić łańcuchem:

GenerateScriptParams & GenerateScriptParams::setNet  ( bool val );
GenerateScriptParams & GenerateScriptParams::setTV   ( bool val );
GenerateScriptParams & GenerateScriptParams::setPhone( bool val );
// ... //

Wtedy kod wywołujący może napisać

generate_script( GenerateScriptParams()
    .setNet(true),
    .setPhone(false),
    .setExtra(10) );

Bez powyższego brzydota. Pozwala to uniknąć nazwanego obiektu, który jest używany tylko raz.

 43
Author: Ralph Tandetzky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-25 11:42:51

Osobiście nie wierzę, że przeniesienie wszystkich argumentów w jedną strukturę sprawi, że kod będzie o wiele lepszy. Po prostu przenosisz brud pod dywan. Kiedy masz zamiar zająć się tworzeniem struktury, masz ten sam problem.

Pytanie brzmi, ile będzie tej struktury wielokrotnego użytku? Jeśli skończysz z parametrami 18 dla jednej funkcji, zadzwoń coś, co nie jest do końca poprawne w Twoim projekcie. Po dalszej analizie można odkryć, że parametry te można pogrupować w kilka różne klasy i te klasy mogą być agregowane do jednego obiektu, który będzie wejściem funkcji. Możesz również preferować klasy do struktury w celu ochrony danych.

EDIT

Dam ci mały przykład, aby opisać, dlaczego kilka klas jest lepszych niż jedna monolityczna struktura. Zacznijmy liczyć testy, które musisz napisać, aby pokryć powyższą funkcję. Jako wejście jest 18 parametrów (3 Logiczne). Więc będziemy potrzebować co najmniej 15 testuje tylko w celu walidacji danych wejściowych (zakładając, że wartości nie są ze sobą połączone).

Całkowita liczba testów jest niemożliwa do obliczenia bez implementacji, ale możemy mieć pojęcie o wielkości. Weźmy dolną granicę wszystkie dane wejściowe mogą być traktowane jako boolean liczba możliwych kombinacji wynosi 2^18 więc około 262000 testów .

Co się stanie, jeśli podzielimy dane wejściowe na kilka obiektów?

Po pierwsze, kod do walidacji danych wejściowych jest przenoszony z dala od funkcji do ciała każdego pojedynczego obiektu (i może być ponownie użyty).

Ale co ważniejsze liczba testów się zawali, powiedzmy w grupie czterech (4,4,4 i 4 paramy na obiekt) całkowita liczba testów wynosi tylko:

2^4 + 2^4 + 2^4 + 2^4 + 2^4 = 80

Piąty atrybut wynika z permutacji obiektów z samym sobą.

Więc, co jest bardziej wymagające kosztów? Napisać tysiąc testów czy kilka klas?

Oczywiście jest to prymitywne uproszczenie będzie jednak podstawą problemu. interfejs bałaganu to nie tylko kwestia stylu lub niewygoda dla programisty, to prawdziwa przeszkoda w tworzeniu kodu jakości .

To najważniejsza lekcja, jaką kiedykolwiek nauczyłem się w mojej karierze profesjonalnego programisty: "Duże klasy i interfejsy fat to zło". To tylko moja heurystyczna wersja Zasady pojedynczej odpowiedzialności (zauważyłem, że SRP może być trudne, aby to dobrze zrobić, co wydaje się rozsądne, aby być pojedynczą odpowiedzialnością może nie być tak samo po Godzinie Kodowania, więc użyłem jakiejś heurystycznej zasady, aby pomóc mi zrewolucjonizować moje początkowe wybory).

 17
Author: Alessandro Teruzzi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-01 08:26:03

Lub możesz użyć płynnego interfejsu . Wyglądałoby to tak:

script my_script(mandatory, parameters);
my_script.net(true).tv(false).phone(true);

Ma to zastosowanie, jeśli masz domyślne wartości dla określonych parametrów lub jest dozwolone mieć częściowo skonstruowany skrypt.

 13
Author: PovilasB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-25 09:23:16

Ignorowanie możliwości lub celowości zmiany funkcji lub programu w jakiś sposób w celu zmniejszenia liczby parametrów...

Widziałem standardy kodowania, które określają, jak długie listy parametrów powinny być sformatowane, w przypadkach, gdy refaktoryzacja nie jest możliwa. Jednym z takich przykładów jest użycie podwójnych wcięć i jednego parametru na linię(nie dla wszystkich funkcji - tylko dla tych, które mają wiele linii parametrów).

Np.

bool generate_script (
        bool net,
        bool tv,
        bool phone,
        std::string clientsID,
        std::string password,
        int index,
        std::string number,
        std::string Iport,
        std::string sernoID,
        std::string VoiP_number,
        std::string  VoiP_pass,
        std::string target,
        int slot,
        int port,
        int onu,
        int extra,
        std::string IP,
        std::string MAC);

Chodzi o to, aby stworzyć spójny układ i wygląd wszystkich funkcji z dużą liczbą parametrów.

 8
Author: izb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-25 09:29:25

Trochę za późno, ale ponieważ nikt jeszcze tego nie zrobił, chciałbym zwrócić uwagę na oczywisty aspekt problemu: dla mnie funkcja, która przyjmuje tak wiele argumentów, może wykonać wiele obliczeń, więc rozważ możliwość rozłożenia jej na mniejsze funkcje jako pierwszy krok.

To powinno pomóc w uporządkowaniu danych.

 1
Author: bassfault,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-12 06:49:56