Pregunta

Tengo mucha curiosidad acerca de la posibilidad de proporcionar inmutabilidad para los Java Beans (por beans aquí me refiero a las clases con un constructor vacío que proporciona getters y setters para los miembros). Claramente, estas clases no son inmutables y donde se usan para transportar valores desde la capa de datos esto parece un problema real.

Aquí se ha mencionado un enfoque para este problema en StackOverflow llamado "Patrón de objeto inmutable en C #". donde el objeto se congela una vez completamente construido. Tengo un enfoque alternativo y realmente me gustaría escuchar las opiniones de la gente al respecto.

El patrón involucra dos clases Immutable y Mutable donde Mutable e Immutable implementan una interfaz que proporciona métodos de bean no mutantes.

Por ejemplo

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

Este enfoque permite construir el bean utilizando la reflexión (según las convenciones habituales) y también nos permite convertir a una variante inmutable en la oportunidad más cercana. Desafortunadamente, hay claramente una gran cantidad de repetitivo por grano.

Estoy muy interesado en escuchar el enfoque de otras personas sobre este tema. (Mis disculpas por no proporcionar una buena pregunta, que puede responderse en lugar de discutirse :)

¿Fue útil?

Solución

Creo que usaría el patrón de delegación: crea una clase ImmutableDate con un solo miembro DateBean que debe especificarse en el constructor:

public class ImmutableDate implements DateBean
{
   private DateBean delegate;

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

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

Si alguna vez necesito forzar la inmutabilidad en un DateBean d, simplemente tengo una nueva ImmutableDate (d) en él. Podría haber sido inteligente y asegurarme de no delegar al delegado, pero se entiende la idea. Eso evita el problema de que un cliente intente convertirlo en algo mutable. Esto es muy similar a lo que hace JDK con Collections.unmodifiableMap () etc. (en esos casos, sin embargo, las funciones de mutación aún deben implementarse y están codificadas para generar una excepción de tiempo de ejecución. Mucho más fácil si tiene una interfaz base sin el mutadores).

Una vez más, es un código repetitivo tedioso, pero es el tipo de cosas que un buen IDE como Eclipse puede generar automáticamente para usted con solo unos pocos clics del mouse.

Si es el tipo de cosas que terminas haciendo con muchos objetos de dominio, es posible que desees considerar usar proxys dinámicos o incluso AOP. Sería relativamente fácil construir un proxy para cualquier objeto, delegando todos los métodos get y atrapando o ignorando los métodos establecidos según corresponda.

Otros consejos

Algunos comentarios (no necesariamente problemas):

  1. La clase Date es en sí mutable, por lo que la está copiando correctamente para proteger la inmutabilidad, pero personalmente prefiero convertir a long en el constructor y devolver una nueva Date (longValue) en el getter.
  2. Ambos métodos getWhateverInstance () devuelven DateBean, lo que requerirá conversión, podría ser una idea cambiar la interfaz para devolver el tipo específico en su lugar.
  3. Dicho todo esto, me inclinaría a tener dos clases, una mutable y otra inmutable, compartiendo una interfaz común (es decir, solo obtener) si es apropiado. Si cree que habrá una gran cantidad de conversiones, agregue un constructor de copia a ambas clases.
  4. Prefiero clases inmutables para declarar campos como final para que el compilador también imponga la inmutabilidad.

por ejemplo

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

}

Utilizo interfaces y casting para controlar la mutabilidad de los beans. No veo una buena razón para complicar mis objetos de dominio con métodos como getImmutableInstance () y getMutableInstance () .

¿Por qué no solo hacer uso de la herencia y la abstracción? por ejemplo,

public interface User{

  long getId();

  String getName();

  int getAge();

}

public interface MutableUser extends User{

  void setName(String name);

  void setAge(int age);

}

Esto es lo que hará el cliente del código:

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

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

¿Responde tu pregunta?

yc

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top