2009-05-14 10 views
6

così ho preso in una discussione con un collega nel corso di un pezzo di codice:Dovrei usare un campo o la proprietà all'interno della classe per impostare i valori

public sealed class NewObject 
{ 
    private string _stuff = string.Empty; 

    public string Stuff 
    { 
     get { return GetAllStuff(); } 
    } 

    private string GetAllStuff() 
    { 
     //Heavy string manipulation of _stuff 
    } 

    public NewObject(string stuffToStartWith) 
    { 
     _stuff = stuffToStartWith; 
    } 

    public static NewObject operator +(NewObject obj1, NewObject obj2) 
    { 
     if (obj1 == null) 
      throw new ArgumentNullException(); 

     if (obj2 == null) 
      throw new ArgumentNullException(); 

     NewObject result = new NewObject(string.Empty); 
     result._stuff = String.Concat(obj1._stuff, obj2._stuff); 

     return result; 
    } 
} 

L'argomento era finita l'override dell'operatore. Il mio collaboratore ritiene che non sia la migliore pratica di programmazione per impostare i valori dei campi privati ​​ovunque tranne il costruttore. La soluzione proposta dal mio collega di lavoro era quella di refactoring il nome della proprietà Stuff a AllStuff e aggiungere una proprietà, Stuff, che ha un accesso get E set e utilizzare la nuova proprietà Stuff nell'override dell'operatore. Assomigli a questo:

public static NewObject operator +(NewObject obj1, NewObject obj2) 
    { 
     if (obj1 == null) 
      throw new ArgumentNullException(); 

     if (obj2 == null) 
      throw new ArgumentNullException(); 

     NewObject result = new NewObject(string.Empty); 
     result.Stuff = String.Concat(obj1.Stuff, obj2.Stuff); 

     return result; 
    } 

Non sono d'accordo. Sento che il primo modo è migliore poiché mantiene la proprietà di sola lettura al di fuori della classe. La mia domanda è, qual è la migliore pratica per la progettazione orientata agli oggetti?

risposta

7

Si potrebbe darsi un private set sulla proprietà (che manterrebbe la visibilità o la mancanza di esso pur consentendo di utilizzare la sintassi della proprietà), ma che in realtà non indirizza il punto.

All'interno della classe, dico che le variabili sono un gioco leale. Ovunque al di fuori, comprese le classi ereditate, dovrebbe get e set la proprietà, ma all'interno della classe dichiarante dico che è OK assegnare il membro privato.

2

Hai ragione

err ... per elaborare, le variabili private sono il vostro da fare come ti pare. Se qualcuno esegue un'operazione su di te che modifica il valore dell'oggetto, (in particolare qualcosa come +), non c'è niente di sbagliato nel modificare il valore al di fuori del costruttore. Questo è il loro punto di vista privato.

A meno che non si vuole che immutabile ...

Aggiornamento
Più ci penso, più mi ritieni che il tuo collega di lavoro è fonte di confusione variabili 'private' con 'costante' quelli - o forse unendo i due concetti. Non c'è motivo per cui le variabili private debbano rimanere uguali per tutta la durata dell'oggetto, che è ciò che il tuo amico sembra implicare. const è per immutabile, privato è solo per l'oggetto, sono due modelli molto distinti.

Update2
Inoltre, il suo progetto va in pezzi se improvvisamente l'oggetto ha più di una semplice stringa - e le variabili si intrecciano (pensare a un oggetto stringa, che ha un char * e len, e deve essere mantenuto insieme). L'ultima cosa che vuoi è che l'utente abbia a che fare con le variabili interne di un oggetto. Lascia che l'oggetto sia un oggetto e mantenga i propri valori interni e presenti una singola entità all'utente.

+0

@cyber: Penso che questo sia più una questione dell'OP non essere del tutto chiaro. Mi sembrava che il suo collaboratore sostenesse di non modificare una variabile privata direttamente al di fuori del costruttore o dell'implementazione della proprietà. Non penso che sostenesse di mantenere i valori uguali per la vita dell'oggetto. –

1

Non vedo quale sarebbe il beneficio del suo approccio.

0

io personalmente preferisco avere nessun campo a tutti, quindi io uso auto-realizzato private proprietà invece di private campi e public-getprivate-set proprietà, se vuole avere public proprietà di sola lettura.
Se devo aggiungere il codice alla proprietà, utilizzo ancora il campo all'interno degli accessors della proprietà e uso i getter e setter ovunque, incluso il costruttore.
Devo usare anche i campi, se ho bisogno dei campi readonly, ma C# 4.0 introdurrà proprietà di sola lettura.


Inoltre avrei evitato l'intero problema utilizzando il seguente codice.

public static NewObject operator +(NewObject obj1, NewObject obj2) 
{ 
    return new NewObject(String.Concat(obj1.Stuff, obj2.Stuff)); 
} 

mia realizzazione preferita sarebbe qualcosa di simile.

public sealed class NewObject 
{ 
    private String Stuff { get; set; } 

    // Use a method instead of a property because the operation is heavy.  
    public String GetAllStuff() 
    { 
     // Heavy string manipulation of this.Stuff. 
     return this.Stuff; 
    } 

    // Or lets use a property because this.GetAllStuff() is not to heavy. 
    public String AllStuff 
    { 
     get { return this.GetAllStuff(); } 
    } 

    public NewObject(String stuffToStartWith) 
    { 
     this.Stuff = stuffToStartWith; 
    } 

    public static NewObject operator +(NewObject obj1, NewObject obj2) 
    { 
     // Error handling goes here. 

     return new NewObject(String.Concat(obj1.Stuff, obj2.Stuff); 
    } 
} 
+0

Sono frainteso cosa intendi per "proprietà di sola lettura"? In questo momento posso codificare una proprietà senza un setter, rendendola di sola lettura ... –

2

La questione generale ha a che fare con una politica di contratto.

La nozione di una proprietà (set pubblico) è che quando viene chiamata, altre azioni possono essere adottate in aggiunta alla nozione semantica di cambiamento di stato. Ad esempio, chiamare un setter può far scattare eventi, attivare un dispositivo periferico e così via.

Il tuo collega sta dicendo che non usando la proprietà, stai facendo un passo indietro al contratto e nessun evento verrà licenziato.

Quindi, ecco che si dovrebbe fare dal punto di vista del vostro collega:

this.Prop = CalculateSomeValue(); 
if (this.Prop < kPropMin) { 
    this.Prop = kPropMin; 
} 
else if (this.Prop > kPropMax * 2) { 
    this.Prop = kPropMax * 2; 
} 
this.Prop = this.Prop/2; 

Ora, questo è un caso artificiosa, ma ho appena colpito un possibile immobile peso massimo fino a tre volte in get e fino tre volte nel set, e uno di quelli potrebbe essere illegale (impostando su kHighLimit/2). Posso aggirare questo problema usando un locale e chiamando il set precisamente una volta alla fine. Preferirei solo scherzare con il campo, però.

Penso che un approccio migliore sia pragmatico: usa la proprietà all'interno della classe se e solo se vuoi invocare tutti gli effetti collaterali di un set o di un get, altrimenti obbedisci allo spirito della proprietà.

- chiarimenti - Con obbedire allo spirito della proprietà, diciamo che la mia proprietà set è simile al seguente:

bool PropValueOutOfRange(int val) { 
    return val < kPropMin || val > kPropMax; 
} 

public int Prop { 
    set { 
     if (PropValueOutOfRange(value)) 
      throw new ArgumentOutOfRangeException("value"); 
     if (PropValueConflictsWithInternalState(value)) 
      throw new ArgumentException("value"); 
     _prop = value; 
     NotifyPeriperalOfPropChange(_prop); 
     FirePropChangedEvent(/* whatever args might be needed */); 
    } 
} 

In questo ho scomposto fuori un sacco di dettagli grungy, ma che mi permette di riutilizzarli. Quindi ora sono fiducioso nel toccare il campo privato _prop perché ho la stessa infrastruttura per essere sicuro di mantenerlo a portata di mano e di avvisare la periferica e licenziare l'evento.

Questo mi permette di scrivere questo codice:

_prop = CalculateSomeValue(); 

if (_prop < kPropMin) 
    _prop = kPropMin; 
else if (_prop > kPropMax * 2) 
    _prop = kPropMax; 

_prop /= 2; 

NotifyPeripheralOfPropChange(); 
FirePropChangedEvent(); 

sto usando gli stessi strumenti come quelli usati per costruire la proprietà così sto lavorando nello spirito della proprietà. Mantengo il range corretto (ma non lancio - lo so meglio, sono l'implementatore), colpisco gli eventi periferici e antincendio, e lo faccio in modo ponderato, leggibile ed efficiente - non indiscriminatamente.

+0

+1 perché dopo aver letto questo penso che questo sia probabilmente il processo di pensiero del mio collega. Tuttavia, ai fini della contestazione del codice, la proprietà non dovrebbe mai essere modificata al di fuori della classe per nessun motivo. –

+0

Domanda: Nel tuo approccio pragmatico, quando dici di "obbedire allo spirito della proprietà" intendi che se la proprietà non dovesse mai cambiare al di fuori della classe, allora usa il campo per aggiornare come sto facendo sopra? –

Problemi correlati