Quando devo gerar exceções? (checagem de pré-condições)

Veja o método abaixo. Ele é público e recebe 2 parâmetros.
Sempre que tiver esse tipo de situação eu devo fazer checagem dos parâmetros e gerar exceções?
Qual exceção devo gerar se o quantidade não for > que 0 ?

/** * Localiza se o material existe. * Caso o material exista, incrementa a quantidade. * Caso não exista acrescenta o material. */ public void addMaterial(DescricaoMaterial material, BigDecimal quantidade){ // Deve ser realizada a checagem de parâmetros. // material não deve ser nulo e quantidade deve ser > que 0. boolean adicionar = true; for (MaterialDoItem materialDoitem:materiais) { if (materialDoitem.equals(material.getId())){ BigDecimal novaQtd = materialDoitem.getQuantidade(); novaQtd = novaQtd.add(quantidade); materialDoitem.setQuantidade(novaQtd); adicionar = false; break; } } if (adicionar){ MaterialDoItem novo = new MaterialDoItem(); novo.setQuantidade(quantidade); novo.setDescricaoMaterial(material); materiais.add(novo); } }

www.fragmental.com.br/arquivos/contratosnulos.pdf

Nesse exemplo, creio que não teria pos-condição. O que você acha?

/** * Localiza se o material existe. * Caso o material exista, incrementa a quantidade. * Caso não exista acrescenta o material. * @param material Descrição do material que será adicionado. * @param quantidade Quantidade do material passado. * @throws IllegalArgumentException se quantidade <= 0. */ public void addMaterial(DescricaoMaterial material, BigDecimal quantidade){ // Deve ser realizada a checagem de parâmetros. // material não deve ser nulo e quantidade deve ser > que 0. if ( (quantidade.compareTo(new BigDecimal(0)) <= 0) ) throw new IllegalArgumentException(); boolean adicionar = true; for (MaterialDoItem materialDoitem:materiais) { if (materialDoitem.equals(material.getId())){ BigDecimal novaQtd = materialDoitem.getQuantidade(); novaQtd = novaQtd.add(quantidade); materialDoitem.setQuantidade(novaQtd); adicionar = false; break; } } if (adicionar){ MaterialDoItem novo = new MaterialDoItem(); novo.setQuantidade(quantidade); novo.setDescricaoMaterial(material); materiais.add(novo); } }

Me parece que sua pós condição é “O material foi adicionado na lista”, você pdoe, se achar necessário 9geralmente não é nestes casos) checar isso.

O que é isso?!?!

É para verificar se a pré-condição foi atendida. O parâmetro quantidade deve ser maior que 0. Se for menor ou igual a 0, lanço a exceção.

Mas não entendi direito algumas coisas.
a) Eu devo verificar se a pré-condição foi obedecida?

b) E antes de chamar o método, devo verificar se estou obedecendo a pré-condição?

Se sim para os dois casos, então eu teria if ( (quantidade.compareTo(new BigDecimal(0)) <= 0) ) throw new IllegalArgumentException(); tanto no método quanto no código que irá chamar o método?

Sim.

Mas como mencionei no PDF, geralmente em linguagens como Java vai ser melhor você só fazer isso quando necessário, mas tente validar todos os possíveis casos de erro com testes unitários e ver como seu sistema responde.

Mesmo sistemas com linguagens feitar para Design by Contract geralmente são compilados com estas checagens apenas para debug e desenvolvimento.

[quote]
Se sim para os dois casos, então eu teria if ( (quantidade.compareTo(new BigDecimal(0)) <= 0) ) throw new IllegalArgumentException(); tanto no método quanto quantidadeno código que irá chamar o método?[/quote]

Por que não

if(quantidade.intValue()==0) throw blablabla

E tem mais, quantidades podem ser decimais? Se não, por que usar BigDecimal e não um inteiro (ou long…)?

No meu caso, as quantidades podem ser decimais sim. Então é isso mesmo. Eu o if verificando os parâmetros sempre 2 vezes. No método e também antes de chamar o método.

Uma outra coisa… tenho algumas pré-condições que não são em cima dos parâmetros. Tenho um método que faz um cálculo, mas que utiliza no cálculo vários atributos do próprio objeto. Então todos os atributos tem que ser não nulos e maiores que 0 (para números). Nesse caso, a verificação pelos atributos (que também farei 2 vezes) deveria lançar qual tipo de exceção?
Desconfio que não seja IllegalArgumentException, devido a documentação dela (Thrown to indicate that a method has been passed an illegal or inappropriate argument.)

Obrigado pelas dicas…

Isso é uma quebra de invariante (novamente,e stá no PDF que te passei) e a exceção padrão recomendade em java é IllegalStateException.

Realmente estava no artigo mesmo. Foi mal essa… Tenho que dar uma lida nas descrições de exceções do package java.lang.

Não entendi muito bem algumas observações…

Fazer testes de unidade passando valores inválidos? Se eu fizer isso sempre vai lançar as exceções que eu programaei, neh.
Talvez, o que estou fazendo agora seja muito simples, para mim entender esse ponto.

Fazer as checagens apenas para debug e desenvolvimento funcionaria como assertions, não seria? Não é recomendado para métodos públicos, neh?
Pensando bem, realmente seria considerado um bug, caso as atuais pré-condições que estou trabalhando não fossem atendidas. Pois essas checagens deveriam ser feitas nos controladores da interface gráfica e mensagens de erro deveriam ser mostradas ao usuário, se algo tivesse errado.
Essas exceções não deveriam ser executadas nunca… Mas se eu esquecer de fazer alguma checagem quando for montar as interfaces gráficas, ai seria um problema…
Devo continuar a utilizá-las, ou nessa situação seria melhor assertions?

É bom você ter testes negativos, mas não foi isso que falei.

O que falei é para você testar as classes clientes, se elas chamam as classes obedecendo a pré-condição destas :wink:

Asserçõe sde Java e C/C++ são uma tentativa fracad e fazer contratos. Sim, é a mesma cosia, só que em linguagens focadas nisso você tem mais recursos, asserções geralmente são inúteis