restituisce un oggetto senza restituire il riferimento

2

Ho qualche problema con la restituzione di oggetti e liste di oggetti. Per problema è voglio restituire un oggetto nel mio esempio uno sviluppatore ma non voglio dare il riferimento. Qual è l'approccio migliore per questo? Per prima cosa ho pensato di restituire un nuovo sviluppatore:

public Developer getDeveloper(int index){
    Developer d =  developers.get(index);
    return new Developer(d.getName());
}

Ma questo sembra buono per oggetti semplici, ma cosa succede se ho un oggetto sviluppatore che ha un campo istanza che è anche un oggetto o un elenco?

Un'altra domanda riguarda gli elenchi. Ricambio un elenco di sviluppatori:

public List<Developer> getDeveloppers(){
    return Collections.unmodifiableList(this.developers);
}

Quindi la lista ora non è modificabile, ma penso che gli oggetti non siano ... come posso restituire una lista con oggetti non modificabili?

Il codice di esempio:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Project {

    private List<Developer> developers;
    private String name;

    public Project(){
        this("DEFAULT");
    }

    public Project(String name){
        this.setName(name);
        this.developers = new ArrayList<Developer>();
    }

    public List<Developer> getDeveloppers(){
        return Collections.unmodifiableList(this.developers);
    }

    public Developer getDeveloper(int index){
        Developer d =  developers.get(index);
        return new Developer(d.getName());
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }   
}

La classe sviluppatore, nota che questo non ha oggetto istanza:

public class Developer {

    private String name;

    public Developer(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }
}

L'aiuto sarebbe apprezzato! Saluti

    
posta John 20.05.2015 - 10:30
fonte

2 risposte

4

Puoi rendere gli oggetti immutabili:

public class Developer {

    private final String name;

    public Developer(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }

}

Quindi non ci saranno problemi con la restituzione del riferimento.

Altrimenti puoi mappare ogni sviluppatore a una vista non modificabile di se stesso:

public Stream<IDeveloper> getDeveloppers(){
    return this.developers.map(dev => dev.unmoddifiableView());
}

Dove unmoddifiableView() restituisce un'istanza di IDeveloper che non consente modifiche:

private static IDeveloper readOnlyView = new UnmodifiableDeveloper(this);

public IDeveloper unmoddifiableView(){
    return readOnlyView;
}

E UnmodifiableDeveloper delega tutti i getter al parametro costruttore.

    
risposta data 20.05.2015 - 10:42
fonte
3

Non esiste un modo diretto per fare ciò che stai descrivendo. Tuttavia, hai alcune opzioni che si spera possano ottenere ciò che desideri:

  1. Avere un'interfaccia che contiene solo gli accessor e non i mutatori e assicurarsi che il tipo di ritorno sia dell'interfaccia piuttosto che della classe concreta.
  2. Avere un modo per clonare una classe - questo potrebbe essere un metodo esterno o un metodo .clone() sulla classe. Questo dovrebbe restituire una nuova istanza della classe, ma lo stato che rappresenta la stessa informazione (ad esempio name sarebbe lo stesso). Quindi, invece di restituire la classe originale, restituire una copia. Queste saranno comunque modificabili, ma le modifiche a queste istanze non influiranno sugli originali.
  3. Avere una seconda classe che rappresenta una "vista" immutabile sulla prima classe. Questo è ciò che cratchet freak descrive nella sua risposta. Di nuovo, avrai bisogno di un metodo che possa creare un'istanza della vista da un'istanza della classe originale.
  4. Progetta le tue classi in modo che non sia necessario.

Supponendo che queste siano le classi che controlli, 4 è la mia travolgente opzione preferita, e dovrebbe essere sicuramente il tuo primo obiettivo finché non hai una buona ragione per escluderlo. Alcuni motivi:

  • 1,2 e 3 richiedono tutti aggiunte fastidiose al codice (interfacce o metodi). Soprattutto con 2 e 3, se questo è qualcosa che fai molto per tutto il tuo codice, stai creando un lavoro di manutenzione significativo per te stesso.
  • Una classe dovrebbe essere responsabile del proprio incapsulamento. Stai capovolgendo quest'idea rendendo la classe aperta e facendola dipendere dalla gentilezza degli estranei per trovare il modo di passarla intorno, cosa che gli darà una certa protezione. Ora invece di avere questa responsabilità centralizzata in un posto che si prenderà cura di se stessa, ogni volta che usi questa lezione in un posto diverso, tu come sviluppatore devi ricordare queste regole extra su come usarlo e passarlo.
  • Un aspetto veramente utile dell'incapsulamento è che consente a una classe di formulare ipotesi sul suo stato, purché si assuma anche la responsabilità di mantenere vere queste ipotesi. Un esempio potrebbe essere una classe Fraction . Se questa classe gestisce il proprio stato incapsulato e si mantiene in una forma ridotta, il suo metodo equals può essere simile a:

    equals(Object obj) {
        return obj.numerator = numerator  && obj.denominator = denominator;
    }
    

    Se numerator e denominator non erano incapsulati, non poteva essere certo che fosse in forma ridotta e avrebbe dovuto usare una logica più complessa per assicurare che, per esempio 1/2 e 2/4 confrontato come uguale. Questo è chiamato invariante di classe. Spera solo che ogni volta che utilizzi il tuo corso ricorderai di proteggere i suoi invarianti, una via sicura per i bug e probabilmente per il codice orribilmente accoppiato. Questo è un altro aspetto del precedente punto elenco: stai estinguendo la responsabilità dell'incapsulamento se ti affidi a tutte le classi che usano questo per ricordare di chiamare .clone() o altro.

Se ti trovi in una situazione in cui hai per fare questo (oi costi di non farlo sono troppo alti), a parità di tutte le altre cose raccomando l'opzione 1, in quanto è un uso relativamente naturale delle interfacce.

    
risposta data 20.05.2015 - 14:48
fonte

Leggi altre domande sui tag