Question

Je suis très curieux de la possibilité de fournir l’immuabilité pour les haricots java (par haricots, j’entends des classes avec un constructeur vide fournissant des getters et des setters aux membres). Il est clair que ces classes ne sont pas immuables et qu’elles sont utilisées pour transporter des valeurs de la couche de données, cela semble être un réel problème.

Dans StackOverflow, une approche de ce problème a été mentionnée sous le nom "Modèle d'objet immuable en C #". où l'objet est gelé une fois entièrement construit. J'ai une approche alternative et j'aimerais vraiment connaître l'opinion des gens à ce sujet.

Le modèle implique deux classes Immutable et Mutable où Mutable et Immutable implémentent une interface fournissant des méthodes de bean non mutantes.

Par exemple

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;
    }
}

Cette approche permet de construire le haricot en utilisant la réflexion (selon les conventions habituelles) et de convertir une variante immuable à la prochaine occasion. Malheureusement, il y a clairement une grande quantité de bouillie par haricot.

Je suis très intéressé d'entendre l'approche d'autres personnes sur ce problème. (Mes excuses pour ne pas fournir une bonne question, à laquelle on peut répondre plutôt que discuter:)

Était-ce utile?

La solution

Je pense que j'utiliserais le modèle de délégation - créer une classe ImmutableDate avec un seul membre DateBean à spécifier dans le constructeur:

public class ImmutableDate implements DateBean
{
   private DateBean delegate;

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

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

Si jamais j'ai besoin de forcer l'immuabilité sur un DateBean d, je viens juste d'ajouter ImmutableDate (d) dessus. J'aurais pu être intelligent et m'assurer de ne pas déléguer le délégué, mais vous voyez l'idée. Cela évite qu'un client essaie de le transformer en quelque chose de mutable. C’est un peu comme le JDK le fait avec Collections.unmodifiableMap (), etc. (dans ce cas, les fonctions de mutation doivent encore être implémentées et sont codées pour renvoyer une exception d’exécution. Beaucoup plus facile si vous avez une interface de base sans le mutateurs).

Encore une fois, c’est un code passe-partout fastidieux, mais c’est le genre de chose qu’un bon IDE comme Eclipse peut générer automatiquement pour vous en quelques clics de souris.

Si c’est le genre de choses que vous faites en fin de compte pour un grand nombre d’objets de domaine, vous pouvez envisager d’utiliser des proxys dynamiques ou peut-être même des AOP. Il serait alors relativement facile de créer un proxy pour n’importe quel objet, de déléguer toutes les méthodes get et de piéger ou d’ignorer les méthodes définies, le cas échéant.

Autres conseils

Quelques commentaires (pas nécessairement des problèmes):

  1. La classe Date est elle-même modifiable, vous la copiez donc correctement pour protéger l’immuabilité. Personnellement, je préfère convertir en long dans le constructeur et renvoyer une nouvelle Date (longValue) dans le getter.
  2. Vos deux méthodes getWitelyInstance () renvoient DateBean, ce qui nécessitera un transtypage; il peut être judicieux de modifier l'interface pour renvoyer le type spécifique à la place.
  3. Cela dit, je serais enclin à n'avoir que deux classes, une mutable et une immuable, partageant une interface commune (c'est-à-dire seulement obtenir) le cas échéant. Si vous pensez qu'il y aura beaucoup de conversions, ajoutez un constructeur de copie aux deux classes.
  4. Je préfère que les classes immuables déclarent les champs comme final pour que le compilateur impose également l'immuabilité.

par exemple

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();
    }

}

J'utilise des interfaces et des transtypages pour contrôler la mutabilité des beans. Je ne vois pas de bonne raison de compliquer mes objets de domaine avec des méthodes telles que getImmutableInstance () et getMutableInstance () .

Pourquoi ne pas simplement utiliser l'héritage et l'abstraction? par exemple

public interface User{

  long getId();

  String getName();

  int getAge();

}

public interface MutableUser extends User{

  void setName(String name);

  void setAge(int age);

}

Voici ce que le client du code fera:

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

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

Cela répond-il à votre question?

yc

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top