Best practice per consentire la mutazione di un campo di tipo Collection

2

Supponiamo di avere una classe (stile DTO) con un campo che è una lista:

public class MyClass {
    private List<String> stuff = new LinkedList<>();
}

Ci sono alcune scelte per consentire la mutazione del campo:

// provide only a setter that *replaces* the list with the contents of the parameter
public void setStuff(List<String> list) {
    stuff = new LinkedList<>(list); // safe copy, or clear() + addAll(), whatever
}

// provide only a getter, allowing direct mutation
public List<String> getStuff() {
    return stuff; // deliberately allowing getStuff().add("foo") etc
}

// provide delegating mutator methods for allowed mutations, eg
public void addStuff(String s) {
    stuff.add(s);
}
// and removeStuff(String), clearStuff(), etc as actual need arises

Supponendo che i soli chiamanti siano (fidati) dello stesso codice di progetto, che è l'approccio "migliore"?

    
posta Bohemian 03.06.2015 - 05:36
fonte

1 risposta

3

La decisione è tra l'offrire i propri metodi (proxy) o la restituzione di un oggetto interno che deve essere mutato dal chiamante. A meno che le prestazioni non siano il singolo obiettivo più importante della tua architettura, la legge di Demeter suggerirebbe strongmente di implementare metodi proxy:

  • Più corretto: un metodo proxy è il luogo ideale per eseguire la convalida che altrimenti dovrebbe essere implicita e diffusa nell'intero codice base. Non facendo filtrare un riferimento allo stato interno, si preclude la possibilità di rendere questo stato invalido. setStuff e addStuff fanno bene, ma getStuff no.

  • Meno accoppiati: i tipi di ritorno e argomento di un metodo diventano parte dell'interfaccia di quel metodo. Pertanto questi tipi dovrebbero essere il più possibile ristretti e generali. Prova sensibilmente a farlo esponendo l'interfaccia List e non un tipo di classe specifico.

Nel contesto delle DTO - che sono davvero solo contenitori di serializzazione - la maggior parte di questa discussione non ha importanza. Non c'è stato interno da proteggere, c'è solo un valore. Tuttavia, una volta costruito non c'è motivo di mutare un DTO. Con questo argomento, un DTO in uscita necessita solo di un supporto per la costruzione e la serializzazione, mentre un DTO in arrivo necessita solo di supporto per la serializzazione e metodi getter. A meno che non sia usato dal meccanismo di serializzazione, questo non ha bisogno di mutatori o setter, così da poter favorire un design immutabile.

In particolare, avremmo l'equivalente di setStuff nel costruttore e un getStuff accessor. Questa funzione di accesso in genere applica l'immutabilità, ma poiché la durata di un DTO termina dopo che i valori contenuti sono stati letti e i valori vengono letti solo in un punto per ciascun DTO, sarebbe sicuro utilizzare e modificare liberamente tali valori.

    
risposta data 03.06.2015 - 08:22
fonte

Leggi altre domande sui tag