Pesquisar

terça-feira, 10 de agosto de 2010

Decisões de design - Herança X Composição

Mesmo que esse assunto pareça já explorado de todas as formas ainda há certas situações que relutamos (pelos menos eu) em usar a herança ao invés da composição.
Invejando descaradamente o conto de cada dia do Phillip http://images.americanas.com.br/produtos/item/2508/8/2508843gg.jpg. Vou contar de uma experiência que tive. O exemplo clássico é o controlador CRUD (seja seu servelet, managed bean, action form... ) que contém os métodos para uso geral e genérico e alguns ganchos. Vou dar um exemplo bem simples de um Managed Bean CRUD.

public class ManagedBeanCRUD [E,ID]{
private E instancia;
public E getInstancia(){return instancia;}
public Void setInstancia(E entidade){this.instancia = entidade;}

public String salvar(){
preSalvar();
servicoCrud.salvar(getInstancia);
return "navegacaoListar";
}
public void preSalvar(){}
}

E apartir desse MB crio os meus outros CRUDS simples, exemplo telefone:

public class TelefoneMB extends ManagedBeanCRUD [Telefone, Integer] {
@Override
public void preSalvar(){
System.out.println("Oi esse é gancho ...");
}
}

Ótimo, houve reuso por meio da herança mas logo veio as seguintes perguntas: porque não transformou os métodos preXXX em métodos abstratos? Porque daí eu teria que obrigar o usuário a implementar sempre mesmo quando não fosse necessário. porque não usou composição ao invés da herança? Porque minha visão (xhtml) já está toda padronizada.

[c:campoentrada binding="#{mb.instancia.numero}"/]
[c:botao binding="#{mb.salvar}"/]

Note que o método salvar não existe (ao menos no fonte) na classe TelefoneMB ele é acessado por herança. A primeira tentativa foi substituir a herança por composição por meio de delegação, minha classe TelefoneMB ficaria mais ou menos assim:

public class TelefoneMB{
private ManagedBeanCRUD[Telefone, Integer] delegate;
public String salvar(){
return delegate.salvar();
}
// + do mesmo
}
Mas será que não pode ser melhorado?!

Claro que sim até agora consegui chegar num patamar assim: os métodos ganchos se transformam no padrão decorator e a herança se torna composição por delegação mas de um modo diferente, fiz o MangedBeanCRUD ser um atributo de leitura :

public class TelefoneMB{
private ManagedBeanCRUD[Telefone, Integer] crud;

public TelefoneMB(){
crud.addEventoPreSalvar(new Evento(){
public void executar(){
System.out.println("antes de salvar...");
}
});
}

public ManagedBeanCRUD getCrud(){
return crud;
}
}
E na view (jsf, xhtml ...) não terei muita mudança será uma navegação a mais na #EL #{telefonemb.crud.salvar} terei que adicionar o crud antes do padrão CRUD de ser.

Com isso tenho um managed bean livre de más práticas, melhorei o sistema de eventos e mais um plus não terei problemas futuros com proxies do JDK ou GLIB2! Mais camadas e indireções sempre foi o caminho conforme post sobre abstrações pra todo lado.

Todo comentário sempre é e será bem-vindo!

ps: só pra deixar claro não sou contra o uso da herança, há casos que você pode usar herança para compor suas entidades e outros n usos. O único cuidado que deve ter é pensar, pensar, pensar e pensar para decidir se é herança ou não.