@rodbv

Em busca de uma arquitetura sólida – Parte 2

com 3 comentários

Olá,

Na segunda parte de nossa série sobre princípios S.O.L.I.D., vamos analisar a segunda letra do acrônimo, O. Caso você não tenha lido a introdução a essa série, recomendo que dê uma passadinha lá antes de prosseguir.

Como vimos lá, a letra O representa o princípio Open-Closed, ou seja, Aberto-Fechado. O enunciado desse princípio é simples:

Uma classe deve ser aberta para extensão, e fechada para mudanças.

Ora, todo bom programador não busca escrever código que seja fácil de ser mantido, ou seja, fácil de mudar? E como o sistema pode mudar se minhas classes estão fechadas para mudança?

Mudanças são inevitáveis, a questão é minimizar riscos e facilitar testes. A idéia é reduzir ao máximo o número de mudanças que temos que fazer em classes já existentes (de preferência, a zero), e ao invés disso implementar mudanças através de novas classes que podem ser “plugadas” ao sistema, como blocos em um Lego.

Vamos ver como isso funciona com um exemplo prático. Suponha que você está encarregado de desenvolver um sistema que suporte busca de usuários por email. Uma forma de implementar isso seria

    public class Procura
    {
        public IEnumerable<Usuario> ProcuraUsuarioPorEmail(string email)
        {
            //efetua a procura no banco de dados
        }
    }

E seu programa principal seria mais ou menos assim:

            Procura p = new Procura();
            var resultado = p.ProcuraUsuarioPorEmail("usuario@meusistema.com.br");

Bem, nada mal. O código funciona, e o cliente está satisfeito. Pelo menos até a próxima semana, que ele te pede que implemente agora uma busca por nome.

Sem problemas, você abre sua classe e faz algumas alterações:

    public class Procura
    {
        public IEnumerable<Usuario> ProcuraUsuarioPorEmail(string email)
        {
            //efetua a procura no banco de dados
        }

        public IEnumerable<Usuario> ProcuraUsuarioPorNome(string nome)
        {
            //efetua a procura no banco de dados
        }
    }

Essa solução, a princípio, não é de todo ruim. Funciona, e seu código ainda é legível. A questão é, à medida que tais mudanças forem se acumulando com o passar dos meses e anos, quão complexa será sua classe?

Todos nós conhecemos a dor de abrir uma classe pra manutenção rápida e deparar com 200 linhas de código, muitas vezes se uma ordem lógica (ninguém tem tempo pra ficar colocando métodos similares em blocos), o que sempre nos deixa com medo que nossa mexida rápida cause problemas em algum lugar que nem sabíamos que existia.

Como poderíamos então fechar nossa classe pra mudança, mas mesmo assim permitir que o sistema evolua? A chave é extensão, e existem várias estratégias para tal.
Vamos olhar uma dessas possibilidades. Uma idéia boa de seguir quando desenhamos classes é separar o que é constante do que varia. No nosso caso, o que é constante é a necessidade de buscar usuários. O que varia é a estratégia usada para tal busca.

Podemos então definir uma interface para busca genérica, e uma classe que use tal interface, que representam as partes constantes da equação:


    public interface IEstrategiaDeProcura
    {
        IEnumerable<Usuario> EfetuaProcura(string criterio);
    }

    public class Procura
    {
        private IEstrategiaDeProcura _estrategia;

        public Procura(IEstrategiaDeProcura estrategia)
        {
            _estrategia = estrategia;
        }

        public IEnumerable<Usuario> EfetuaProcura(string criterio)
        {
            return _estrategia.EfetuaProcura(criterio);
        }

    }

Embora tal estrutura pareça mais complicada a princípio, a grande vantagem é que essas duas classes não terão mais que mudar tão cedo, podemos agora plugar quantas estratégias diferentes quisermos. O que fizemos na classe de procura é injetar a estratégia que ela deverá usar, ainda que ela não saiba nem tenha que saber qual estratégia concreta seja. Com isso estamos atendendo a outro princípio de boa pragramação OO que é programar em cima de abstrações, não classes concretas. Veja que ao reescrevermos essa classe também atingimos o princípio de responsabilidade única! Antes nossa classe tinha dois motivos para mudar (caso a procura por nome ou email mudassem, respectivamente), agora ela tem apenas um motivo para mudar, por tem apenas um simples método genérico. Como dizem os capixabas, matamos dois coelhos com uma caixa d’água só! :)

A busca por email e nome então implementariam tal interface, e serão usadas pela classe de procura:


    public class ProcuraPorEmail : IEstrategiaDeProcura
    {
        public IEnumerable<Usuario> EfetuaProcura(string criterio)
        {
            //efetua a busca por email
        }
    }

    public class ProcuraPorNome : IEstrategiaDeProcura
    {
        public IEnumerable<Usuario> EfetuaProcura(string criterio)
        {
            //efetua a busca por nome
        }
    }

E tais implementações seriam usadas assim:


            var p = new Procura(new ProcuraPorEmail());
            var resultado = p.EfetuaProcura("usuario@meusistema.com.br");

De agora em diante, toda vez que o cliente pedir um novo tipo de procura, basta implementar uma nova classe para a interface IEstrategiaDeProcura, escrever alguns unit tests para a nova classe, e pronto. Reduzimos em muito a chance de quebrar algo que antes funcionava, que é o grande problema em manutenção de sistemas.

Claro, esse exemplo é bem simples (por exemplo, não trata de procuras mais complexas), mas quis manter o exemplo simples para que o princípio Aberto-Fechado ficasse claro, sem distrações.

Seguindo esse princípio você estará abrindo possibilidades para que seu sistema seja extensível, por exemplo para que outras pessoas escrevam módulos de procura que você não implementou, mesmo que eles não tenham acesso ao código-fonte.

Até a próxima!

Escrito por rodbv

15/01/2009 às 02:57

Publicado em Programação, SOLID

3 Respostas

Assinar os comentários com RSS.

  1. Fala Rodrigo,
    Legal seu post. Só umas notas:
    Como está falando de DDD, essa procura deveria ser um repositório, não? Você poderia implementar também um decorator para adicionar qualquer número de níveis de filtros. Ou de repente usando fluent interfaces como o hibernate para adicionar os filtros e uma única query no final.

    André Carlucci

    15/01/2009 em 14:45

  2. Fala André :)

    Valeu pelo comentário…o primeiro!

    Eu quis manter bem simples intencionalmente, pois a idéia é focar em SOLID, que não é necessariamente relacionado a DDD. Uma pessoa pode usar esses princípios em qualquer arquitetura OO, ou mesmo sem arquitetura nenhuma, são só princípios de boa programação OO.

    Mas, caso esse exemplo fosse um sistema completo, sim, essa procura seria simplesmente uma chamada pra um repositório, ou diretamente ou através de um serviço. E precisaríamos de algo que suportasse queries mais complexas, por exemplo usando o pattern decorador ou filtros.

    Eu ainda não tentei o fluent interfaces do NH, mas é algo que pretendo aprender em breve, pois tudo indica que minha empresa vai começar a usar NH esse ano (no momento a gente usa um banco de dados OO em partes do sistema, mas quer mover tudo pra SQL Server pra parar de ter 2 plataformas).

    Valeu! Pro meu próximo post tô pensando em escrever sobre algo mais prático, por exemplo um Hello World pra ASP.NET MVC, usando repositórios e IoC.

    rodbv

    15/01/2009 em 14:54

  3. Rodrigo, muito bom o post. Está bem claro e didático. Pena que é muito raro encontrar programadores que sigam esses conselhos. Na maioria das vezes, quanto você vai dar manutenção em alguma classe, encontra 50 métodos para a mesma coisa. Fica até engraçado ver métodos do tipo BuscaPorEmail, BuscaPorEmailOuNome, BuscaPorEmailOuNomeEmUmaCidade, e por aí vai…

    Roberto Barros

    15/01/2009 em 15:47


Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Sair / Alterar )

Imagem do Twitter

You are commenting using your Twitter account. Sair / Alterar )

Foto do Facebook

You are commenting using your Facebook account. Sair / Alterar )

Connecting to %s

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.