Qual è il modo migliore per effettuare il refactoring di un metodo che ha troppi (6+) parametri?


102

Di tanto in tanto mi imbatto in metodi con un numero scomodo di parametri. Il più delle volte sembrano essere costruttori. Sembra che dovrebbe esserci un modo migliore, ma non riesco a vedere cosa sia.

return new Shniz(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

Ho pensato di usare gli struct per rappresentare l'elenco dei parametri, ma sembra che questo sposti il ​​problema da un posto all'altro e crei un altro tipo nel processo.

ShnizArgs args = new ShnizArgs(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)
return new Shniz(args);

Quindi non sembra un miglioramento. Allora qual è l'approccio migliore?


Hai detto "struct". Questo termine ha connotazioni diverse in diversi linguaggi di programmazione. Cosa intendi significare?
Jay Bazuzi

1
Se stai cercando un linguaggio particolare per chiarire le ambiguità, vai con C #. Ma fondamentalmente, solo una semplice borsa di proprietà. Ha proprietà denominate diverse con tipi diversi. Potrebbe essere definito come una classe, una tabella hash, uno struct o qualsiasi altra cosa.
ricorsivo

Questo articolo ha alcune buone informazioni sull'argomento. Specifico per Javascript, ma i principi possono essere riapplicati ad altre lingue.
lala

Risposte:


94

Il modo migliore sarebbe trovare modi per raggruppare insieme gli argomenti. Ciò presuppone, e funziona davvero solo se, si finirà con più "raggruppamenti" di argomenti.

Ad esempio, se stai passando la specifica per un rettangolo, puoi passare x, y, larghezza e altezza oppure puoi semplicemente passare un oggetto rettangolo che contiene x, y, larghezza e altezza.

Cerca cose come questa durante il refactoring per ripulirlo un po '. Se davvero gli argomenti non possono essere combinati, inizia a verificare se hai una violazione del Principio di responsabilità unica.


4
Buona idea ma cattivo esempio; il costruttore per il rettangolo dovrebbe avere 4 argomenti. Ciò avrebbe più senso se il metodo si aspettasse 2 set di coordinate / dimensioni rettangolari. Quindi potresti passare 2 rettangoli invece di x1, x2, y1, y2 ...
Programmatore fuorilegge

2
Giusto. Come ho detto, ha davvero senso farlo solo se si finisce con più raggruppamenti logici.
Matthew Brubaker

23
+1: A Single Responsibility, è uno dei pochi commenti in tutte le risposte che sta davvero affrontando il vero problema. Quale oggetto ha davvero bisogno di 7 valori indipendenti per formare la sua identità.
AnthonyWJones

6
@AnthonyWJones Non sono d'accordo. I dati per le condizioni meteorologiche attuali possono avere molti più valori indipendenti per formare la sua identità.
7

107

Presumo che tu intenda C # . Alcune di queste cose si applicano anche ad altre lingue.

Hai diverse opzioni:

passare da costruttore a setter di proprietà . Questo può rendere il codice più leggibile, perché è ovvio al lettore quale valore corrisponde a quali parametri. La sintassi dell'inizializzatore di oggetti lo rende piacevole. È anche semplice da implementare, poiché puoi semplicemente usare proprietà generate automaticamente e saltare la scrittura dei costruttori.

class C
{
    public string S { get; set; }
    public int I { get; set; }
}

new C { S = "hi", I = 3 };

Tuttavia, si perde l'immutabilità e si perde la capacità di garantire che i valori richiesti siano impostati prima di utilizzare l'oggetto in fase di compilazione.

Modello di generatore .

Pensa alla relazione tra stringe StringBuilder. Puoi ottenerlo per le tue classi. Mi piace implementarlo come una classe nidificata, quindi la classe Cha una classe correlata C.Builder. Mi piace anche un'interfaccia fluida sul generatore. Fatto bene, puoi ottenere una sintassi come questa:

C c = new C.Builder()
    .SetX(4)    // SetX is the fluent equivalent to a property setter
    .SetY("hello")
    .ToC();     // ToC is the builder pattern analog to ToString()

// Modify without breaking immutability
c = c.ToBuilder().SetX(2).ToC();

// Still useful to have a traditional ctor:
c = new C(1, "...");

// And object initializer syntax is still available:
c = new C.Builder { X = 4, Y = "boing" }.ToC();

Ho uno script PowerShell che mi consente di generare il codice del builder per fare tutto questo, dove l'input assomiglia a:

class C {
    field I X
    field string Y
}

Quindi posso generare in fase di compilazione. partialle classi mi consentono di estendere sia la classe principale che il generatore senza modificare il codice generato.

Refactoring "Introdurre oggetto parametro" . Consulta il catalogo di refactoring . L'idea è di prendere alcuni dei parametri che stai passando e inserirli in un nuovo tipo, quindi passare invece un'istanza di quel tipo. Se lo fai senza pensare, tornerai al punto di partenza:

new C(a, b, c, d);

diventa

new C(new D(a, b, c, d));

Tuttavia, questo approccio ha il potenziale maggiore per avere un impatto positivo sul codice. Quindi, continua seguendo questi passaggi:

  1. Cerca sottoinsiemi di parametri che abbiano senso insieme. Raggruppare semplicemente tutti i parametri di una funzione insieme senza pensare non ti dà molto; l'obiettivo è avere raggruppamenti che abbiano un senso. Saprai di aver capito bene quando il nome del nuovo tipo è ovvio.

  2. Cerca altri punti in cui questi valori vengono utilizzati insieme e utilizza anche il nuovo tipo. È probabile che, quando hai trovato un buon nuovo tipo per un insieme di valori che già usi ovunque, quel nuovo tipo avrà senso anche in tutti quei posti.

  3. Cerca la funzionalità che è nel codice esistente, ma appartiene al nuovo tipo.

Ad esempio, forse vedi un codice che assomiglia a:

bool SpeedIsAcceptable(int minSpeed, int maxSpeed, int currentSpeed)
{
    return currentSpeed >= minSpeed & currentSpeed < maxSpeed;
}

Si potrebbe prendere le minSpeede maxSpeedparametri e metterli in un nuovo tipo:

class SpeedRange
{
   public int Min;
   public int Max;
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return currentSpeed >= sr.Min & currentSpeed < sr.Max;
}

È meglio, ma per sfruttare davvero il nuovo tipo, sposta i confronti nel nuovo tipo:

class SpeedRange
{
   public int Min;
   public int Max;

   bool Contains(int speed)
   {
       return speed >= min & speed < Max;
   }
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return sr.Contains(currentSpeed);
}

E ora stiamo arrivando da qualche parte: l'implementazione di SpeedIsAcceptable()now dice cosa intendi e hai una classe utile e riutilizzabile. (Il prossimo passo ovvio è fare SpeedRangein Range<Speed>.)

Come puoi vedere, Introduce Parameter Object è stato un buon inizio, ma il suo vero valore è stato che ci ha aiutato a scoprire un tipo utile che mancava dal nostro modello.


4
Suggerirei di provare prima "Introduce Oggetto Parametro" e ripiegare sulle altre opzioni solo se non riesci a trovare un buon oggetto parametro da creare.
Douglas Leeder

4
ottima risposta. se hai menzionato la spiegazione del refactoring prima degli zuccheri sintattici c #, questo sarebbe stato votato più alto IMHO.
rpattabi

10
Ooh! +1 per "Saprai di aver capito bene quando il nome del nuovo tipo è ovvio".
Sean McMillan

20

Se è un costruttore, in particolare se sono presenti più varianti sovraccaricate, dovresti guardare il modello Builder:

Foo foo = new Foo()
          .configBar(anything)
          .configBaz(something, somethingElse)
          // and so on

Se è un metodo normale, dovresti pensare alle relazioni tra i valori passati e magari creare un oggetto di trasferimento.


Ottima risposta. Forse anche più rilevante della risposta "metti i parametri in una classe" che tutti (me compreso) hanno dato.
Wouter Lievens

1
Probabilmente è una cattiva idea rendere la tua classe mutabile solo per evitare di passare troppi parametri al costruttore.
Programmatore fuorilegge

@outlaw - se la mutabilità è un problema, puoi facilmente implementare la semantica "run once". Tuttavia, un gran numero di parametri ctor indica spesso la necessità di una configurazione (o, come altri hanno notato, una classe che cerca di fare troppe cose). (segue)
kdgregory

Sebbene sia possibile esternalizzare la configurazione, in molti casi non è necessario, in particolare se è guidata dallo stato del programma o è standard per un determinato programma (si pensi ai parser XML, che possono essere consapevoli dello spazio dei nomi, convalidare con strumenti diversi, ecc).
kdgregory

Mi piace il pattern builder, ma separo i miei tipi di builder immutabili e mutabili, come string / StringBuilder, ma utilizzo classi nidificate: Foo / Foo.Builder. Ho uno script PowerShell per generare il codice per farlo per semplici classi di dati.
Jay Bazuzi

10

La risposta classica a questa domanda è utilizzare una classe per incapsulare alcuni o tutti i parametri. In teoria sembra fantastico, ma sono il tipo di persona che crea classi per concetti che hanno un significato nel dominio, quindi non è sempre facile applicare questo consiglio.

Ad esempio invece di:

driver.connect(host, user, pass)

Potresti usare

config = new Configuration()
config.setHost(host)
config.setUser(user)
config.setPass(pass)
driver.connect(config)

YMMV


5
Sicuramente mi piacerebbe di più il primo pezzo di codice. Sono d'accordo, che c'è un certo limite, oltre il quale il numero di parametri diventa brutto, ma per i miei gusti, 3 sarebbe accettabile.
blabla999

10

Questo è citato dal libro di Fowler e Beck: "Refactoring"

Elenco dei parametri lungo

Nei nostri primi giorni di programmazione ci è stato insegnato a trasmettere come parametri tutto ciò che serve per una routine. Ciò era comprensibile perché l'alternativa erano i dati globali, e i dati globali sono malvagi e solitamente dolorosi. Gli oggetti cambiano questa situazione perché se non hai qualcosa di cui hai bisogno, puoi sempre chiedere a un altro oggetto di prenderlo per te. Quindi con gli oggetti non si passa tutto ciò di cui il metodo ha bisogno; invece passi abbastanza in modo che il metodo possa ottenere tutto ciò di cui ha bisogno. Molto di ciò di cui ha bisogno un metodo è disponibile nella classe host del metodo. Nei programmi orientati agli oggetti gli elenchi di parametri tendono ad essere molto più piccoli rispetto ai programmi tradizionali. Questo è positivo perché gli elenchi di parametri lunghi sono difficili da capire, perché diventano incoerenti e difficili da usare, e perché li stai cambiando per sempre perché hai bisogno di più dati. La maggior parte delle modifiche vengono rimosse passando gli oggetti perché è molto più probabile che sia necessario effettuare solo un paio di richieste per ottenere una nuova porzione di dati. Usa Sostituisci parametro con metodo quando puoi ottenere i dati in un parametro effettuando una richiesta di un oggetto che già conosci. Questo oggetto potrebbe essere un campo o potrebbe essere un altro parametro. Usa Preserve Whole Object per prendere una serie di dati raccolti da un oggetto e sostituirli con l'oggetto stesso. Se si dispone di più elementi di dati senza alcun oggetto logico, utilizzare Introduce Parameter Object. C'è un'importante eccezione per apportare queste modifiche. Questo è quando esplicitamente non si desidera creare una dipendenza dall'oggetto chiamato all'oggetto più grande. In questi casi è ragionevole decomprimere i dati e inviarli come parametri, ma prestare attenzione al dolore coinvolto. Se l'elenco dei parametri è troppo lungo o cambia troppo spesso, è necessario ripensare la struttura delle dipendenze.


7

Non voglio suonare come un saggio crack, ma dovresti anche controllare per assicurarti che i dati che stai trasmettendo dovrebbero davvero essere passati: Passare cose a un costruttore (o metodo per quella materia) ha un odore un po 'come poca enfasi sul comportamento di un oggetto.

Non fraintendetemi: i metodi e i costruttori a volte avranno molti parametri. Ma quando lo incontri, prova invece a considerare l'incapsulamento dei dati con il comportamento .

Questo tipo di odore (dato che stiamo parlando di refactoring, questa orribile parola sembra appropriata ...) potrebbe essere rilevato anche per oggetti che hanno molte proprietà (leggi: qualsiasi) o getter / setter.


7

Quando vedo lunghi elenchi di parametri, la mia prima domanda è se questa funzione o oggetto stia facendo troppo. Tener conto di:

EverythingInTheWorld earth=new EverythingInTheWorld(firstCustomerId,
  lastCustomerId,
  orderNumber, productCode, lastFileUpdateDate,
  employeeOfTheMonthWinnerForLastMarch,
  yearMyHometownWasIncorporated, greatGrandmothersBloodType,
  planetName, planetSize, percentWater, ... etc ...);

Ovviamente questo esempio è volutamente ridicolo, ma ho visto molti programmi reali con esempi solo leggermente meno ridicoli, dove una classe è usata per contenere molte cose a malapena correlate o non correlate, apparentemente solo perché lo stesso programma chiamante ha bisogno di entrambe o perché il il programmatore pensava a entrambi allo stesso tempo. A volte la soluzione più semplice è suddividere la classe in più parti, ognuna delle quali fa le proprie cose.

Un po 'più complicato è quando una classe ha davvero bisogno di gestire più cose logiche, come sia un ordine del cliente che informazioni generali sul cliente. In questi casi, crea una classe per il cliente e una classe per l'ordine e lascia che si parlino tra loro se necessario. Quindi invece di:

 Order order=new Order(customerName, customerAddress, customerCity,
   customerState, customerZip,
   orderNumber, orderType, orderDate, deliveryDate);

Potremmo avere:

Customer customer=new Customer(customerName, customerAddress,
  customerCity, customerState, customerZip);
Order order=new Order(customer, orderNumber, orderType, orderDate, deliveryDate);

Anche se ovviamente preferisco le funzioni che richiedono solo 1 o 2 o 3 parametri, a volte dobbiamo accettare che, realisticamente, questa funzione richiede un mucchio e che il numero di per sé non crea davvero complessità. Per esempio:

Employee employee=new Employee(employeeId, firstName, lastName,
  socialSecurityNumber,
  address, city, state, zip);

Sì, sono un mucchio di campi, ma probabilmente tutto ciò che faremo con loro è salvarli in un record di database o lanciarli su uno schermo o qualcosa del genere. Non c'è davvero molta elaborazione qui.

Quando i miei elenchi di parametri diventano lunghi, preferisco di gran lunga dare ai campi diversi tipi di dati. Come quando vedo una funzione come:

void updateCustomer(String type, String status,
  int lastOrderNumber, int pastDue, int deliveryCode, int birthYear,
  int addressCode,
  boolean newCustomer, boolean taxExempt, boolean creditWatch,
  boolean foo, boolean bar);

E poi lo vedo chiamato con:

updateCustomer("A", "M", 42, 3, 1492, 1969, -7, true, false, false, true, false);

Mi preoccupo. Guardando la chiamata, non è affatto chiaro cosa significano tutti questi numeri, codici e bandiere criptici. Questo è solo chiedere errori. Un programmatore potrebbe facilmente confondersi sull'ordine dei parametri e scambiarne due accidentalmente, e se sono lo stesso tipo di dati, il compilatore lo accetterebbe. Preferirei di gran lunga una firma in cui tutte queste cose sono enumerazioni, quindi una chiamata passa in cose come Type.ACTIVE invece di "A" e CreditWatch.NO invece di "false", ecc.


5

Se alcuni dei parametri del costruttore sono opzionali, ha senso usare un builder, che otterrebbe i parametri richiesti nel costruttore, e avrebbe metodi per quelli opzionali, restituendo il builder, da usare in questo modo:

return new Shniz.Builder(foo, bar).baz(baz).quux(quux).build();

I dettagli di questo sono descritti in Effective Java, 2nd Ed., P. 11. Per gli argomenti del metodo, lo stesso libro (p. 189) descrive tre approcci per abbreviare gli elenchi di parametri:

  • Suddividi il metodo in più metodi che richiedono meno argomenti
  • Crea classi membro helper statiche per rappresentare gruppi di parametri, ad esempio passa a DinoDonkeyinvece di dinoedonkey
  • Se i parametri sono opzionali, il builder sopra può essere adottato per i metodi, definendo un oggetto per tutti i parametri, impostando quelli richiesti e quindi chiamando alcuni metodi di esecuzione su di esso

4

Userei il costruttore predefinito e i settor di proprietà. C # 3.0 ha una sintassi piacevole per eseguire questa operazione automaticamente.

return new Shniz { Foo = foo,
                   Bar = bar,
                   Baz = baz,
                   Quuz = quux,
                   Fred = fred,
                   Wilma = wilma,
                   Barney = barney,
                   Dino = dino,
                   Donkey = donkey
                 };

Il miglioramento del codice consiste nel semplificare il costruttore e nel non dover supportare più metodi per supportare varie combinazioni. La sintassi "chiamante" è ancora un po '"prolissa", ma in realtà non è peggiore che chiamare manualmente i settor di proprietà.


2
Ciò consentirebbe l'esistenza di un oggetto t new Shniz (). Una buona implementazione OO cercherebbe di ridurre al minimo la possibilità di oggetti esistenti in stato incompleto.
AnthonyWJones

In generale, qualsiasi lingua con una sintassi hash / dizionario nativa viene fornita con un sostituto adeguato per i parametri denominati (che sono ottimi e spesso ciò che richiedono queste situazioni, ma per qualche motivo l'unico linguaggio popolare che li supporta è il peggiore del pianeta) .
caos

4

Non hai fornito informazioni sufficienti per giustificare una buona risposta. Un lungo elenco di parametri non è intrinsecamente negativo.

Shniz (foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

potrebbe essere interpretato come:

void Shniz(int foo, int bar, int baz, int quux, int fred, 
           int wilma, int barney, int dino, int donkey) { ...

In questo caso è molto meglio creare una classe per incapsulare i parametri perché dai un significato ai diversi parametri in un modo che il compilatore può controllare e rendere visivamente il codice più facile da leggere. Inoltre, rende più semplice la lettura e il refactoring in seguito.

// old way
Shniz(1,2,3,2,3,2,1,2);
Shniz(1,2,2,3,3,2,1,2); 

//versus
ShnizParam p = new ShnizParam { Foo = 1, Bar = 2, Baz = 3 };
Shniz(p);

In alternativa se avessi:

void Shniz(Foo foo, Bar bar, Baz baz, Quux quux, Fred fred, 
           Wilma wilma, Barney barney, Dino dino, Donkey donkey) { ...

Questo è un caso molto diverso perché tutti gli oggetti sono diversi (e non è probabile che vengano confusi). Concordato sul fatto che se tutti gli oggetti sono necessari e sono tutti diversi, non ha molto senso creare una classe di parametri.

Inoltre, alcuni parametri sono opzionali? Ci sono sostituzioni di metodo (stesso nome di metodo, ma firme di metodo diverse?) Questi tipi di dettagli sono tutti importanti su quale sia la risposta migliore .

* Anche una borsa delle proprietà può essere utile, ma non specificamente migliore dato che non viene fornito alcuno sfondo.

Come puoi vedere, c'è più di una risposta corretta a questa domanda. Fai la tua scelta.


3

Puoi provare a raggruppare il tuo parametro in multipli strutture / classi significative (se possibile).


2

In genere propendo per l'approccio struct - presumibilmente la maggior parte di questi parametri sono correlati in qualche modo e rappresentano lo stato di alcuni elementi rilevanti per il tuo metodo.

Se l'insieme di parametri non può essere trasformato in un oggetto significativo, è probabilmente un segno che Shnizsta facendo troppo e il refactoring dovrebbe comportare la scomposizione del metodo in questioni separate.


2

Puoi scambiare la complessità con le righe del codice sorgente. Se il metodo stesso fa troppo (coltellino svizzero) prova a dimezzare i suoi compiti creando un altro metodo. Se il metodo è semplice, richiede solo troppi parametri, i cosiddetti oggetti parametro sono la strada da percorrere.


2

Se la tua lingua lo supporta, usa i parametri denominati e rendine il maggior numero possibile facoltativo (con valori predefiniti ragionevoli).


1

Penso che il metodo che hai descritto sia la strada da percorrere. Quando trovo un metodo con molti parametri e / o uno che probabilmente ne avrà bisogno di più in futuro, di solito creo un oggetto ShnizParams da passare, come descrivi.


1

Che ne dici di non impostarlo tutto in una volta nei costruttori ma farlo tramite proprietà / setter ? Ho visto alcune classi .NET che utilizzano questo approccio come Processclass:

        Process p = new Process();

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.FileName = "cmd";
        p.StartInfo.Arguments = "/c dir";
        p.Start();

3
C # 3 ha effettivamente una sintassi per farlo facilmente: inizializzatori di oggetti.
Daren Thomas,

1

Concordo con l'approccio di spostare i parametri in un oggetto parametro (struct). Piuttosto che incollarli tutti in un oggetto, controlla se altre funzioni utilizzano gruppi di parametri simili. Un oggetto paramater è più prezioso se viene utilizzato con più funzioni in cui ci si aspetta che quel set di parametri cambi in modo coerente tra quelle funzioni. È possibile che tu inserisca solo alcuni dei parametri nel nuovo oggetto parametro.


1

Se hai tanti parametri, è probabile che il metodo stia facendo troppo, quindi risolvi questo problema prima suddividendo il metodo in diversi metodi più piccoli. Se hai ancora troppi parametri dopo questo, prova a raggruppare gli argomenti oa trasformare alcuni parametri in membri di istanza.

Preferisci classi / metodi piccoli rispetto a quelli grandi. Ricorda il principio della responsabilità unica.


Il problema con i membri e le proprietà dell'istanza è che 1) devono essere scrivibili, 2) potrebbero non essere impostati. Nel caso di un costruttore, ci sono alcuni campi che voglio garantire siano riempiti prima che un'istanza possa esistere.
ricorsivo

@ ricorsivo - Non sono d'accordo sul fatto che i campi / proprietà debbano essere sempre scrivibili. Per le classi piccole ci sono molte volte in cui i membri di sola lettura hanno senso.
Brian Rasmussen

1

Gli argomenti con nome sono una buona opzione (presumendo un linguaggio che li supporti) per disambiguare elenchi di parametri lunghi (o anche brevi!) Consentendo anche (nel caso dei costruttori) che le proprietà della classe siano immutabili senza imporre un requisito per consentirne l'esistenza in uno stato parzialmente costruito.

L'altra opzione che cercherò in questo tipo di refactoring sarebbe costituita da gruppi di parametri correlati che potrebbero essere gestiti meglio come un oggetto indipendente. Usando la classe Rectangle da una risposta precedente come esempio, il costruttore che accetta parametri per x, y, height e width potrebbe fattorizzare xey in un oggetto Point, consentendo di passare tre parametri al costruttore del Rectangle. Oppure vai un po 'oltre e impostalo su due parametri (UpperLeftPoint, LowerRightPoint), ma sarebbe un refactoring più radicale.


0

Dipende dal tipo di argomenti che hai, ma se sono molti valori / opzioni booleani potresti usare un Flag Enum?


0

Penso che quel problema sia profondamente legato all'ambito del problema che stai cercando di risolvere con la classe.

In alcuni casi, un costruttore a 7 parametri può indicare una cattiva gerarchia di classi: in tal caso, la struttura / classe helper suggerita sopra è di solito un buon approccio, ma poi si tende anche a finire con carichi di strutture che sono solo borse di proprietà e non fare nulla di utile. Il costruttore a 8 argomenti potrebbe anche indicare che la tua classe è troppo generica / troppo versatile, quindi ha bisogno di molte opzioni per essere davvero utile. In questo caso puoi effettuare il refactoring della classe o implementare costruttori statici che nascondono i costruttori complessi reali: es. Shniz.NewBaz (foo, bar) potrebbe effettivamente chiamare il vero costruttore passando i parametri giusti.


0

Una considerazione è quale dei valori sarebbe di sola lettura una volta creato l'oggetto?

Le proprietà scrivibili pubblicamente potrebbero forse essere assegnate dopo la costruzione.

Alla fine da dove vengono i valori? Forse alcuni valori sono veramente esterni mentre altri provengono davvero da una configurazione o da dati globali mantenuti dalla libreria.

In questo caso potresti nascondere il costruttore dall'uso esterno e fornire una funzione Crea per esso. La funzione create prende i valori veramente esterni e costruisce l'oggetto, quindi utilizza le funzioni di accesso disponibili solo per la libreria per completare la creazione dell'oggetto.

Sarebbe davvero strano avere un oggetto che richiede 7 o più parametri per dare all'oggetto uno stato completo e tutti veramente esterni in natura.


0

Quando una classe ha un costruttore che richiede troppi argomenti, di solito è un segno che ha troppe responsabilità. Probabilmente può essere suddiviso in classi separate che cooperano per fornire le stesse funzionalità.

Nel caso in cui hai davvero bisogno di tanti argomenti per un costruttore, il pattern Builder può aiutarti. L'obiettivo è comunque passare tutti gli argomenti al costruttore, quindi il suo stato viene inizializzato dall'inizio e puoi comunque rendere la classe immutabile se necessario.

Vedi sotto :

public class Toto {
    private final String state0;
    private final String state1;
    private final String state2;
    private final String state3;

    public Toto(String arg0, String arg1, String arg2, String arg3) {
        this.state0 = arg0;
        this.state1 = arg1;
        this.state2 = arg2;
        this.state3 = arg3;
    }

    public static class TotoBuilder {
        private String arg0;
        private String arg1;
        private String arg2;
        private String arg3;

        public TotoBuilder addArg0(String arg) {
            this.arg0 = arg;
            return this;
        }
        public TotoBuilder addArg1(String arg) {
            this.arg1 = arg;
            return this;
        }
        public TotoBuilder addArg2(String arg) {
            this.arg2 = arg;
            return this;
        }
        public TotoBuilder addArg3(String arg) {
            this.arg3 = arg;
            return this;
        }

        public Toto newInstance() {
            // maybe add some validation ...
            return new Toto(this.arg0, this.arg1, this.arg2, this.arg3);
        }
    }

    public static void main(String[] args) {
        Toto toto = new TotoBuilder()
            .addArg0("0")
            .addArg1("1")
            .addArg2("2")
            .addArg3("3")
            .newInstance();
    }

}
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.