Gostaria de opiniões sobre minha implementação do padrão Specification, definido no livro de Eric Evans.
Vou usar um exemplo de uma especificacao que define requisitos para criacao de carros:
public class CarroSpecification {
private List requisitos;
public CarroSpecification(List requisitos) {
this.requisitos = requisitos;
}
boolean isSatisfeitaPor(Carro carro) {
return carro.getCaracteristicas().containsAll(requisitos);
}
}
Posteriormente eu refatorei para que além de definir os requisitos a especification criasse um objeto baseado nos requisitos fornecidos e usei o padrão builder. Neste ele lança uma excecao caso os requisitos não sejam suficientes ara a criação de um carro. Ficou algo assim:
public class CarroSpecification {
private Map requisitos = null;
...
public CarroSpecification() {
...
}
public CarroSpecification motorizacao(Float valor) {
this.requisitos.put(MOTORIZACAO, valor);
return this;
}
public CarroSpecification cor(Color cor) {
this.requisitos.put(COR, cor);
return this;
}
public CarroSpecification comAr() {
this.requisitos.put(AR, new Boolean(true));
return this;
}
public Carro build() throws IllegalStateException {
// cria um carro, se possível
}
public boolean isSatisfeitoPor(ModeloCarro modelo) {
...
}
}
O que vocês acham? A minha solucao viola o que DDD define como specifications ou é apenas uma specification “um pouco diferente” que perde a caracteristica value object e alem de especificar também cria.
Eu achei essa solucao mais facil para o cliente usar mas gostaria de saber a opiniao dos DDDers de plantao.
[quote=cmoscoso]Gostaria de opiniões sobre minha implementação do padrão Specification
[/quote]
Eu acho que vc está misturando Specification com Builder. São dois padrões diferentes.
Pense em termos reais. Um máquina produz um produto cortado de certa forma. Ela é o builder. Ela constroi o artefacto. Mas ela o faz conforme um conjunto de parametros: a especificação.
Então o seu CarroBuilder é o unico responsável por criar o carro.
Note que o metodo containsAll deixa ao Carro a responsabilidade de verifica a condição e isso não é o objetivo.
A funcionalidade dentro de containsAll deveria estar no specification ou ser um método protegido/de pacote.
O CarroBuilder vai criar um carro. Ele pode fazer isso por ordens directas (interface fluente) ou pela intrepretação de uma especificação. Se vc usa a interface fluente na especificação isso significa que vc não precisa dela, apenas do builder com essa interface.
A ideia é agregar os parametros na especificação, passar ela ao builder, deixar o builder intrepretar a espeficiação.
De tabela deixar a especificação testar se um dado carro atende à especificação. Assim vc pode testar se o builder fez o trabalho como deveria.
Outra coisa, se a especificação é um conjunto de requisitos, eles devem ser objetos tb e não apenas conjuntos num mapa. O seu List requisitos deveria ser List requisitos. Isso é na realidade o padrão Composite, onde vc está montando uma especificação como um conjunto de mini-especificações.
Sergio, obrigado pela resposta.
Foi essa a conclusao que cheguei. Eliminei a specification e mantive apenas o proprio objeto (digamos carro)… Como este é um value object também não é o caso de usar o padrão builder.
Mantive apenas a interface fluente para que os clientes do domain model pudessem criaar esses objetos Carro de uma forma mais, ehhh… “fluente”.
Novamente obrigado.
[quote=sergiotaborda] Outra coisa, se a especificação é um conjunto de requisitos, eles devem ser objetos tb e não apenas conjuntos num mapa. O seu List requisitos deveria ser List requisitos. Isso é na realidade o padrão Composite, onde vc está montando uma especificação como um conjunto de mini-especificações.
[/quote]
Se eu for usar specification eu vou evoluir por esse caminho sim…
Talvez seja util para consultas algo do tipo:
Specification gasolina = new CarroSpecification(GASOLINA);
Specification alcool = new CarroSpecification(ALCOOL);
Specification flex = gasolina.and(alcool);