Tecniche per ridurre al minimo il numero di argomenti delle funzioni


13

In Clean Code, è scritto che "il numero ideale di argomenti per una funzione è zero". I motivi per cui sono spiegati e hanno senso. Quello che sto cercando sono tecniche per riformattare i metodi con 4 o più argomenti per risolvere questo problema.

Un modo è quello di estrarre gli argomenti in una nuova classe, ma ciò porterebbe sicuramente a un'esplosione di classi? E quelle classi probabilmente finiranno con nomi che violano alcune delle regole di denominazione (che terminano con "Dati" o "Informazioni" ecc.)?

Un'altra tecnica consiste nel rendere variabili utilizzate da più funzioni una variabile membro privata per evitare di passarle, ma ciò espande l'ambito della variabile, possibilmente in modo tale che sia aperto a funzioni che non ne hanno realmente bisogno.

Sto solo cercando modi per ridurre al minimo gli argomenti delle funzioni, avendo accettato i motivi per cui è una buona idea farlo.


21
Tbh non sono affatto d'accordo con il codice pulito. Se il numero di argomenti per una funzione è zero, ciò implica che la funzione ha effetti collaterali e che probabilmente cambia stato da qualche parte. Mentre sono d'accordo che meno di 4 argomenti possono essere una buona regola empirica - Preferirei avere una funzione con 8 argomenti che è statica e non ha effetti collaterali rispetto a una funzione non statica con zero argomenti che cambia stato e ha effetti collaterali .
wasatz,

4
" In Clean Code, è scritto che" il numero ideale di argomenti per una funzione è zero ". " Davvero? È così sbagliato! Il numero ideale di parametri è uno, con un valore di ritorno derivato in modo deterministico da quell'unico parametro. In pratica, tuttavia, il numero di parametri non ha molta importanza; ciò che conta è che, quando possibile, la funzione dovrebbe essere pura (ovvero, deriva il suo valore di ritorno solo dai suoi parametri senza effetti collaterali).
David Arno,

2
Bene, in seguito il libro sottolinea che gli effetti collaterali non sono desiderabili ...
Neil Barnwell,


Risposte:


16

La cosa più importante da ricordare è che si tratta di linee guida, non di regole.

Ci sono casi in cui un metodo deve semplicemente prendere un argomento. Pensa al +metodo per i numeri, ad esempio. O il addmetodo per una raccolta.

In effetti, si potrebbe anche sostenere che ciò che significa aggiungere due numeri dipende dal contesto, ad esempio in ℤ 3 + 3 == 6, ma in ℤ | 5 3 + 3 == 2 , quindi l'operatore di addizione dovrebbe essere un metodo su un oggetto di contesto che accetta due argomenti anziché un metodo sui numeri che accetta un argomento.

Allo stesso modo, un metodo per confrontare due oggetti deve essere un metodo di un oggetto che prende l'altro come argomento, o un metodo del contesto, che prende due oggetti come argomenti, quindi semplicemente non ha senso avere un metodo di confronto con meno di un argomento.

Detto questo, ci sono un paio di cose che possono essere fatte per ridurre il numero di argomenti per un metodo:

  • Rendi il metodo stesso più piccolo : forse, se il metodo ha bisogno di tanti argomenti, sta facendo troppo?
  • Un'astrazione mancante : se gli argomenti sono strettamente correlati, forse appartengono insieme e c'è un'astrazione che ti manca? (Esempio di libro di testo canonico: invece di due coordinate, passa un Pointoggetto o invece di passare nome utente ed e-mail, passa un IdCardoggetto.)
  • Stato dell'oggetto : se l'argomento è necessario per più metodi, forse dovrebbe far parte dello stato dell'oggetto. Se è necessario solo con alcuni dei metodi ma non con altri, forse l'oggetto sta facendo troppo e dovrebbe essere davvero due oggetti.

Un modo è quello di estrarre gli argomenti in una nuova classe, ma ciò porterebbe sicuramente a un'esplosione di classi?

Se il tuo modello di dominio ha molti tipi diversi di cose, il tuo codice finirà con molti tipi diversi di oggetti. Non c'è niente di sbagliato in questo.

E quelle classi probabilmente finiranno con nomi che violano alcune delle regole di denominazione (che terminano con "Dati" o "Informazioni" ecc.)?

Se non riesci a trovare un nome proprio, forse hai raggruppato troppi argomenti insieme o troppo pochi. Quindi, o hai solo un frammento di una classe o hai più di una classe.

Un'altra tecnica consiste nel rendere variabili utilizzate da più funzioni una variabile membro privata per evitare di passarle, ma ciò espande l'ambito della variabile, possibilmente in modo tale che sia aperto a funzioni che non ne hanno realmente bisogno.

Se hai un gruppo di metodi che funzionano tutti con gli stessi argomenti e un altro gruppo di metodi che non lo fanno, forse appartengono a classi diverse.

Nota quanto spesso ho usato la parola "forse"? Ecco perché quelle sono linee guida, non regole. Forse il tuo metodo con 4 parametri va benissimo!


7
@ BrunoSchäpper: Sicuro: (1) " Rendi il metodo stesso più piccolo: forse, se il metodo ha bisogno di tanti argomenti, sta facendo troppo? ". Il numero di parametri è un test scarso di questo. I parametri opzionali / booleani e molte righe di codice sono forti indicatori di un metodo che fa troppo. Molti parametri sono nella migliore delle ipotesi deboli. (2) " Stato dell'oggetto: se l'argomento è necessario per più metodi, forse dovrebbe far parte dello stato dell'oggetto ". No, no e tre volte, no. Ridurre al minimo lo stato dell'oggetto; non parametri di funzione. Se possibile, passare un valore a tutti i metodi tramite parametri per evitare lo stato dell'oggetto.
David Arno,

L'esempio che hai fornito per l'aggiunta è completamente sbagliato. La addfunzione per i numeri naturali e la addfunzione per l'anello di numeri interi sono due azioni di azioni diverse su due tipi diversi. Non capisco neanche cosa intendi per "contesto".
gardenhead,

Thx @DavidArno. 1) concordato, non un forte indicatore in sé e per sé. Ma comunque buono. Vedo spesso alcuni metodi, con alcuni oggetti passati in giro. Non c'è stato dell'oggetto. Questa è buona, ma 2) l'opzione migliore IMHO sta riformattando quei metodi, sposta lo stato implicito in una nuova classe, che prende tutti questi parametri come argomenti espliciti. Si finisce con un metodo di argomento zero pubblico e molti metodi interni di argomento zero a uno. Lo stato non è pubblico, globale o addirittura mantenuto in vita a lungo, ma il codice è molto più pulito.
Bruno Schäpper,

6

Nota che zero argomenti non implica effetti collaterali, poiché il tuo oggetto è un argomento implicito. Guarda quanti metodi a zero arità ha l'elenco immutabile di Scala , per esempio.

Una tecnica utile che chiamo tecnica di "messa a fuoco dell'obiettivo". Quando si mette a fuoco un obiettivo fotografico, è più facile vedere il vero punto AF se lo si porta troppo lontano, quindi riportarlo nel punto corretto. Lo stesso vale per il refactoring del software.

Soprattutto se si utilizza il controllo della versione distribuita, le modifiche al software sono facili da sperimentare, vedere se ti piace come appaiono e arretrare in caso contrario, ma per qualche ragione le persone sembrano spesso riluttanti a farlo.

Nel contesto della tua domanda attuale, ciò significa scrivere le versioni zero o di un argomento, con prima diverse funzioni divise, quindi è relativamente facile vedere quali funzioni devono essere combinate per la leggibilità.

Nota che l'autore è anche un grande sostenitore dello sviluppo guidato dai test, che all'inizio tende a produrre funzioni a bassa arità perché inizi con i tuoi banali casi di test.


1
Come l'analogia della "messa a fuoco dell'obiettivo" - Soprattutto quando si esegue il refactoring, è importante utilizzare l'obiettivo grandangolare invece di quello da vicino. E esaminare il numero di parametri è semplicemente troppo ravvicinato
tofro

0

Un approccio che semplicemente (e ingenuamente - o dovrei anche dire alla cieca ) mira solo a ridurre il numero di argomenti per le funzioni è probabilmente sbagliato. Non c'è assolutamente nulla di sbagliato nelle funzioni che hanno un gran numero di argomenti. Se sono richiesti dalla logica, beh, sono richiesti ... Un lungo elenco di parametri non mi preoccupa affatto - purché sia ​​formattato e commentato correttamente per essere leggibile.

Nel caso in cui tutti o un sottoinsieme di argomenti appartengano a un'unica entità logica e siano comunemente passati in gruppi in tutto il programma, potrebbe essere sensato raggrupparli in alcuni contenitori, in genere una struttura o un altro oggetto. Esempi tipici potrebbero essere una sorta di messaggio o tipo di dati di evento .

Puoi facilmente esagerare con questo approccio: una volta scoperto che l'imballaggio e il disimballaggio delle cose da e verso tali contenitori di trasporto genera un sovraccarico maggiore di quello che migliora la leggibilità, probabilmente sei andato troppo lontano.

OTOH, elenchi di parametri di grandi dimensioni possono essere un segno del fatto che il tuo programma potrebbe non essere strutturato correttamente - forse la funzione che richiede un numero così elevato di parametri sta solo cercando di fare troppo e dovrebbe essere suddivisa in diverse funzioni più piccole. Preferirei iniziare qui piuttosto che preoccuparmi del numero di parametri.


5
Ridurre ciecamente il numero di argomenti è sbagliato, ovviamente. Ma non sono d'accordo con "Non c'è assolutamente nulla di sbagliato nelle funzioni che hanno un gran numero di argomenti". . Secondo me, quando si colpisce una funzione con un gran numero di argomenti, nel 99,9% di tutti i casi c'è un modo per migliorare la struttura del codice in modo deliberato che (anche) riduce il numero di argomenti della funzione.
Doc Brown,

@DocBrown Ecco perché c'è questo ultimo paragrafo e la raccomandazione di iniziare lì .... E un altro: Probabilmente non hai mai provato a programmare contro un'API di MS Windows;)
tofro

"forse la funzione che richiede un numero così elevato di parametri sta solo cercando di fare troppo e dovrebbe essere suddivisa in diverse funzioni più piccole." Sono totalmente d'accordo, anche se in pratica non finisci con un'altra funzione più in alto nello stack che chiama quelle diverse funzioni più piccole? Potresti quindi trasformarli in un oggetto, ma quell'oggetto avrà un ctor. Potresti usare un costruttore blah blah blah. Il punto è che è una regressione infinita - da qualche parte, ci sono un certo numero di valori necessari affinché il software faccia il suo lavoro e devono in qualche modo raggiungere quelle funzioni.
Neil Barnwell,

1
@NeilBarnwell Nel caso ideale (vale la pena refactoring) potresti essere in grado di dividere una funzione che necessita di 10 argomenti in tre che richiedono 3-4 argomenti ciascuno. Nel caso non così ideale, si finiscono con tre funzioni che richiedono 10 argomenti ciascuna (meglio lasciar perdere, quindi). Per quanto riguarda l'argomento dello stack più in alto: Accetto - Potrebbe essere il caso, ma non necessariamente - gli argomenti provengono da qualche parte e quel recupero potrebbe essere anche da qualche parte all'interno dello stack e deve solo essere messo nel posto giusto lì - Chilometraggio tende a variare.
martedì

La logica del software non richiede mai più di quattro parametri. Solo un compilatore potrebbe.
theDoctor

0

Non esiste un modo magico: è necessario padroneggiare il dominio problematico per scoprire l'architettura giusta. Questo è l'unico modo per refactoring: padroneggiare il dominio problematico. Più di quattro parametri è solo una scommessa sicura che la tua architettura attuale sia difettosa e sbagliata.

Le uniche eccezioni sono i compilatori (metaprogrammi) e le simulazioni, in cui il limite è teoricamente 8, ma probabilmente solo 5.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.