Domanda

Sono molto curioso della possibilità di fornire immutabilità per i java bean (per bean qui intendo classi con un costruttore vuoto che fornisce getter e setter per i membri). Chiaramente queste classi non sono immutabili e dove vengono utilizzate per trasportare valori dal livello dati questo sembra un vero problema.

Un approccio a questo problema è stato menzionato qui in StackOverflow chiamato "modello a oggetti immutabili in C #" dove l'oggetto viene congelato una volta creato completamente. Ho un approccio alternativo e vorrei davvero sentire le opinioni delle persone su di esso.

Il modello coinvolge due classi Immutable e Mutable dove Mutable e Immutable implementano entrambe un'interfaccia che fornisce metodi bean non mutanti.

Ad esempio

public interface DateBean {
    public Date getDate();
    public DateBean getImmutableInstance();
    public DateBean getMutableInstance();
}

public class ImmutableDate implements DateBean {
    private Date date;

ImmutableDate(Date date) {
    this.date = new Date(date.getTime());
}

public Date getDate() {
    return new Date(date.getTime());
}

    public DateBean getImmutableInstance() {
        return this;
    }

    public DateBean getMutableInstance() {
        MutableDate dateBean = new MutableDate();
        dateBean.setDate(getDate());
        return dateBean;
    }
}

public class MutableDate implements DateBean {
    private Date date;

public Date getDate() {
    return date;
}

public void setDate(Date date) {
    this.date = date;
}

public DateBean getImmutableInstance() {
    return new ImmutableDate(this.date);
}

    public DateBean getMutableInstance() {
        MutableDate dateBean = new MutableDate();
        dateBean.setDate(getDate());
        return dateBean;
    }
}

Questo approccio consente di costruire il bean usando la riflessione (secondo le consuete convenzioni) e ci consente anche di convertirci in una variante immutabile alla più vicina opportunità. Sfortunatamente esiste chiaramente una grande quantità di boilerplate per bean.

Sono molto interessato a sentire l'approccio di altre persone a questo problema. (Mi scuso per non aver fornito una buona domanda, alla quale si può rispondere anziché discutere :)

È stato utile?

Soluzione

Penso che userei il modello di delega - creare una classe ImmutableDate con un singolo membro DateBean che deve essere specificato nel costruttore:

public class ImmutableDate implements DateBean
{
   private DateBean delegate;

   public ImmutableDate(DateBean d)
   {
      this.delegate = d;
   }

   public Date getDate()
   {
      return delegate.getDate();
   }
}

Se mai dovessi forzare l'immutabilità su un DateBean d, ho appena immutato la nuova ImmutableDate (d) su di esso. Avrei potuto essere intelligente e assicurarmi di non delegare il delegato, ma tu hai avuto l'idea. Ciò evita il problema di un client che tenta di inserirlo in qualcosa di mutabile. Questo è molto simile a JDK con Collections.unmodifiableMap () ecc. (In quei casi, tuttavia, le funzioni di mutazione devono ancora essere implementate e sono codificate per generare un'eccezione di runtime. Molto più semplice se si dispone di un'interfaccia di base senza il mutatori).

Ancora una volta è un noioso codice boilerplate ma è il genere di cose che un buon IDE come Eclipse può generare automaticamente per te con pochi clic del mouse.

Se è il genere di cose che finisci per fare a molti oggetti di dominio, potresti prendere in considerazione l'uso di proxy dinamici o forse anche AOP. Sarebbe relativamente semplice quindi creare un proxy per qualsiasi oggetto, delegando tutti i metodi get e intercettando o ignorando i metodi impostati come appropriato.

Altri suggerimenti

Alcuni commenti (non necessariamente problemi):

  1. La classe Date è essa stessa mutabile, quindi la stai copiando correttamente per proteggere l'immutabilità, ma personalmente preferisco convertirla in long nel costruttore e restituire una nuova data (longValue) nel getter.
  2. Entrambi i tuoi metodi getWhateverInstance () restituiscono DateBean che richiederà il cast, potrebbe essere un'idea cambiare l'interfaccia per restituire il tipo specifico.
  3. Detto tutto ciò che sarei propenso ad avere solo due classi, una mutabile e una immutabile, condividendo un'interfaccia comune (cioè solo get) se appropriato. Se pensi che ci saranno molte conversioni avanti e indietro, aggiungi un costruttore di copie ad entrambe le classi.
  4. Preferisco le classi immutabili per dichiarare i campi come final per fare in modo che anche il compilatore imponga l'immutabilità.

per es.

public interface DateBean {
    public Date getDate();
}

public class ImmutableDate implements DateBean {
    private final long date;

    ImmutableDate(long date) {
       this.date = date;
    }

    ImmutableDate(Date date) {
       this(date.getTime());
    }

    ImmutableDate(DateBean bean) {
       this(bean.getDate());
    }

    public Date getDate() {
         return new Date(date);
    }
}


public class MutableDate implements DateBean {
    private long date;

    MutableDate() {}

    MutableDate(long date) {
       this.date = date;
    }

    MutableDate(Date date) {
       this(date.getTime());
    }

    MutableDate(DateBean bean) {
       this(bean.getDate());
    }

    public Date getDate() {
        return new Date(date);
    }

    public void setDate(Date date) {
        this.date = date.getTime();
    }

}

Uso interfacce e casting per controllare la mutabilità dei bean. Non vedo una buona ragione per complicare i miei oggetti di dominio con metodi come getImmutableInstance () e getMutableInstance () .

Perché non usare solo l'eredità e l'astrazione? per es.

public interface User{

  long getId();

  String getName();

  int getAge();

}

public interface MutableUser extends User{

  void setName(String name);

  void setAge(int age);

}

Ecco cosa farà il client del codice:

public void validateUser(User user){
  if(user.getName() == null) ...
}

public void updateUserAge(MutableUser user, int age){
  user.setAge(age);
}

Risponde alla tua domanda?

YC

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top