Pergunta

Estou muito curioso sobre a possibilidade de fornecer imutabilidade para Java Beans (por feijão aqui eu aulas médios com um construtor vazio fornecendo getters e setters para os membros). Claramente essas classes não são imutáveis ??e onde eles são usados ??para valores de transporte da camada de dados este parece ser um problema real.

Uma abordagem a este problema foi mencionado aqui no StackOverflow chamado "padrão de objeto imutável em C #" onde o objeto está congelado, uma vez completamente construído. Eu tenho uma abordagem alternativa e realmente gostaria de ouvir a opinião das pessoas sobre ele.

O padrão envolve duas classes imutáveis ??e mutáveis, onde mutável e imutável ambos implementam uma interface que fornece não-mutação métodos de bean.

Por exemplo

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

Esta abordagem permite que o feijão a ser construído usando a reflexão (pelas convenções usuais) e também nos permite converter para uma variante imutável na oportunidade mais próxima. Infelizmente, não existe claramente uma grande quantidade de boilerplate por bean.

Estou muito interessado em ouvir abordagem de outras pessoas para esta questão. (As minhas desculpas por não fornecer uma boa pergunta, que podem ser respondidas em vez de discutidos:)

Foi útil?

Solução

Eu acho que usar o padrão de delegação - fazer uma classe ImmutableDate com um único membro DateBean que deve ser especificado no construtor:

public class ImmutableDate implements DateBean
{
   private DateBean delegate;

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

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

Se alguma vez eu preciso força de imutabilidade em um d DateBean, eu só nova ImmutableDate (d) sobre ele. Eu poderia ter sido inteligente e fez com que eu não delegar o delegado, mas você começa a idéia. Isso evita o problema de um cliente tentando lançá-lo em algo mutável. Isto é muito parecido com o JDK faz com Collections.unmodifiableMap () etc. (nesses casos, no entanto, as funções de mutação ainda têm de ser implementadas, e são codificadas para lançar uma exceção tempo de execução. Muito mais fácil se você tiver uma interface de base sem a mutators).

No entanto, novamente, é código clichê tedioso, mas é o tipo de coisa que um bom IDE como o Eclipse pode gerar automaticamente para você com apenas alguns cliques do mouse.

Se é o tipo de coisa que você acaba fazendo um monte de objetos de domínio, você pode querer considerar o uso de proxies dinâmicos ou talvez até AOP. Seria relativamente fácil, em seguida, para construir um proxy para qualquer objeto, delegando todos os métodos get e prendendo ou ignorar os métodos set conforme o caso.

Outras dicas

Alguns comentários (não necessariamente problemas):

  1. A classe Date é a própria mutável para que você está copiando-lo corretamente a imutabilidade proteger, mas, pessoalmente, eu prefiro a se converter ao longo no construtor e retornar uma nova data (longValue) no getter.
  2. Tanto o getWhateverInstance () métodos retornam DateBean o que exigirá casting, que poderia ser uma idéia para mudar a interface para retornar o tipo específico, em vez.
  3. Tendo dito tudo o que eu estaria inclinado a apenas tem duas classes um mutável e uma imutável, compartilhando um comum (ou seja, obter apenas) Interface se for o caso. Se você acha que haverá um monte de volta a conversão e para trás, em seguida, adicione um construtor de cópia para ambas as classes.
  4. Eu prefiro classes imutáveis ??para declarar campos como final para fazer o compilador impor imutabilidade também.

por exemplo.

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

}

I usar interfaces e lançando para controlar a mutabilidade de feijão. Eu não vejo uma boa razão para complicar meus objetos de domínio com métodos como getImmutableInstance() e getMutableInstance().

Porque não basta fazer uso de herança e abstração? por exemplo.

public interface User{

  long getId();

  String getName();

  int getAge();

}

public interface MutableUser extends User{

  void setName(String name);

  void setAge(int age);

}

Aqui está o que o cliente do código estará fazendo:

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

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

Será que responder a sua pergunta?

yc

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top