Olá.
Estou dando uma olhada na apostila de testes, xml e disgn patterns da Caelum e me deparei com a seguinte situação.
Eu tenho uma classe chamada Candlestick, que possui o seguinte construtor:
[code]public Candlestick(double abertura, double fechamento, double minimo, double maximo, double volume, Calendar data) {
// Implementação do construtor
}[/code]
Como deu para perceber, este construtor é bastante extenso. Para resolver isto, um dos exercícios opcionais é criar um builder para este construtor. Desta forma, eu posso criar um Candlestick assim:
[code]CandleBuilder builder = new CandleBuilder();
builder.comAbertura(10.0);
builder.comFechamento(20.0);
…
Candlestick candle = builder.geraCandle();[/code]
Em um determinado momento, a apostila orienta a criar um teste para a classe CandleBuilder. Teste este que deve levantar uma IllegalStateException no método geraCandle, responsável por criar um Candlestick, sempre que não forem chamados todos os setters desta classe, pois um Candlestick não pode existir sem que todos os valores sejam passados para seu constructor. A apostila até dá a sugestão de criar vários booleans para controlar se o setter já foi chamado ou não. Minha pergunta é: O mais correto (se é que existe mais correto neste caso) não seria criar um teste e implementar as verificações diretamente no constructor da classe Candlestick ao invés de fazer isto no método geraCandle da classe CandleBuilder? Digo isto pois, se eu vier a chamar este constructor de algum outro lugar mais adiante terei problemas e provavelmente terei de implementar duas vezes a mesma coisa.
Espero ter sido claro na minha dúvida. Agradeço a ajuda de todos.
Att.
O problema de implementar isso no construtor é que você não sabe se o valor passado, no caso dos doubles, foi omitido (porque você não chamou o tal do builder para esse parâmetro) ou se é zero mesmo.
Não recomendo, para resolver esse problema, ficar usando wrappers a torto e a direito. Você resolve o problema de saber se o parâmetro foi omitido ou não (porque você poderia dizer que ‘null’ é um parâmetro omitido), mas é sempre melhor usar o tipo primitivo em vez do wrapper, dentro da medida do possível.
// Exemplo de como não resolver esse problema
public Candlestick(Double abertura, Double fechamento, Double minimo, Double maximo, Double volume, Calendar data) {
if (abertura == null) throw new IllegalStateException (.....
this.abertura = abertura;
...
if (data == null) throw new IllegalStateException (...
}
Note que isso pode ser uma forma de resolver esse problema em outro contexto (tanto é que volta e meia eu faço exatamente isso).
[quote=bezier curve]O problema de implementar isso no construtor é que você não sabe se o valor passado, no caso dos doubles, foi omitido (porque você não chamou o tal do builder para esse parâmetro) ou se é zero mesmo.
Não recomendo, para resolver esse problema, ficar usando wrappers a torto e a direito. Você resolve o problema de saber se o parâmetro foi omitido ou não (porque você poderia dizer que ‘null’ é um parâmetro omitido), mas é sempre melhor usar o tipo primitivo em vez do wrapper, dentro da medida do possível.
// Exemplo de como não resolver esse problema
public Candlestick(Double abertura, Double fechamento, Double minimo, Double maximo, Double volume, Calendar data) {
if (abertura == null) throw new IllegalStateException (.....
this.abertura = abertura;
...
if (data == null) throw new IllegalStateException (...
}
Note que isso pode ser uma forma de resolver esse problema em outro contexto (tanto é que volta e meia eu faço exatamente isso). [/quote]
bezier curve,
O seu “Exemplo de como não resolver esse problema” foi exatamente o que eu pensei em fazer para que o objeto não seja criado quando algum dos parâmetros seja null. Não sei se compreendi muito bem o motivo de isto estar errado. Qual seria sua sugestão para resolver este problema de forma correta? Onde você testaria / implementaria isto? Em uma classe CandlestickTest ou CandleBuilderTest?
Muito obrigado pela ajuda! 
Oi bruno, tenho o seguinte ponto de vista:
Um builder tem apenas uma responsabilidade: instanciar uma determinada classe, apenas isso.
O construtor da classe deve garantir que a classe seja instanciada de forma consistente (ver good citizen). Aqui entra um ponto importante, o construtor deve garantir um estado consistente, na prática: evitar que parametros são nulo nulas, pra vc n tomar nullPointerEx depois e etc. Mas validar o conteúdo dos dados, por exemplo se o valor de um parametro deve estar entre 50 e 100, isso não deve ser responsabilidade do construtor, pois em determinados momentos aquele valor pode ser aceitável e em outros não.
abrasssss
[quote=renanreismartins]Oi bruno, tenho o seguinte ponto de vista:
Um builder tem apenas uma responsabilidade: instanciar uma determinada classe, apenas isso.
O construtor da classe deve garantir que a classe seja instanciada de forma consistente (ver good citizen). Aqui entra um ponto importante, o construtor deve garantir um estado consistente, na prática: evitar que parametros são nulo nulas, pra vc n tomar nullPointerEx depois e etc. Mas validar o conteúdo dos dados, por exemplo se o valor de um parametro deve estar entre 50 e 100, isso não deve ser responsabilidade do construtor, pois em determinados momentos aquele valor pode ser aceitável e em outros não.
abrasssss[/quote]
Olá renanreismartins.
Pelo que entendi do seu ponto de vista, eu não devo fazer este tipo de verificação no builder, pois não é responsabilidade do mesmo. Porém não seria correto fazer esta verificação no constructor… Desta forma, me leva a crer que o correto seria testar isto a cada chamada do builder. Estou certo? Porém, desta forma, teria de repetir código sempre, não teria?
Obrigado pela atenção.
então no construtor coloco apenas validações, coisas que eu poderia fazer também com beans validation, por exemplo, falar que data não pode ser nula. Já validações de negócio mais especificas, ex: se a data tem que estar entre determinado periodo, nao coloco isso no meu construtor, coloco onde encaixa melhor no sistema. E pra nao ficar duplicando essas regras, vc pode criar sua classe especifica de validacao de negocios, o padrao especifications pode te dar uma ajudinha
abrassss