In java, cosa significa esporre il rappresentante?


"Esporre il rappresentante" di un oggetto può essere causato da quale delle seguenti:

  1. Non dichiarare le variabili nella sua classe come private.
  2. Consentire a una variabile nell'oggetto di fare riferimento a un oggetto passato a come argomento per il suo costruttore.
  3. Consentire a un riferimento memorizzato in una variabile nell'oggetto di essere passato come valore restituito di un metodo chiamato su quell'oggetto.
  4. 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

Author: ruakh, 2014-03-19

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);
    }
 2
Author: Brandon, 2014-03-20 01:08:45