Avere getter e setter non interrompe di per sé l'incapsulamento. Ciò che impedisce l'incapsulamento è l'aggiunta automatica di un getter e un setter per ogni membro dei dati (ogni campo , in java lingo), senza dare alcun pensiero. Anche se è meglio che rendere pubblici tutti i membri dei dati, è solo un piccolo passo.
Il punto di incapsulamento non è che non dovresti essere in grado di sapere
o per cambiare lo stato dell'oggetto dall'esterno dell'oggetto, ma quello tu
dovrebbe avere una politica ragionevole per farlo.
-
Alcuni membri di dati potrebbero essere interamente interni all'oggetto e dovrebbero
non hanno né getter né setter.
-
Alcuni membri di dati dovrebbero essere di sola lettura, quindi potrebbero aver bisogno di getter ma
non setter.
-
Alcuni membri dei dati potrebbero dover essere coerenti tra loro. Nel
in questo caso non si fornisce un setter per ciascuno, ma un singolo
metodo per impostarli allo stesso tempo, in modo da poter controllare
valori per coerenza.
-
Alcuni membri dei dati possono solo essere modificati in un certo modo, ad esempio
come incrementato o decrementato di un importo fisso. In questo caso, tu
fornirebbe un metodo increment()
e / o decrement()
, piuttosto
di un setter.
-
Eppure altri potrebbero effettivamente aver bisogno di essere letti e scrivere e avere entrambi un getter
e un setter.
Considera un esempio di class Person
. Diciamo che una persona ha un nome, un numero di previdenza sociale e un'età. Diciamo che non permettiamo alle persone di cambiare mai il loro nome o il numero di previdenza sociale. Tuttavia, l'età della persona dovrebbe essere incrementata di 1 ogni anno. In questo caso, fornirebbe un costruttore che inizializzerà il nome e l'SSN ai valori specificati e che inizializzerà l'età a 0. Fornirai anche un metodo incrementAge()
, che aumenterebbe l'età di 1. Tu fornirebbe anche getter per tutti e tre. In questo caso non è richiesto alcun setter.
In questa progettazione si consente di controllare lo stato dell'oggetto dall'esterno della classe e di consentire che venga modificato dall'esterno della classe. Tuttavia, non si consente che lo stato venga modificato arbitrariamente. Esiste una politica che afferma in modo efficace che il nome e l'SSN non possono essere modificati affatto e che l'età può essere incrementata di 1 anno alla volta.
Ora diciamo che anche una persona ha uno stipendio. E le persone possono cambiare lavoro a volontà, il che significa che anche il loro stipendio cambierà. Per modellare questa situazione non abbiamo altro modo che fornire un metodo setSalary()
! Permettere che lo stipendio venga cambiato a piacimento è una politica perfettamente ragionevole in questo caso.
A proposito, nel tuo esempio, darei alla classe Fridge
i metodi putCheese()
e takeCheese()
, invece di get_cheese()
e set_cheese()
. Allora avresti ancora l'incapsulamento.
public class Fridge {
private List objects;
private Date warranty;
/** How the warranty is stored internally is a detail. */
public Fridge( Date warranty ) {
// The Fridge can set its internal warranty, but it is not re-exposed.
setWarranty( warranty );
}
/** Doesn't expose how the fridge knows it is empty. */
public boolean isEmpty() {
return getObjects().isEmpty();
}
/** When the fridge has no more room... */
public boolean isFull() {
}
/** Answers whether the given object will fit. */
public boolean canStore( Object o ) {
boolean result = false;
// Clients may not ask how much room remains in the fridge.
if( o instanceof PhysicalObject ) {
PhysicalObject po = (PhysicalObject)o;
// How the fridge determines its remaining usable volume is a detail.
// How a physical object determines whether it fits within a specified
// volume is also a detail.
result = po.isEnclosedBy( getUsableVolume() );
}
return result;
}
/** Doesn't expose how the fridge knows its warranty has expired. */
public boolean isPastWarranty() {
return getWarranty().before( new Date() );
}
/** Doesn't expose how objects are stored in the fridge. */
public synchronized void store( Object o ) {
validateExpiration( o );
// Can the object fit?
if( canStore( o ) ) {
getObjects().add( o );
}
else {
throw FridgeFullException( o );
}
}
/** Doesn't expose how objects are removed from the fridge. */
public synchronized void remove( Object o ) {
if( !getObjects().contains( o ) ) {
throw new ObjectNotFoundException( o );
}
getObjects().remove( o );
validateExpiration( o );
}
/** Lazily initialized list, an implementation detail. */
private synchronized List getObjects() {
if( this.list == null ) { this.list = new List(); }
return this.list;
}
/** How object expiration is determined is also a detail. */
private void validateExpiration( Object o ) {
// Objects can answer whether they have gone past a given
// expiration date. How each object "knows" it has expired
// is a detail. The Fridge might use a scanner and
// items might have embedded RFID chips. It's a detail hidden
// by proper encapsulation.
if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
throw new ExpiredObjectException( o );
}
}
/** This creates a copy of the warranty for immutability purposes. */
private void setWarranty( Date warranty ) {
assert warranty != null;
this.warranty = new Date( warranty.getTime() )
}
}