.NET – Design Patterns – Identificando e aplicando padrões – Parte 02
Na primeira parte demos alguns conselhos para identificar e aplicar padrões de projetos. Agora vamos por em prática essa teoria.
Na primeira parte deste artigo demos alguns conselhos para ajudar a identificar e aplicar padrões de projetos. Agora vamos por em prática toda essa teoria em um exemplo simples e objetivo.
É muito bom falar sobre como os padrões e princípios são importantes e podem nos ajudar, mas mais importante é vê-los em ação na prática. Com isto em mente, este artigo examina como um simples pedaço de código ASP.NET↳.NET113 conteúdosNovidades do .NET 9 – o tipo genérico OrderedDictionaryDev (Back & Front) · dez 2024Novidades .NET 10: novas formas de uso do .NET CLIDev (Back & Front) · dez 2025Novidades do .NET 5: implementando um proxy reverso com YARP + ASP.NET 5Dev (Back & Front) · nov 2020Ver tudo em Dev (Back & Front) → que você já deve ter visto inúmeras vezes antes pode ser melhorado com o uso de padrões de projetos.
Vamos examinar um trecho de código que você pode encontrar em um aplicativo de comércio eletrônico↳E-commerce21 conteúdosECBR Club: novo espaço para devs se conectarem ao ecossistema de e-commerceDev (Back & Front) · mai 2025TOTVS anuncia joint venture com VTEXMarketing Tech · mai 2019Jovens da Brasilândia recebem formação gratuita em tecnologiaGestão Dev & TI · jul 2025Ver tudo em Marketing Tech → típico que recupera todos os produtos de uma determinada categoria.
Na figura abaixo vemos o diagrama de classe contendo uma classe ProdutoService com o método GetTodosProduto, uma classe Produto, que representa os produtos da loja, e uma classe ProdutoRepository que é usada para recuperar os produtos de um banco de dados↳Banco de dados134 conteúdosSQL ou NoSQL: eis a questão!!Data · mar 2020Banco de dados: como organizar e dar segurança para milhões de dados de loteriasData · mai 20215 serviços gratuitos na cloud para bancos de dados PostgresData · fev 2025Ver tudo em Data →.
O trabalho da classe ProdutoService é coordenar a recuperação de uma lista de produtos a partir do repositório para um dado código (ID) de categoria e depois armazenar os resultados em cache de forma que a próxima chamada possa ser executada mais rapidamente.
Vamos, então, criar um projeto usando o Visual Studio Express 2012 for web, criar as classes e examinar o código.
Abra o VS Express 2012 for web e no menu Start clique em New Project. A seguir selecione template Other Project Types-> Visual Studio Solutions -> Blank Solution , informe o nome ASPNET_PadraoProjeto e clique no botão OK.
A seguir, no menu FILE clique Add -> New Project e selecione o template Visual C# -> Class Library informando o nome ASPNET_PadraoProjeto_Service e clique no botão OK.
Será criado o projeto ASPNET_PadraoProjeto_Service e a classe Class1.cs. Renomeie o arquivo Class1.cs para Produto.cs e conforme mostrado na figura abaixo:
Vamos incluir uma nova classe, chamada ProdutoRepository, via menu PROJECT -> Add Class contendo o seguinte código:
Crie agora a classe ProdutoService via menu PROJECT -> Add Class com o código mostrado a seguir.
Observe que temos que incluir uma referência a System.Web ao projeto pois estamos usando a classe HttpContext que encapsula todas as informações do HTTP específico sobre uma solicitação HTTP individual.
As classes Produto e ProdutoRepository não necessitam de qualquer explicação, porque eles são simples espaços reservados neste cenário. A classe ProdutoService usando o métodoGetTodosProdutos coordena a recuperação dos produtos a partir do cache, e no caso do cache estar vazio, a recuperação dos produtos a partir do repositório e a inserção no cache.
Este é um cenário típico e encontrado com frequência.
Mas então, o que esta errado com o código acima? Você saberia identificar quais os problemas ele carrega?
- A classe ProdutoService depende da classe ProdutoRepository. Se ProdutoRepository for alterada, vamos precisar alterar também a classe ProductService, e isso não é bom.
- O código não pode ser testado facilmente. Sem ter uma classe ProdutoRepository que se conecte com um banco de dados real, faz com que você não possa testar o método ProdutoServicepor, causa do forte acoplamento que existe entre essas duas classes. Outro problema relacionado com o teste é a dependência com contexto HTTP para o uso com o cache dos produtos. É difícil testar o código, pois ele está muito vinculado ao contexto HTTP.
- Você está preso ao contexto HTTP para o cache. Em seu estado atual, se você precisar usar um cache diferente para armazenamento, tal como o Velocity ou Memcached, exigiria uma alteração da classe ProdutoService e qualquer outra classe que usa o cache.
O Velocity.net é um serviço de cache distribuido que é facilmente instalado em múltiplos servidores e pode ser acessado pelo .NET
através de classes criadas no namespace System.Data.Cache.
O Memcached é um sistema de cache em memória distribuido muito fácil de usar.
Agora que você já sabe o que esta de errado com o código acima vem a pergunta que não quer calar: quais as providências você deverá tomar para melhorar o código usando padrões de projetos? Vamos começar com o problema da dependência entre classe ProductService e a classe ProductRepository.
No cenário atual, a classe ProdutoService é frágil, visto que se a classe ProdutoRepository for alterada ela provavelmente terá que sofrer modificações e isso vai contra dois princípios importantes: a separação de responsabilidades e o princípio da responsabilidade única.
Como resolver esse impasse?
Aplicando o princípio da inversão de dependência
Este princípio nos diz que não podemos depender de uma classe concreta, mas de uma interface: programe para uma interface e não para uma implementação (classe concreta).
Vamos começar com o problema da dependência entre classe ProductService e a classe ProductRepository.
No cenário atual, a classe ProdutoService é frágil, visto que se a classe ProdutoRepository for alterada, ela provavelmente terá que sofrer modificações e isso vai contra dois princípios importantes: a separação de responsabilidades e o princípio da responsabilidade única.
Podemos empregar o princípio da inversão da dependência para desacoplar a classe ProdutoService da classe ProdutoRepository, fazendo com que ambas dependam de uma abstração – uma interface.
Vamos abrir a classe ProdutoRepository e refatorar a classe, extraindo uma interface. Você pode fazer isso usando a refatoração (o Visual Studio possui este recurso nativo) ou criando você mesmo o código.
Devemos criar a interface IProdutoRepository, via menu PROJECT-> Add New Item, com o código mostrado a seguir:
A seguir vamos ajustar a classe ProdutoRepository para implementar a nova interface recém-criada:
Finalmente precisamos atualizar a classe ProdutoService para garantir que ele faz referência a interface ao invés da classe concreta:
O que você conseguiu através da introdução de uma nova interface?
A classe ProductService agora depende apenas de uma abstração em vez de uma classe concreta, o que significa que a classe agora é completamente ignorante de qualquer implementação, assegurando que ela é menos frágil e que o seu código base é menos sujeito a mudanças.
No entanto, existe um pequeno problema: a classe ProdutoService ainda é responsável pela implementação concreta, e, atualmente é impossível testar o código sem uma classeProdutoRepository válida.
O que fazer para resolver isso?
Aqui entra a Injeção de dependência para nos ajudar a solucionar este problema.
Aplicando o princípio da Injeção de dependência
A classe ProdutoService ainda está acoplada à implementação concreta da classe ProdutoRepository porque é de responsabilidade da classe ProdutoService criar essa instância. Isto pode ser visto no construtor da classe.
Usando a injeção de dependência podemos mover a responsabilidade de criar a implementação de ProdutoRepository para fora da classe ProdutoService e fazer a injeção da dependência através do construtor da classe, tal como pode ser visto na listagem de código a seguir:
Note que removemos a linha de código que implementa a classe concreta:
_produtoRepository = new ProdutoRepository(); Injetando a dependência da interface: _produtoRepository = produtoRepository;
Isso permite que um substituto possa ser passado para a classe ProdutoService durante os testes, o que permite que você teste a classe isoladamente.
Ao remover a responsabilidade de obter dependências de ProdutoService, você está garantindo que a classe adere ao princípio da responsabilidade única, visto que agora ela só se preocupa com a recuperação dos dados do cache ou repositório e não mais em criar uma implementação concreta de IProductRepository.
A injeção de dependência pode ser aplicada de três maneiras distintas: via Construtor, Método e Propriedade. Usamos neste nosso exemplo a injeção de dependência via construtor.
A última coisa que precisamos fazer agora é resolver a dependência do contexto HTTP para o cache. Para isso, vamos contratar os serviços de um padrão de design simples.
Refatorando o padrão Adapter
Como não temos o código-fonte para a classe de contexto HTTP, não podemos simplesmente criar uma interface da mesma forma que fizemos para a classe ProdutoRepository.
Felizmente, esse tipo de problema foi resolvido inúmeras vezes antes, e existe um padrão de projeto pronto para nos ajudar: o padrão de projeto Adapter.
O padrão Adapter basicamente traduz uma interface para uma classe em uma interface compatível para que você possa aplicar esse padrão e mudar a API Context cache HTTP para uma API compatível que você deseje usar. Então, você pode injetá-la através de uma interface para a classe ProdutoService usando o príncipio da injeção de dependência.
Vamos criar uma interface chamada ICachePersistencia, via menu PROJECT-> Add New Item -> Interface, com o seguinte código:
Agora que temos a nova interface, podemos atualizar a classe ProdutoService para usá-la ao invés de usar a implementação do contexto HTTP.
Temos a seguir o código da classe ProdutoService alterado, já usando a interface ICachePersistencia:
Agora o nosso problema é que a API Context Cache HTTP não pode implementar implicitamente a nova interface ICachePersistencia.
Como o padrão Adapter pode ser usado para nos salvar desse impasse? Qual o objetivo do padrão Adapter? É converter a interface de uma classe em outra interface esperada pelos clientes. E é exatamente isso que precisamos aqui.
Abaixo vemos o diagrama UML representando o padrão Adapter:

Como vemos na figura, um cliente tem uma referência a uma abstração – o Target. Neste caso, esta é a interface ICachePersistencia.
O adaptador é uma implementação da interface de Target e simplesmente delega o método de operação para o Adaptee que gera o seu próprio método SpecificOperation. Vemos que o adaptador simplesmente envolve uma instância do Adaptee e delega o trabalho dele enquanto implementa o contrato da interface Target.
Ajustanto o diagrama para o nosso exemplo chegamos ao seguinte diagrama:
Vemos a classe do projeto e a classe Adapter que precisamos implementar usando o padrão Adapter com o contexto HTTP cache de API.
A classe HttpContextCacheAdapter é um invólucro para o cache Context HTTP e delega o trabalho para os seus métodos. Para implementar o padrão Adapter precisamos criar classe HttpContextCacheAdapter.
Assim, inclua uma nova classe no projeto, via menu PROJECT -> Add Class, chamada HttpContextCacheAdapter.cs com o seguinte código no projeto:
Agora é mais fácil implementar uma solução de cache nova, sem afetar qualquer código existente. Por exemplo, se você quiser usar o Velocity, tudo que você precisa fazer é criar um adaptador que permite que a classe ProdutoService possa interagir com o provedor de cache de armazenamento através da interface comum ICachePersistencia.
O padrão Adapter também é simples. Seu único propósito é fazer com que as classes com interfaces incompatíveis trabalhem em conjunto.
O adaptador não é o único padrão que pode nos ajudar com o cache de dados. Existe um padrão de projeto chamado Proxy que também pode ser usado mas isso é assunto para outro artigo.
Vimos assim como a aplicação de alguns padrões básicos pode desacoplar o seu código, tornando-o mais robusto e testável.
Pegue o projeto completo aqui: ASPNET_PadraoProjeto.zip





















José Carlos Macoratti




