Dev (Back & Front)ARTIGO

Design By Contract (DbC) em .NET

“O homem, criatura viva e criador individual, é sempre mais importante do que qualquer estabelecido estilo ou sistema.” – Bruce Lee

Olá, pessoal! Escrevo este artigo no intuito de compartilhar com
vocês o resultado de uma de minhas pesquisas: Como aplicar o Design By Contract usando os recursos do framework .NET.

O conceito DbC é usado para garantir o estado de seus objetos em
tempo de execução. Basicamente, quando construímos nossa classe usando
o conceito DbC, definimos acordos formais (o que chamamos de contratos) com quem a utiliza. Esses contratos visam garantir regras de utilização e estado, regras essas que são expressas através de pré-condições, pós-condições e invariantes.

Vamos explorar algumas situações do nosso dia a dia para fortalecer o conceito e aprender a utilizar a classe Contract que se encontra no namespace System.Diagnostics.Contracts:

Como podem ver no código abaixo, estou criando uma classe chamada Cor
que receberá no construtor uma string contendo o código hexadecimal. No
teste, desejo garantir que caso a classe Cor seja instanciada com um
parâmetro nulo, uma exceção do tipo ArgumentNullException seja lançada.

[TestMethod]<br />[ExpectedException(typeof(ArgumentNullException))]<br />public void Deve_rejeitar_parametros_nulos()<br />{<br />    var cor = new Cor(null);<br />}

Para fazer o nosso teste passar, poderíamos verificar se o parâmetro é nulo usando if e
lançar a exceção, porém, quando pensamos em contratos, o que fazemos é
criar uma pré-condição de utilização da nossa classe. Para construir
pré-condições, utilizamos o método Requires da classe Contract informando qual tipo de exceção será lançada caso o contrato for quebrado. Vejam o código abaixo:

public class Cor<br />{<br />    public Cor(string pCodigoHexadecimal)<br />    {<br />        Contract.Requires<ArgumentNullException>(pCodigoHexadecimal != null);<br /> <br />        // Código que converte o hexadecimal...<br />    }<br />}

Como podem ver, o código fica bem mais limpo e expressivo. Sem falar que, caso você tenha o plugin do Visual Studio
instalado, terá essas verificações de contrato em tempo de design; isso
mesmo, enquanto você cria suas classes, seu código será analisado para
verificar possíveis quebras de contrato. Vejam a imagem abaixo:

Vamos para outra situação:agora nossa classe Cor possui um
construtor que recebe as taxas de vermelho, verde e azul. Construímos
um método para adicionar tonalidades de vermelho na cor, porém, a taxa
máxima permitida de vermelho é de 255.

[TestMethod]<br />public void Deve_garantir_taxa_de_vermelho_menor_ou_igual_255()<br />{<br />    var vermelho = 200;<br />    var verde = 200;<br />    var azul = 200;<br />    var cor = new Cor(vermelho, verde, azul);<br /> <br />    try<br />    {<br />        cor.AdicionarVermelho(56);<br />    }<br />    catch<br />    {<br />        // O método deve lançar a exceção pois 200 + 56 ultrapassa o limite de 255.<br />        Assert.AreEqual(vermelho + 56, cor.Vermelho);<br />    }<br />}

Para garantir o limite da taxa de vermelho no nosso objeto,
precisamos que o nosso método seja executado para então verificar o
estado. Em DbC, podemos criar uma pós-condição para avaliar o estado do
nosso objeto ao final da execução do nosso método/propriedade. No
exemplo abaixo, usamos o método Ensures para criar a pós-condição que verifica a taxa de vermelho do nosso objeto após a execução do nosso método. Os contratos sempre são definidos no início dos métodos/propriedades.

public class Cor<br />{<br />    public int Vermelho { get; private set; }<br /> <br />    public Cor(int pVermelho, int pVerde, int pAzul)<br />    {<br />        this.Vermelho = pVermelho;<br />        // Configurar as outras propriedades...<br />    }<br /> <br />    public void AdicionarVermelho(int pValor)<br />    {<br /><br />// Aqui estamos garantindo que ao final da execução desse método, a<br />propriedade Vermelho deverá respeitar o limite de 255;<br />        Contract.Ensures(this.Vermelho <= 255);<br /> <br />        this.Vermelho += pValor;<br />    }<br />}

As pós-condições são tão simples de serem escritas quanto as pré-condições.

Outra forma de garantir o nosso limite de 255 é criar um contrato
invariante, ou seja, uma regra que será mantida sempre, independentemente de
quais métodos forem executados no objeto. Para criamos o contrato
invariante no .net, usamos o método Invariant da classe Contract.
Métodos invariantes são verificados sempre após a execução de qualquer
método público da sua classe, eles só devem ter declarações de
contratos e não podem retornar valores.

[ContractInvariantMethod]<br />private void Invariant()<br />{<br />    Contract.Invariant(this.Vermelho <= 255);<br />}

Como vimos, DbC é uma abordagem muito interessante e pode nos
auxiliar muito no dia-a-dia. O objetivo desse post não é apresentar bons
padrões para design de testes ou grandes soluções no trabalho com
cores, os exemplos aqui mostrados visam apenas apresentar alguns
recursos da classe Contract. Façam os devidos testes e contem o que
acharam do recurso e dessa abordagem.

Separei alguns links interessantes sobre o assunto no delicious, veja no link: delicious.com/denisferrari/DbC.

Fico no aguardo do feedback de vocês.

Abraços!

Outro cara que um dia vai mudar o mundo.

Ver perfil