In java, cosa significa esporre il rappresentante?
"Esporre il rappresentante" di un oggetto può essere causato da quale delle seguenti:
- Non dichiarare le variabili nella sua classe come private.
- Consentire a una variabile nell'oggetto di fare riferimento a un oggetto passato a come argomento per il suo costruttore.
- Consentire a un riferimento memorizzato in una variabile nell'oggetto di essere passato come valore restituito di un metodo chiamato su quell'oggetto.
- Tutto quanto sopra.
So che la risposta è 4 ma non ne sono sicuro perché, la mia comprensione di ciò che espone il rappresentante non è completamente chiaro.
Modifica: per risposte chiare
1 answers
Non avevo mai sentito quel termine, quindi l'ho cercato su Google. Questo articolo lo spiegava abbastanza chiaramente:
Http://www.cs.newpaltz.edu / ~pletcha/oop_chap5_1.html
Esporre il rappresentante significa violare la regola che i metodi di un oggetto controllano il suo stato. Ad esempio, se un oggetto ha variabili di istanza e mutatori per modificare i loro valori, allora l'oggetto ha un mezzo per controllare il suo stato. Se chiamo foo.setBar(5)
, allora foo.getBar()
meglio restituire 5 se la documentazione dice getBar
restituisce il valore impostato da setBar
.
Spiegherò perché ognuna delle tre descrizioni che hai dato espone la rappresentazione di un oggetto (o più in generale, rompe l'incapsulamento):
Non dichiarare le variabili nella sua classe come private.
Questo è il più facile. Se le variabili di istanza sono pubbliche, qualsiasi cosa nella JVM può modificare i loro valori dall'esterno del codice all'interno dello stesso oggetto/classe. Se chiamiamo foo.setBar(5)
e poi foo.getBar()
, potremmo ottenere qualcosa di diverso di 5 perché bar
è un ambito pubblico, quindi un'altra area di codice potrebbe averlo mutato.
Consentire a una variabile nell'oggetto di fare riferimento a un oggetto passato come argomento al suo costruttore.
Questo mi ci è voluto un minuto per capire, ma ha senso se pensi a un oggetto e alle sue dipendenze come a una singola unità.
Se un Foo
ha un Bar
e Bar
ha int
chiamato x
, allora Foo
può vedere e controllare la proprietà x
su bar
perché ha un riferimento. Se creo un'istanza di Foo
e passo un riferimento a un'istanza di Bar
nel costruttore di Foo
, sembra che Foo
abbia il controllo completo. Ma non è così. Esempio:
public class Foo {
private Bar bar;
public Foo(Bar bar) {
this.bar = bar;
}
// immutable property - can only be read once this object is instantiated
public Bar getBar() {
return this.bar;
}
}
public class Bar {
private int x;
public Bar(int x) {
this.x = x;
}
public int getX() {
return this.x;
}
public void setX(int x) {
this.x = x;
}
}
// some other java class
Bar bar = new Bar(10);
Foo foo = new Foo(bar);
bar.setX(5);
Questo codice espone il rappresentante perché foo
ha assunto in modo critico che controlli bar
. Si noti che il suo riferimento a bar
è immutabile. Ma non è davvero immutabile. Solo il riferimento stesso è immutabile. Il codice che ha creato foo
ha ancora un riferimento a bar
e può mutarlo senza che foo
lo sappia.
Più semplicemente, foo
ha una dipendenza da bar
e la considera parte di se stessa. Ma bar
può effettivamente cambiare indipendentemente e quindi lo stato di foo
è cambiato indirettamente senza che lo sappia.
Consentire un riferimento memorizzato in una variabile nell'oggetto da passare come valore restituito di un metodo chiamato su quell'oggetto.
Questo è più facile da spiegare attraverso un raccolta.
public class Foo {
private Collection<Bar> bars = new ArrayList<Bar>();
// immutable property - can only be read once this object is instantiated
public Collection<Bar> getBars() {
return this.bars;
}
public void addBar(Bar bar) {
this.bars.add(bar);
}
public int getBarCount() {
return this.bars.size();
}
}
Foo foo = new Foo();
foo.getBars().add(new Bar(someUnexpectedBar));
System.out.println(foo.getBarCount()); // -> 1
Questo viola il contratto. Per aggiungere una barra, dovresti chiamare addBar
. Ecco perché il metodo è esposto. Restituendo un riferimento alla raccolta in getBars
, la raccolta sottostante può essere manipolata.
All'inizio sembra banale. Ma se Foo
rende questa regola e l'uso di cui sopra la viola, allora cosa accadrebbe se volessi refactoring Foo
a questo (diciamo per le prestazioni):
public class Foo {
private Collection<Bar> bars = new LinkedList<Bar>();
private int barCount; // for faster inserts, use a linked list and to maintain fast counts, track the count ourselves by tracking the adds.
// immutable property - can only be read once this object is instantiated
public Collection<Bar> getBars() {
return this.bars;
}
public void addBar(Bar bar) {
this.bars.add(bar);
this.barCount++;
}
public int getBarCount() {
return this.barCount;
}
}
Foo foo = new Foo();
foo.getBars().add(new Bar(someUnexpectedBar));
System.out.println(foo.getBarCount()); // -> 0
Funzionalmente, è identico. Se tu usa i metodi in modo appropriato, non ci sarebbe differenza. Ma abbiamo imbrogliato. Abbiamo preso la collezione sottostante e l'abbiamo mutata. Ora il metodo getBarCount()
restituisce la risposta sbagliata (0).
Il modo per aggirare questo è quello di restituire una nuova collezione con un clone dell'originale.
public Collection<Bar> getBars() {
return new ArrayList<Bar>(this.bars);
}
O anche
public Collection<Bar> getBars() {
return Collections.unmodifiableCollection(this.bars);
}