"O homem, criatura viva e criador individual, é sempre mais importante do que qualquer estabelecido estilo ou sistema." - Bruce Lee
Olá Pessoal! Estou escrevendo esse post 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 utizar 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.
1
2
3
4
5
6
| [TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Deve_rejeitar_parametros_nulos()
{
var cor = new Cor(null);
} |
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:
1
2
3
4
5
6
7
8
9
| public class Cor
{
public Cor(string pCodigoHexadecimal)
{
Contract.Requires<ArgumentNullException>(pCodigoHexadecimal != null);
// Código que converte o hexadecimal...
}
} |
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 constrato 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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| [TestMethod]
public void Deve_garantir_taxa_de_vermelho_menor_ou_igual_255()
{
var vermelho = 200;
var verde = 200;
var azul = 200;
var cor = new Cor(vermelho, verde, azul);
try
{
cor.AdicionarVermelho(56);
}
catch
{
// O método deve lançar a exceção pois 200 + 56 ultrapassa o limite de 255.
Assert.AreEqual(vermelho + 56, cor.Vermelho);
}
} |
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public class Cor
{
public int Vermelho { get; private set; }
public Cor(int pVermelho, int pVerde, int pAzul)
{
this.Vermelho = pVermelho;
// Configurar as outras propriedades...
}
public void AdicionarVermelho(int pValor)
{
// Aqui estamos garantindo que ao final da execução desse método, a propriedade Vermelho deverá respeitar o limite de 255;
Contract.Ensures(this.Vermelho <= 255);
this.Vermelho += pValor;
}
} |
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, independente de quais método 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.
1
2
3
4
5
| [ContractInvariantMethod]
private void Invariant()
{
Contract.Invariant(this.Vermelho <= 255);
} |
Como vimos, DbC é uma obordagem muito interessante e pode nos auxiliar muito no dia-a-dia. O objeto 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!
Populares