2010-10-20 10 views
5

Oggi ho letto un libro e l'autore ha scritto che in una classe ben progettata l'unico modo per accedere agli attributi è attraverso uno di quei metodi di classe. È un pensiero ampiamente accettato? Perché è così importante incapsulare gli attributi? Quali potrebbero essere le conseguenze di non farlo? Ho letto da qualche parte prima che questo migliora la sicurezza o qualcosa del genere. Qualsiasi esempio in PHP o Java sarebbe molto utile.Incapsulamento. Classe ben progettata

risposta

10

È un pensiero ampiamente accettato?

Nel mondo orientato agli oggetti, sì.

Perché è così importante incapsulare gli attributi? Quali potrebbero essere le conseguenze di non farlo?

Gli oggetti sono intesi come entità coesive contenenti dati e comportamenti a cui altri oggetti possono accedere in modo controllato tramite un'interfaccia pubblica. Se una classe non incapsula i suoi dati e il suo comportamento, non ha più il controllo sui dati a cui si accede e non può adempiere ai suoi contratti con altri oggetti implicati dall'interfaccia pubblica.

Uno dei maggiori problemi con questo è che se una classe deve cambiare internamente, l'interfaccia pubblica non deve essere modificata. In questo modo non infrange alcun codice e altre classi possono continuare a usarlo come prima.

Qualsiasi esempio in PHP o Java sarebbe molto utile.

Ecco un esempio Java:

public class MyClass { 
    // Should not be < 0 
    public int importantValue; 
    ... 
    public void setImportantValue(int newValue) { 
     if (newValue < 0) { 
      throw new IllegalArgumentException("value cannot be < 0"); 
     } 
    } 
    ... 
} 

Il problema qui è che perché non ho incapsulato importantValue rendendola private piuttosto che pubblica, chiunque può venire avanti e aggirare il controllo ho messo in setter per impedire che l'oggetto abbia uno stato non valido. importantValue non dovrebbe mai essere inferiore a 0, ma la mancanza di incapsulamento rende impossibile impedirlo.

1

Le opinioni su come "un buon OOD" è raggiunto sono una dozzina, e anche programmatori e designer molto esperti tendono a non essere d'accordo sulle scelte di progettazione e sulle filosofie. Questo potrebbe essere un antipasto di guerra di fiamma, se chiedete alle persone attraverso un'ampia varietà di background e paradigmi linguistici.

E sì, in teoria la teoria e la pratica sono le stesse, quindi la scelta della lingua non dovrebbe influenzare molto il design di alto livello. Ma in pratica lo fanno, e cose buone e cattive accadono a causa di ciò.

Lasciatemi aggiungere: Dipende. L'incapsulamento (in un linguaggio di supporto) ti dà un po 'di controllo sul modo in cui le tue classi sono usate, quindi puoi dire a allo alle persone: questa è l'API e devi usarla. In altre lingue (ad es. Python) la differenza tra le interfacce API ufficiali e informali (soggetto a modifica) è solo mediante convenzione di denominazione (after all, we're all consenting adults here)

L'incapsulamento non è una funzione di sicurezza.

1

Un altro pensiero su cui riflettere

incapsulamento con funzioni di accesso fornisce anche molto migliore manutenibilità in futuro.Nella risposta di Feanor sopra, funziona perfettamente per rafforzare i controlli di sicurezza (supponendo che il tuo instvar sia privato), ma può avere molti vantaggi aggiuntivi.

Considerare il seguente scenario:
1) completare l'applicazione e distribuirla a un gruppo di utenti (interno, esterno, qualunque).
2) BigCustomerA si avvicina al proprio team e desidera una traccia di controllo aggiunta al prodotto.

Se tutti utilizzano i metodi di accesso nel codice, questo diventa quasi banale da implementare. Qualcosa in questo modo:

MyApi Versione 1,0

public class MyClass { 
    private int importantValue; 
    ... 
    public void setImportantValue(int newValue) { 
     if (newValue < 0) { 
      throw new IllegalArgumentException("value cannot be < 0"); 
     } 
     importantValue = newValue; 
    } 
    ... 
} 



V1.1 MyApi (ora con audit trail)

public class MyClass { 
    private int importantValue; 
    ... 
    public void setImportantValue(int newValue) { 
     if (newValue < 0) { 
      throw new IllegalArgumentException("value cannot be < 0"); 
     } 
     this.addAuditTrail("importantValue", importantValue, newValue); 
     importantValue = newValue; 
    } 
    ... 
} 

utenti esistenti di API non apportare alcuna modifica alla loro codice e la nuova funzionalità (audit trail) è ora disponibile.
Senza incapsulamento utilizzando le risorse di accesso di fronte a un enorme sforzo di migrazione.


Quando si scrive per la prima volta, sembrerà molto lavoro. È molto più veloce digitare: class.varName = something rispetto a class.setVarName(something); ma se tutti prendessero la via più semplice, farsi pagare per la richiesta di funzionalità di BigCustomerA sarebbe un enorme sforzo.

+1

YAGNI http://en.wikipedia.org/wiki/You_ain't_gonna_need_it – knitti

3

Quali potrebbero essere le conseguenze di non farlo ?

L'intera idea dietro l'incapsulamento è che tutta la conoscenza di qualsiasi cosa relativa alla classe (diversa dalla sua interfaccia) è all'interno della classe stessa. Ad esempio, consentire l'accesso diretto agli attributi comporta l'onere di assicurarsi che tutti i compiti siano validi sul codice che esegue l'assegnazione. Se la definizione di ciò che è valido cambia, devi passare e controllare tutto usando la classe per assicurarti che siano conformi. Incapsulare la regola in un metodo "setter" significa che devi solo cambiarla in un posto, e qualsiasi chiamante che prova qualcosa di divertente può ottenere in cambio un'eccezione generata. Ci sono molte altre cose che potresti voler fare quando un attributo cambia, e un setter è il posto giusto per farlo.

Se consentire o meno l'accesso diretto per attributi che non dispongono di regole per vincolarli (ad esempio, tutto ciò che rientra in un numero intero è corretto) è una buona pratica discutibile. Suppongo che l'utilizzo di getter e setter sia una buona idea per coerenza, ovvero che tu sappia sempre che è possibile chiamare setFoo() per modificare l'attributo foo senza dover cercare se è possibile farlo direttamente o meno. Inoltre ti permettono di rendere la tua lezione a prova di futuro in modo che se hai un codice aggiuntivo da eseguire, il posto dove metterlo è già lì.

Personalmente, penso che dover usare getter e setter sia goffo. Piuttosto preferisco scrivere x.foo = 34 rispetto a x.setFoo(34) e attendo con ansia il giorno in cui in alcuni linguaggi è disponibile l'equivalente di trigger di database per i membri che consentono di definire il codice che viene generato prima, dopo o al posto di un compito.

+0

Concordato sui setter. La maggior parte delle volte finisce per essere un sacco di confusione visiva. – Feanor

+1

Un linguaggio come, ad esempio, C# in cui è possibile utilizzare Proprietà invece di Campi e può avere codice che viene eseguito su ciò che sembra un compito normale ??? –

+0

Bene, imparo qualcosa di nuovo ogni giorno. Dovrò vedere che c'è una stanza nel mio cervello per una 17esima lingua. – Blrfl

1

In oggetto Oriente Programmazione c'è un principio che è noto come (http://en.wikipedia.org/wiki/Open/closed_principle): POC ->Principio di aperto e chiuso.Questo principio vale per: un design di classe well dovrebbe essere aperto per l'estensibilità (ereditarietà) ma chiuso per la modifica dei membri interni (incapsulamento). Significa che non potresti essere in grado di modificare lo stato di un oggetto senza averne cura.

Quindi, le nuove lingue modificano solo le variabili interne (campi) tramite le proprietà (metodi getter e setter in C++ o Java). Nelle proprietà C# compili ai metodi in MSIL.

C#:

int _myproperty = 0; 
public int MyProperty 
{ 
    get { return _myproperty; } 
    set { if (_someVarieble = someConstantValue) { _myproperty = value; } else { _myproperty = _someOtherValue; } }  
} 

C++/Java:

int _myproperty = 0; 
public void setMyProperty(int value) 
{ 
    if (value = someConstantValue) { _myproperty = value; } else { _myproperty = _someOtherValue; } 
} 
public int getMyProperty() 
{ 
    return _myproperty; 
} 
0

tesi prendere le idee (da Head First C#):

  • pensare a modi i campi possono abusato. Cosa può andare storto se non sono impostati correttamente.
  • È tutto nella tua classe pubblico? Passa un po 'di tempo a pensare all'incapsulamento.
  • Quali campi richiedono elaborazione o calcolo? Sono i primi candidati.
  • Rendi pubblici campi e metodi solo se è necessario. Se non hai un motivo per dichiarare qualcosa di pubblico, non farlo.
Problemi correlati