Qualidade de Software

Desenvolvimento Psicopata – Qualidade Total

Princípios de Projeto III – Design por contrato

Bertrand Meyer descreve as pré e pós-condições de uma operação como design por contrato. Invariantes são condições que todo objeto de uma determinada classe deve satisfazer em todo ciclo de vida, enquanto estiver em equilíbrio, isto é, não estiver numa mudança de estado.

Um exemplo de invariante, dado por Page-Jones é sobre um objeto da classe Triangulo.

Sabendo que os lados de um triângulo são Triangulo.a, Triangulo.b, Triangulo.c, então uma parte da invariante de classe de Triangulo é: a + b > c and b + c > a and c + a > b.

A pré-condição é uma condição que deve ser verdadeira antes do início da execução da operação. Se não for, a operação pode recusar executar e lançar alguma excessão. Uma pós-condição é uma condição que deve ser verdadeira no fim da execução da operação.

Interfaces são outra forma de design por contrato. Quando uma determinada classe afirma que implementa uma interface, ela deve, obrigatoriamente, implementar todas as operações que estão definidas nesta interface. Existe um excelente artigo aqui sobre o assunto.

Enfim, invariantes, pré e pós-condições e interfaces formam uma abordagem de design conhecida como “Design por Contrato”. Esta abordagem garante que uma operação de um dado objeto vai gerar a resposta correta, para o objeto cliente (objeto que está consumindo o serviço), ao mesmo tempo que obriga o objeto cliente a obedecer as pré-condições do serviço que está consumindo. Também obriga a implementação das operações que estão definidas na interface implemetada.



Referência: Fundamentals of Object-Oriented Design in UML by Meilir Page-Jones

12/05/2009 Posted by | design por contrato, interfaces, invariantes, pós-condições, pré-condições | Deixe um comentário

Princípios de projeto – Parte I

Falaremos agora de correção e robustez.

Aqui, a palavra correção, é utilizada para traduzir correctness, representando a qualidade do que é correto.

Sintetizando em uma frase, correção é o atendimento dos requisitos pela sua aplicação; robustez é a capacidade do seu sistema em lidar, de forma correta, com as exceções.

Estes princípios são obrigatórios. Sem eles não existe aplicação. Na teoria, isso é simples de ver, enquanto, na prática, não é tão simples assim. Existem alguns motivos para o projeto falhar na correção, e serão mostrados no próximo post.

Quanto mais simples e direta a pergunta, mais simples e direta será a resposta. Entretanto, é com requisitos complexos e confusos que, normalmente, trabalhamos. O cliente, não nos chama para resolver dois mais dois.

Normalmente, para implementar um determinado requisito, dispomos de n formas. Assim, para sabermos qual a forma correta, precisamos recorrer aos outros princípios de projeto de software. Por hora, isso não nos interessa.

Podemos usar invariantes para garantir os valores de entrada. Essa é, inclusive, a razão de viver dos métodos de atribuição: setters.

Mas o que são invariantes? Segundo Page-Jones:

É uma condição que todo objeto de uma determinada classe deve satisfazer, por todo seu ciclo de vida (quando o objeto estiver em equilibrio).

Objeto em equilíbrio é o objeto que não está em transição de estado.

Também temos as pré e pós-condições, que atuam a nível das operações. Ex: MENOR_DATA <= nascimento <= MAIOR_DATA. Lembrando que eliminamos números mágicos, portanto não interessa, para quem estiver implementando, os valores das constantes MENOR_DATA e MAIOR_DATA. Em termos gerais, esses valores, fazem parte das regras de negócio do cliente. Mas e a correção no comportamento? Este tipo é bem coberto pelos Testes Unitários. Com eles, descobrimos se a lógica está fazendo o que deveria fazer. Existe um excelente artigo sobre o JUnit aqui. Outro excelente artigo, este sobre mock objects. Montando uma suíte de testes, teremos muito mais segurança ao executar refatorações e atender as Change Requests.

O tratamento de exceções é outra forma de garantia de robustez. E o primeiro aspecto que deve ser observado é: nunca lance uma Exception. Se você está tratanto de alguma exceção, saiba o que está sendo tratado. O tempo é curto, mas isso é ponto chave na aplicação. Saiba, ao certo, que tipo de exceção aquele trecho de código pode lançar e trate-a. Todos nós, desenvolvedores, seremos um pouco mais felizes.

É válido notar que, os testes podem passar e mesmo assim a robustez não estará totalmente garantida. A suíte não é responsável por testar determinadas situações, como queda do banco. Enquanto o banco está no ar, os testes rodam. E quando cair? Vai despejar um stack trace na tela do cliente? Claro que não.



A robustez é uma das variáveis que fornecerá os dados para medir a confiabilidade do seu sistema. Outras variáveis são a arquitetura de hardware, S.O. e a rede. A confiabilidade é obtida através de qtd de falhas / unidade de tempo.

Uma falha na rede pode, facilmente, ser tratada na sua aplicação, mostrando uma mensagem de erro agradável (se é que podemos unir erro e agradável na mesma frase) para o usuário. Falhas e/ou baxa perfomance de hardware ou S.O. é mais complicado de resolver no código.

Podemos prever problemas de perfomance, pensando na eficácia do código. Entretanto, estaremos sempre limitados ao(s) hardware(s), ao S.O. e à rede. Se uma base de dados tunada, estiver em um Pentium 100 MMX, com 64mb de memória, em uma rede de 10Mbits, sua aplicação CRUD pode estar em um supercomputador, com código eficaz e, mesmo assim, deixará a desejar na perfomance.

É necessário buscar o equilíbrio correto entre essas quatro variáveis principais, equilibrando, também, com uma variável importante para o cliente: o custo.

No próximo post, discutiremos alguns dos fatores que levam a falha do princípio da correção no processo de software.

05/05/2009 Posted by | correção, invariantes, princípios, qualidade, robustez, testes | Deixe um comentário