Opções para programação por contrato em Java

5

Estou trabalhando em um projeto (que inclui aplicativos da web JavaEE e aplicativos JavaSE) que cresceu de um único desenvolvedor para uma equipe de três, e questões de legibilidade e robustez estão começando a surgir.

Uma omissão gritante da linguagem Java tem sido contratos bem ajustados e a facilidade com que eles são expostos a outros desenvolvedores. (Por contratos quero dizer principalmente validar parâmetros de métodos e condições de pré / pós objeto).

Existem algumas soluções que estou vendo e gostaria de receber alguns comentários de outras pessoas que tenham alguma experiência prática.

Parece que atualmente o Java tem duas maneiras suportadas para definir contratos:

  1. A declaração de afirmação
  2. Lançamento de exceções (como IllegalArgumentException)

As declarações parecem ter valor apenas como uma ferramenta de depuração. Como as declarações não são ativadas por padrão, a expectativa é que os desenvolvedores só confiem nelas durante algum período de teste formalizado, após o qual serão ignoradas.

Arremessar exceções parece ser uma opção robusta, pois permite que quase todo tipo de controle seja feito. No entanto, o intervalo de exceções definido pela biblioteca Java parece ser pouco adequado à definição de contratos (para subclasses explícitas de IllegalArgumentException não especificamente com problemas de intervalo, verificações de nulos, etc, e subclasses de IllegalStateException parece estar preocupado com a conexão de rede e os estados do arquivo), o que força cada grupo a definir seu próprio intervalo de classes de exceção.

Mas o maior problema é que nem as afirmações nem as exceções mapeiam explicitamente os requisitos do contrato para parâmetros ou conceitos, como pré e pós-condições. Espera-se que o desenvolvedor mantenha manualmente os comentários do JavaDoc com o código para deixar claro sob quais condições uma afirmação falhará ou uma exceção será lançada.

É claro para mim que as bibliotecas Java padrão não suportam adequadamente o design por contrato, o que me leva às minhas perguntas:

As suposições acima estão corretas ou perdi alguma propriedade de afirmações e exceções que realmente as tornam bastante úteis para esse tipo de problema?

    
por Phyxx 06.05.2012 / 23:17
fonte

2 respostas

2

Suas suposições estão corretas. Java requer uma biblioteca de terceiros para implementar o design por contrato.

Aqui estão algumas bibliotecas de terceiros listadas no artigo da Wikipédia sobre projeto por contrato .

Contract4J, jContractor, Jcontract, C4J, Google CodePro Analytix, STclass, Jass preprocessor, OVal with AspectJ, Java Modeling Language (JML), SpringContracts for the Spring framework, Modern Jass, Custos using AspectJ,JavaDbC using AspectJ, JavaTESK using extension of Java, chex4j using javassist, or Contracts for Java, and the highly customizable java-on-contracts.

    
por 07.05.2012 / 16:40
fonte
2

Geralmente eu acho que bons testes e um nível razoável de validação em tempo de execução oferecem a maioria dos benefícios do projeto por contrato. Não tenho certeza se vale a pena trazer um conjunto de ferramentas totalmente novo para o projeto por contrato em programas Java normais.

Técnicas que você pode usar sem sair do Java padrão:

  • Use os testes da JUnit para publicar explicitamente as condições em uma ampla variedade de entradas
  • Para qualquer API pública (ou seja, uma que se destina a ser consumida por usuários externos / outros grupos em sua organização), faça a validação de parâmetros de tempo de execução explicitamente. Lance exceções com uma mensagem de erro significativa no caso de qualquer entrada inválida (um IllegalArgumentException é bom, mas certifique-se de que a mensagem faz sentido!)
  • Para todas as funções que validam seus argumentos, você deve escrever testes JUnit com entrada inválida e verificar se uma exceção é lançada (esse é efetivamente um teste de pré-condição)
  • Escreva um método validate () para todos os objetos personalizados complexos que testam o estado / consistência interna do objeto e quaisquer sub-objetos compostos. Você pode chamar esse método durante o teste ou no tempo de execução para garantir que toda a estrutura seja válida. Efetivamente, essa é uma verificação invariante em uma estrutura de dados.
  • Use as afirmações nas funções para testar invariantes durante a execução. Isso pode ser caro, por isso, faz sentido usar afirmações para que as verificações não sejam aplicadas no código de produção, mas essas verificações são muito valiosas durante o teste e a depuração. Lembre-se de que seus recursos também devem ser usados durante as execuções do conjunto de testes automatizados do JUnit, portanto, você deve detectar a maioria dos erros lógicos durante o desenvolvimento.
por 07.05.2012 / 16:55
fonte