Desenvolvedores.Net - TechBlog

Daily Archives: 9 de agosto de 2017

SRP Single Responsibility Principle (Princípio da Responsabilidade Única)

1
1 Estrela2 Estrelas3 Estrelas4 Estrelas5 Estrelas (1 votos, média: 5,00 de 5)
Loading...
9 de agosto de 2017


Veja o índice completo do tópico “S.O.L.I.D”

Olá pessoas …

Dando início a nossa saga de artigos, vamos iniciar com a primeira letra do acróstico, S que define a sigla SRP Single Responsibility Principle (Princípio da Responsabilidade Única).

Definição

Isto quer dizer que nossas classes devem ter apenas uma, e somente uma, razão para ser modificada. Se a classe possuir mais de um motivo para ser modificada, a mesma não é coesa e isso já fere os princípios da POO (Programação Orientada à Objetos).

Problemas ao ferir o primeiro princípio.

  • Dificuldade de compreensão, logo dificuldade de manutenção na classe;
  • Não permite reuso;
  • Alto acoplamento, a classe depende de conhecer outras e outras e mais outras classes para poder funcionar, o que dificulta a manutenção ao alterar as classes de dependência;

Imaginem um canivete suíço, para quem não sabe, é este ai abaixo, o pai dos canivetes suíços.

Canivete criado por John S. Holler por volta de 1880, na Alemanha e possui 100 funções.
As tampinhas só foram inventadas em 1891. 
Fonte: Google

Este canivete, se fosse uma aplicação, fere completamente o primeiro princípio. O da responsabilidade única. Em nossas aplicações cada objeto deve fazer apenas o que ele se propõe a fazer.

A ostra ostreia, o gato gateia, o vento venta
(Prof. Clóvis Barros Filho)

Nossas classes devem seguir esta linha de pensamento, se é uma classe de serialização, ela serializa, seja em banco de dados, arquivos, memória. Não importa, ela apenas serializa.
Se nossa classe é de validação, ela apenas valida.

Certo ou Errado?

Nos exemplos abaixo eu sempre colocarei o certo e o errado para compararmos as classes e as soluções propostas. Tentarei usar problemas e soluções do dia-a-dia.

Antes de começarmos vamos ver a imagem abaixo:

Como podemos ver, é muito mais simples criar da forma errada. Mas te trará problemas de manutenção de código, de reusabilidade de código, seu código será macarrônico e anêmico. Alto Acoplamento e baixa coesão. No momento da manutenção deste código, erros simples se tornam complexos de serem resolvidos.

Como ferir o princípio.

Veja o exemplo de código abaixo:

errado

Isto está errado.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

 using System;
using System.Collections.Generic;

namespace Solid.Errado
{
    public class Cliente
    {
        #region Private Methods

        private void Adicionar()
        {
            Validar();
            Console.WriteLine($"O cliente {Nome} foi inserido.");
        }

        private void Atualizar()
        {
            Validar();
            Console.WriteLine($"O cliente {Nome} foi atualizado.");
        }

        #endregion Private Methods

        #region Public Properties

        public string CNPJ { get; set; }
        public int Codigo { get; set; }
        public DateTime DataCadastro { get; set; }
        public string Email { get; set; }
        public string Endereco { get; set; }
        public string Nome { get; set; }

        #endregion Public Properties

        #region Public Methods

        public void EnviarEmail()
        {
            Console.WriteLine($"O e-mail foi enviado para o endereço {Email}");
        }

        public void Excluir(long id)
        {
            Console.WriteLine($"O cliente {Nome} foi excluído.");
        }

        public void Salvar()
        {
            if(Codigo != 0)
                Atualizar();
            else
                Adicionar();

            EnviarEmail();
        }

        public IList<Cliente> Selecionar(long? id = null)
        {
            List<Cliente> result = new List<Cliente>();

            return result;
        }

        public bool Validar()
        {
            if(String.IsNullOrWhiteSpace(Nome))
                throw new Exception("O nome é obrigatório");

            if(!Email.Contains("@"))
                throw new Exception("O e-mail não é válido");

            if(CNPJ.Length != 14)
                throw new Exception("O CNPJ não é válido");

            return true;
        }

        #endregion Public Methods
    }
}

A classe cliente escrita acima, tem diversos pontos que ferem o princípio da Responsabilidade Única:

  • Ela é uma classe que se faz sua própria serialização, como pode ver na linha 17 do método “Adicionar()”, linha 23 do método “Atualizar()” e assim nos demais métodos. Logo esta classe tem que conhecer a implementação de sua forma de serialização, seja banco de dados, XML, JSON, memória;
  • Esta classe é responsável por enviar um e-mail ao cliente, assim que o mesmo é cadastrado na aplicação. Imagina se você criar outras classes de pessoas, como Fornecedor, Usuário, Fabricante, e se fizer necessário o envio de e-mail ao se criar cada pessoa. Cada classe terá sua implementação do método “EnviarEmail()” e você terá que dar manutenção em cada método das classes de pessoas, se por um acaso o servidor for modificado, por exemplo;
  • Esta classe é  responsável por validar seus próprios dados. Em uma customização para clientes, esta abordagem seria trabalhosa, uma vez que cada cliente pode ter sua forma de validação de dados;

Como resolver o problema.

certo

Isto está correto.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Certo.Persistence.Abstract;
using Solid.Certo.Repository;
using System;

namespace Solid.Certo.Persistence
{
    public class Cliente: EntityBase
    {
        #region Public Properties

        public string CNPJ { get; set; }
        public DateTime DataCadastro { get; set; }
        public string Email { get; set; }
        public string Endereco { get; set; }
        public string Nome { get; set; }

        #endregion Public Properties

        #region Public Methods

        public void Excluir()
        {
            DBContext<Cliente>.Excluir(Codigo);
        }

        public void Salvar()
        {
            DBContext<Cliente>.Salvar(this);
        }

        #endregion Public Methods
    }
}

Como podem ver:

  • Esta classe não se “Valida”;
    • Para resolver este problema utilizamos o padrão de projeto “Strategy“. Que são:
      • StrategyDependencyManager: Responsável por fazer a injeção de dependência das estratégias na classe cliente;
      • IStrategy: Define o contrato para qualquer estratégia que não seja de persistência;
      • IPersistenceStrategy: Define o contrato para as estratégias de persistência dos dados;
      • StrategyBase: Abstração de estratégias para os tipos mais básicos;
      • PersistenceStrategyBase: Abstração para estratégias de persistências;
      • ClienteStrategy: Estratégia concreta para validação de clientes;
    • A validação do CNPJ passou a ser de responsabilidade do padrão Validator (Visitor Pattern).
      • IValidator: Contrato para classes de validação de tipos;
      • CNPJValidator: Classe concreta para validação do tipo CNPJ;
        • Estes tópicos, Visitor Pattern e Validators serão discutidos em outros artigos. Aqui iremos falar apenas do conceito S.O.L.I.D.
  • Ela não conhece a implementação de serialização, no caso, podemos perceber que seria em um banco de dados;
    • Este problema foi resolvido usando a classe DbContext, mas temos que tomar cuidado com esta abordagem, pois depende do framework de acesso à dados que você vai utilizar, o mais comum, para desenvolvimento .NET é o Entity Framework. Este link do StackOverflow; em Português; tem uma boa explicação sobre com prós e contras;
  • Ela não manda e-mails;
    • Quem decide se o e-mail deve ou não ser enviado é a estratégia de cada cliente, para isso a estratégia utiliza-se do serviço de e-mail para o envio do e-mail para o cliente.
      • EmailService: Classe de serviço de e-mail responsável por enviar e-mail e receber e-mail em toda a aplicação;

Como podemos ver, esta classe apenas faz o que lhe diz respeito. Manter os dados dos clientes.

Não se preocupe com os nomes de classes e conceitos explicados acima como solução para a forma correta. Durante o desenvolvimento dos demais artigos, irei explicar todas as classes e os princípios aplicados.

O fonte utilizado aqui pode ser baixado pelo GITHub em https://github.com/desenvolvedores-net/ArtigoSOLID

 


É isso ai pessoal 🙂
Até o próximo
♦ Marcelo