Imagine que você possui uma interface que descreve a base para alguma funcionalidade do seu sistema. Como sabemos, a ideia de isolarmos a definição em uma interface serve para facilitar os testes, diminuir o acoplamento e melhorar a estensibilidade.
Quando construímos um tipo, muitas vezes pensamos em como garantir que ele sempre fique em um estado consistente, para que seja utilizado pela aplicação sem qualquer problema. Mas como garantir a consistência de um tipo como uma interface? Interfaces não possuem qualquer implementação, e com isso não há como escrever qualquer código para validar parâmetros, garantia de estado, etc.
Com a criação da interface, também não temos controle de quantas implementações dela serão criadas. Deixar a responsabilidade de garantir a consistência para os implementadores, corre-se o risco de que um deles não se preocupe com isso, prejudicando o propósito para qual a interface foi criada.
A Microsoft recentemente publicou um projeto chamado Code Contracts, e que podemos utilizar uma funcionalidade dele para suavizar esse problema. Através dos atributos ContractClassForAttribute e ContractClassAttribute, conseguimos amarrar (e separar) a nossa interface à uma classe utilizada exclusivamente para validar as implementações, que analisará em runtime se as condições estão ou não sendo atentidas. Abaixo temos uma interface para um repositório de domínio, e logo em seguida, temos uma classe apenas para garantir a sua consistência.
[ContractClass(typeof(RepositoryContract))]
public interface IRepository
{
T GetById<T>(Guid id) where T : AggregateRoot;
void Save(AggregateRoot item);
}
[ContractClassFor(typeof(IRepository))]
internal sealed class RepositoryContract : IRepository
{
public T GetById<T>(Guid id) where T : AggregateRoot
{
return default(T);
}
public void Save(AggregateRoot item)
{
Contract.Requires<ArgumentNullException>(item != null);
}
}
Note que utilizamos o atributo ContractClassAttribute para apontar qual a classe que representa o contrato, e mais abaixo, utilizamos o atributo ContractClassForAttribute para indicarmos que a classe onde este atributo está sendo aplicado serve como contrato para a interface apontada nele. E, finalmente, um detalhe interessante é que a classe é definida como internal, evitando assim o acesso direto pelos consumidores da interface.
Israel,
não seria mais fácil utilizar uma classe abstrata? Desse modo vc poderia escrevar código na classe abastrata que verifica se o estado do objeto é valido, independente da classe concreta.
[]’s
Eduardo Ernandes
Boas Eduardo,
Sim, e com a vantagem de não precisar da API do CodeContracts.