2010-03-05 6 views
5

Si consideri la seguente classemetodi di classe dovrebbe accettare parametri o proprietà della classe di uso

public class Class1 
{ 
    public int A { get; set; } 
    public int B { get; set; } 

    public int GetComplexResult() 
    { 
     return A + B; 
    } 
} 

Per poter utilizzare GetComplexResult, un consumatore di questa classe avrebbe dovuto sapere per impostare A e B prima di chiamare il metodo. Se GetComplexResult accede a molte proprietà per calcolare il suo risultato, questo può portare a valori di ritorno errati se il consumatore non imposta prima tutte le proprietà appropriate. Così si potrebbe scrivere questa classe come questo, invece

public class Class2 
{ 
    public int A { get; set; } 
    public int B { get; set; } 

    public int GetComplexResult(int a, int b) 
    { 
     return a + b; 
    } 
} 

In questo modo, un chiamante per GetComplexResult è costretto a passare in tutti i valori richiesti, garantendo il valore di rendimento atteso è correttamente calcolato. Ma se ci sono molti valori richiesti, anche l'elenco dei parametri cresce e questo non sembra nemmeno un buon progetto. Sembra anche rompere il punto di incapsulare A, B e GetComplexResult in una singola classe. Potrei anche essere tentato di rendere statico GetComplexResult poiché non richiede un'istanza della classe per fare il suo lavoro. Non voglio andare in giro a fare un sacco di metodi statici.

Ci sono termini per descrivere questi 2 diversi modi di creare classi? Entrambi sembrano avere pro e contro - c'è qualcosa che non capisco che dovrebbe dirmi che un modo è migliore dell'altro? In che modo i test unitari influenzano questa scelta?

risposta

5

Se si utilizza un esempio reale, la risposta diventa più chiara.

public class person 
{ 
    public string firstName { get; set; } 
    public string lastName { get; set; } 

    public string getFullName() 
    { 
     return firstName + " " + lastName; 
    } 
} 

Il punto di un oggetto entità è che contiene informazioni su un soggetto, e può fare le operazioni che il soggetto deve fare (sulla base delle informazioni che contiene). Quindi sì, ci sono situazioni in cui certe operazioni non funzioneranno correttamente perché l'entità non è stata completamente inizializzata, ma non è un fallimento del design. Se, nel mondo reale, ti chiedo il nome completo di un bambino appena nato che non è stato ancora nominato, anche questo fallirà.

Se alcune proprietà sono essenziali per un'entità che fa il proprio lavoro, possono essere inizializzate in un costruttore. Un altro approccio è quello di avere un valore booleano che controlla se l'entità è in uno stato in cui un dato metodo può essere chiamato:

while (person.hasAnotherQuestion()) { 
    person.answerNextQuestion(); 
} 
+0

Provo sempre a chiamare getter invece di accedere direttamente ai membri e nel registro immetto un messaggio se restituisco un valore nullo. Usavo le asserzioni, ma poi abbiamo scoperto che alcuni dei nostri clienti eseguivano affermazioni abilitate in produzione (!). – TMN

1

Una buona regola di progettazione è quello di assicurarsi che tutti i costruttori inizializza gli oggetti a stati validi e che tutti i metodi e i metodi di proprietà quindi applicano lo stato valido. In questo modo non ci saranno mai oggetti in stati non validi.

Se i valori di default per A e B, che è 0, non è uno stato valido che produce un risultato valido da GetComplexResult, si dovrebbe un costruttore che inizializza A e B ad uno stato valido.

0

Se alcuni dei campi non sono mai consentiti come null, in genere li si dovrebbe definire come parametri per il costruttore della classe. Se non si dispone sempre di tutti i valori richiesti contemporaneamente, utilizzare una classe di build può essere utile.

Ad esempio:

public Builder { 
    private int a; 
    private int b; 

    public Class1 create() { 
     // some validation logic goes here 
     // to make sure we have everything and 
     // either fill in defaults or throw an error 
     // if needed 
     return new Class1(a, b) 
    } 

    public Builder a(int val) { a = val; } 
    public Builder b(int val) { b = val; } 
} 

Questo Builder può quindi essere utilizzato come segue.

Class1 obj1 = new Builder().a(5).b(6).create(); 

Builder builder = new Builder(); 
// do stuff to find value of a 
builder.a(valueOfA); 
// do stuff to find value of b 
builder.b(valueOfB); 
// do more stuff 
Class1 obj2 = builder.create(); 
Class2 obj3 = builder.create(); 

Questo design permette di bloccare le classi di entità a qualsiasi livello è opportuno, pur consentendo un processo di costruzione flessibile. Inoltre, apre la porta alla personalizzazione del processo di costruzione con altre implementazioni senza modificare il contratto di classe di entità.

Problemi correlati