Você está na página 1de 17

.

NET – Design Patterns – Identificando e


aplicando padrões – Parte 01
PorJosé Carlos Macoratti em 10/10/2013

0 Curtiram

... SHARES

Compartilhe!

Antes de iniciar falando de padrões de projetos, vamos falar da base que deu origem aos
mesmos. Vamos falar um pouco sobre os princípios de design ou projeto de software
conhecidos também como boas práticas no desenvolvimento de software.

Quais são os princípios de projeto de software?

Princípios de projeto ou design de software representam um conjunto de diretrizes que


nos ajudam a evitar que tenhamos um projeto ruim. Os princípios de design estão
associados à Robert Martin, que agrupou essa informação em “Desenvolvimento Ágil
de Software: Princípios, Padrões e Práticas”. De acordo com Robert Martin, há três
características importantes de um projeto ruim que devem ser evitadas:

1. Rigidez – É difícil mudar, porque cada mudança afeta muitas outras partes do sistema;
2. Fragilidade – Quando você faz uma alteração, partes inesperadas do sistema
‘quebram’;
3. Imobilidade – É difícil reutilizar noutra aplicação, porque não pode ser desembaraçada
da aplicação atual.

Os princípios de um projeto de software formam as bases sob as quais os padrões de


projeto foram construídos. Eles são mais fundamentais que os padrões de projeto. Toda
vez que você seguir os princípios de projeto, sua base de código vai se tornar mais
flexível e adaptável a mudanças, bem como mais fácil de ser mantida. Vamos introduzir
brevemente alguns dos princípios de concepção de software mais amplamente
conhecidos e uma série de princípios conhecidos como o princípios S.O.L.I.D.

Princípios de projeto de software

Existe uma série de princípios comuns de projeto que, assim como os padrões de
projeto, se tornaram as melhores práticas ao longo dos anos e ajudou a formar uma base
sobre a qual a criação e manutenção de software pode ser construída. Vejamos os
princípios mais importantes:

Keep It Simple, Stupid (KISS)

Keep It Simple é um princípio geral que valoriza a simplicidade do projeto e defende


que toda a complexidade desnecessária seja descartada. Assim, este princípio nos guia a
manter as coisas simples.
Don’t Repeat Yourself (DRY)

O princípio DRY visa evitar a repetição de qualquer parte de um sistema, abstraindo as


coisas que são comuns e colocando-as em um único local. Este princípio não se
preocupa apenas com código, mas com qualquer lógica que esteja duplicada em um
sistema, em última análise deve existir apenas uma representação para cada pedaço de
conhecimento em um sistema. Em suma, o princípio busca reduzir a duplicação de
código e os problemas de manutenção resultantes.

Tell, Don’t Ask

O princípio Tell, Don’t ASK (Diga, não pergunte) está estreitamente alinhado com o
encapsulamento e a atribuição de responsabilidades às suas classes. O princípio, afirma
que você deve dizer aos objetos quais ações você deseja que eles realizem, em vez de
realizar perguntas sobre o estado de um objeto e depois fazer uma decisão sobre a ação
que você deseja realizar. Isso ajuda a alinhar as responsabilidades e evitar forte
acoplamento entre as classes.

You Ain’t Gonna Need It (YAGNI )

O princípio YAGNI refere-se à necessidade de incluir apenas a funcionalidade que é


necessária para a aplicação e adiar qualquer tentação de adicionar outros recursos que
você pode pensar que você precisa. A metodologia de projeto que adere ao YAGNI é o
desenvolvimento orientado a testes (TDD). O TDD versa sobre a escrever primeiro os
testes que comprovam a funcionalidade de um sistema e, em seguida, escrever apenas o
código para obter o teste.

Separation of Concerns (SoC) – Separação das responsabilidades

O princípio SoC é o processo de dissecação de um pedaço de software em


características distintas que encapsulam comportamento único e dados que podem ser
utilizados por outras classes. Geralmente uma responsabilidade representa uma
característica ou o comportamento de uma classe. O ato de separar um programa em
responsabilidades discretas aumenta significativamente a reutilização de código,
manutenção e testabilidade.

Princípios de projeto S.O.L.I.D

A palavra SOLID é um acróstico onde cada letra significa a sigla de um princípio: SRP,
OCP, LSP, ISP e DIP. Os princípios SOLID para programação e projeto orientados a
objeto são de autoria de Robert C. Martin (mais conhecido como Uncle Bob) e datam
do início de 2000.

Obs: Os acrósticos são formas textuais onde a primeira letra de cada frase ou verso
formam uma palavra ou frase.

Na tabela abaixo vemos cada sigla, o seu significado e um resumo do princípio a que se
refere no original e em uma tradução livre.
The Single Responsibility
Uma classe deve ter um, e somente um, motivo para
Principle
SRP mudar.
Principio da Responsabilidade
A class should have one, and only one, reason to change.
Única

Você deve ser capaz de estender um comportamento de


The Open Closed Principle uma classe, sem modificá-lo.
OCP
Princípio Aberto-Fechado You should be able to extend a classes behavior, without
modifying it.

The Liskov Substitution


As classes derivadas devem ser substituíveis por suas
Principle
LSP classes base.
Princípio da Substituição de
Derived classes must be substitutable for their base classes.
Liskov

The Interface Segregation


Muitas interfaces específicas são melhores do que uma
Principle
ISP interface geral
Princípio da Segregação da
Make fine grained interfaces that are client specific.
Interface

The Dependency Inversion


Principle Dependa de uma abstração e não de uma implementação.
DIP
Princípio da inversão da Depend on abstractions, not on concretions.
dependência

Os princípios SOLID devem ser aplicados no desenvolvimento de software de forma


que o software produzido tenha as seguintes características:

 Seja fácil de manter, adaptar e se ajustar às constantes mudanças exigidas pelos


clientes;
 Seja fácil de entender e testar;
 Seja construído de forma a estar preparado para ser facilmente alterado com o menor
esforço possível;
 Seja possível de ser reaproveitado;
 Exista em produção o maior tempo possível;
 Que atenda realmente as necessidades dos clientes para o qual foi criado.

Dependency Inversion Principle (DIP) – O princípio da injeção de dependência (DIP)

O princípio DIP versa sobre como isolar suas classes de implementações concretas e
fazendo-as depender de classes abstratas ou interfaces. Ele promove o mantra de
codificar para uma interface ao invés de fazê-lo para uma implementação, o que
aumenta a flexibilidade dentro de um sistema garantindo que o mesmo não está
fortemente acoplado a um implementação.
Dependency Injection (DI) and Inversion of Control (IoC) – Injeção de dependência e
Inversão de Controle

Intimamente ligados ao principio DIP temos os princípio DI e o princípio IoC.

O principio DI é o ato de fornecer um baixo nível ou classe dependente através de um


construtor, método ou propriedade. Utilizado em conjunto com DI, estas classes
dependentes podem ser invertidas para interfaces ou classes abstratas que levarão a
sistemas com um baixo acoplamento altamente testáveis e de fácil manutenção. Assim a
DI isola a implementação de um objeto da construção do objeto do qual ele depende.

No príncipio IoC, o fluxo de um sistema de controle é invertido em relação à


programação procedural. Como exemplo podemos citar um recipiente IoC, cujo
objetivo é injetar serviços no código de cliente sem que o código do cliente tenha
especificado a implementação concreta.

Padrões de projetos

Depois de apresentar os princípios de projeto e os princípios SOLID, vamos falar dos


padrões de projeto. Eles são modelos de soluções abstratas de alto nível. Pense neles
como modelos para soluções e não como soluções em si mesmo.

Infelizmente não existe nenhum framework que aplique de forma automática os padrões
de projetos à sua aplicação. Ao invés disso, você normalmente faz isso refatorando o
seu código e generalizando o seu problema e não abrindo um manual de receitas e
copiando soluções.

Os padrões de projeto não são a panaceia universal ou a bala de prata que vai resolver
tudo para você. Você tem que compreender plenamente o seu problema, generalizá-lo e,
em seguida, aplicar um padrão que se ajuste ao seu cenário. No entanto, nem todos os
problemas requerem um padrão de projeto. É verdade que os padrões de projeto podem
ajudar a tornar simples problemas complexos, mas eles também podem tornar
complexos problemas simples.

Muitos desenvolvedores após lerem um livro de padrões, caem na armadilha de tentar


aplicar padrões para tudo que estão fazendo, conseguindo exatamente o oposto do que
os padrões se propõem: fazer as coisas simples.

Não existe mágica: a melhor maneira de aplicar padrões é identificar o problema


fundamental que você está tentando resolver e procurar uma solução que se encaixa ao
seu problema, levando em conta o seu cenário que pode ser uma aplicação web,
desktop, com acesso a dados, etc.

Repito mais uma vez: você não é obrigado a usar os padrões de projeto em todos os seus
projetos. Se você chegou a uma solução para um problema que é simples, clara e de
fácil manutenção, mas ela não se encaixa em nenhum dos 23 padrões de projeto
catalogados, não se apavore; você fez a coisa certa. Se você seguir outro caminho,
apenas por modismo, isso vai complicar o seu projeto.
Eu sei que apesar de todo o conhecimento que se pode ter de padrões de projeto sempre
fica aquela dúvida: Como escolher e como aplicar um padrão de projeto? Bem, não
existe uma receita pronta, mas podemos facilitar a nossa decisão seguindo alguns
conselhos.

Você pode escolher dentre muitos padrões de projetos já existentes e catalogados. Como
então identificar aquele que é mais apropriado para o seu problema?

Para saber qual padrão de projeto você pode usar e como aplicar o modelo ao seu
problema específico é importante compreender e utilizar alguns conceitos:

1. Você não pode aplicar padrões sem conhecê-los bem. O primeiro passo a dar é
expandir o seu conhecimento e estudar os padrões e princípios na forma concreta e
abstrata. Você pode implementar padrões de projetos de muitas formas e quanto mais
você conhecer sobre as diferentes implementações possíveis mais você irá
compreender sobre o real objetivo do padrão e de como um padrão pode possuir
diferentes implementações.

2. Você precisa introduzir a complexidade de um padrão de projeto no seu aplicativo? É


comum para os desenvolvedores tentar usar um padrão para resolver todos os
problemas quando eles estão estudando padrões. Você sempre precisa parar e pesar o
tempo inicial necessário para implementar um padrão e o benefício que isso vai dar.
Lembre-se do princípio KISS: Keep It Simple, Stupid ou seja mantenha as coisas
simples.

3. Generalize o seu problema. Identifique os problemas que você está enfrentando de


forma mais abstrata. Veja a forma como a intenção de cada padrão e princípio está
escrito, e veja se o seu problema se encaixa com o problema que um determinado
padrão ou princípio está tentando resolver. Lembre-se que padrões de projetos são
soluções de alto nível. Tente abstrair o seu problema e não se concentre demais nos
detalhes de seu problema específico.

4. Olhe para os padrões de natureza semelhante e para os padrões no mesmo grupo. Só


porque você utilizou um padrão antes não significa que ele será sempre a escolha
correta quando for resolver um problema.

5. Encapsule o que varia. Olhe para o que provavelmente vai mudar com a sua aplicação.
Se você sabe que um algoritmo vai mudar ao longo do tempo, olhe para um padrão
que vai ajudar você a alterá-lo sem afetar o resto de sua aplicação.

6. Depois de ter escolhido um padrão de projeto, esteja certo em usar a linguagem de


seu padrão ao longo com a linguagem do domínio ao nomear os participantes em uma
solução. Por exemplo, se você está usando o padrão Strategy para fornecer uma
solução procure utilizar nomes adequados usando um vocabulário padrão em comum
com a linguagem de seu domínio, isso tornará o seu código mais legível e
compreensível para outros desenvolvedores com conhecimento de padrões.

7. Quando se trata de padrões de projeto, não há substituto para estudar. Quanto mais
você saber sobre cada um dos padrões, melhor equipado você estará em aplicá-los.
Procure captar a intenção de cada padrão para refrescar sua memória para quando
você tiver um problema e estiver procurando uma solução.
8. Um exercício de aprendizado é tentar identificar os padrões aplicados na plataforma
.NET. Por exemplo, o recurso de cache no ASP.NET usa o padrão Singleton, a criação de
um novo GUID usa o padrão Factory, as classes que tratam XML usam o padrão
Factory, etc. Isso ajuda no seu entendimento de como aplicar os padrões.

Estes são alguns conselhos práticos que podem ajudar você a melhor compreender como
usar e aplicar os padrões de projeto, mas existe um caminho mais adequado: sujar as
mãos.

Na segunda parte deste artigo eu vou mostrar um exemplo prático de como podemos
identificar e aplicar padrões de projetos usando um cenário básico de um situação real.

.NET – Design Patterns – Identificando e


aplicando padrões – Parte 02
PorJosé Carlos Macoratti em 17/10/2013

0 Curtiram

... SHARES

Compartilhe!

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 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 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.
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:

1 _produtoRepository = new ProdutoRepository();

3 Injetando a dependência da interface:

5 _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:

fonte: https://www.l2f.inesc-
id.pt/~david/wiki/pt/index.php/Adapter_Pattern_(padr%C3%A3o_de_desenho)
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

Você também pode gostar