logo

Banner do Post

Entendendo o Princípio da Responsabilidade Única

Entre os princípios SOLID, o Princípio da Responsabilidade Única (SRP) frequentemente é mal interpretado, em parte devido ao seu nome pouco esclarecedor. Quando ouvem esse princípio mencionado, muitos programadores presumem que se trata simplesmente de limitar cada classe a uma única responsabilidade. No entanto, a verdade é que a definição e aplicação desse princípio são mais complexas do que sugerido pelo título.

Introdução

Vamos começar com uma rápida introdução aos Princípios SOLID para quem ainda não está familiarizado com eles. Em 2000, Robert C. Martin, ou Uncle Bob, escreveu o artigo Princípios de Design e Padrões de Design, que compilou vários princípios e padrões de software existentes que Martin encontrou, em sua experiência, funcionavam bem juntos. Martin não necessariamente inventou nada novo aqui, mas provavelmente economizou aos leitores do artigo meses, ou anos, de leitura e estudo das fontes de onde esses princípios e padrões vieram, além de compartilhar sua experiência e pensamentos sobre desenvolvimento de software eficaz.

Em 2004, Michael Feathers apontou para Martin que com um pouco de ajuste, 5 dos princípios de seu artigo poderiam formar o acrônimo SOLID e isso foi o início do termo "princípios SOLID":

  • S - Princípio da Responsabilidade Única - SRP.
  • O - Princípio Aberto-Fechado - OCP.
  • L - Princípio da Substituição de Liskov - LSP.
  • I - Princípio da Segregação de Interface - ISP.
  • D - Princípio da Inversão de Dependência - DIP.

Desde então, Uncle Bob tem obtido uma vida confortável falando sobre os princípios SOLID. Ele escreveu vários livros, mas os 2 principais que contêm mais detalhes sobre os princípios e padrões agregados em seu artigo são Clean Code e Clean Architecture. Os dois livros são ótimas recomendações de leitura, principalmente o Clean Architecture.

Single Responsibility Principle — SRP

Neste artigo, vamos nos concentrar no Princípio da Responsabilidade Única, especificamente se baseando nas explicações que são dadas sobre esse princípio no livro Clean Architecture. Esse princípio já teve a definição adaptada algumas vezes, talvez a mais conhecida seja:


"Uma classe deve ter uma, e apenas uma, razão para mudar."


Muitas vezes, isso é mal interpretado e erroneamente citado como "Uma classe, ou função, deve ser responsável por realizar apenas uma tarefa". Embora esse conselho seja útil, na verdade não encapsula completamente o Princípio da Responsabilidade Única. No livro é especificado que os sistemas de software mudam para atender às demandas de usuários e stakeholders. Esses usuários e stakeholders são a "razão para mudar" da qual o princípio fala.

Para entendermos melhor, precisamos considerar o Design Orientado a Domínio (DDD) e os Contextos Delimitados(Bounded Contexts). A razão para mudar aqui é que um processo do mundo real mudou ou precisa mudar, e o código precisa refletir isso. É aqui que o DDD entra em cena, pois o código reflete o Modelo de Domínio, que por sua vez reflete o Domínio, ou o processo do mundo real. As alterações no processo são atualizadas no Modelo de Domínio e, por sua vez, no próprio código.

Anteriormente mencionei que o sistema muda para atender as demandas de usuários e stakeholders, as palavras "usuário" e "stakeholder" não são realmente as expressões corretas nesse caso. Quase sempre há mais de um usuário ou stakeholder exigindo que o sistema mude da mesma forma. Então, estamos nos referindo efetivamente a um grupo — uma ou mais pessoas que exigem essa mudança. Nesse contexto, a definição final do SRP é:

"Um módulo deve ser responsável por um, e apenas um, ator. "

Contextos Delimitados

No DDD, temos o conceito de uma Linguagem Ubíqua, que é uma linguagem que Especialistas em Domínio (que executam ou realmente conhecem o processo) e Desenvolvedores de Software (que implementam o processo em código) usam. A Linguagem Ubíqua será diferente em diferentes áreas de um negócio, por exemplo, pessoas no depósito falam de maneira diferente sobre seus processos para pessoas em vendas ou compras, ou pessoas trabalhando em uma loja física. Se tomarmos o exemplo de uma organização que vende livros, pessoas que vendem livros para clientes em uma loja física falam sobre um livro em um contexto muito diferente das pessoas no depósito.

Pessoas no depósito estão interessadas em:

  • Quão pesado é o livro.
  • O tamanho físico do livro.
  • Onde no depósito o livro está localizado.
  • Livros que são pesados e vendidos com frequência podem estar localizados mais perto da baía de carga. Livros mais leves que não são vendidos com frequência podem estar mais distantes.

Para pessoas que trabalham em vendas, no entanto, um livro é uma coisa muito diferente. Eles podem estar interessados em:

  • O livro é capa dura ou capa mole.
  • O livro é colorido ou preto e branco.
  • O livro tem fotos ou ilustrações.
  • O texto é grande para pessoas com deficiência visual.
  • Quem é o autor.
  • Em qual categoria o livro está e o tipo de pessoa que poderia estar interessada em comprar essa categoria.

Podemos ver pelas listas acima que, embora um livro possa ter o mesmo identificador, eles significam coisas muito diferentes para pessoas que trabalham em diferentes contextos da mesma organização e seguem processos diferentes. Vendas e o depósito aqui são dois contextos delimitados diferentes. A Linguagem Ubíqua muda entre os dois departamentos.

Responsável Por Uma Coisa

O Princípio da Responsabilidade Única (SRP) desempenha um papel crucial na estruturação do código. Em contraste com a ideia de que uma classe deve ser responsável por uma única tarefa, o SRP propõe que uma classe seja responsável por um ator, que pode ser um departamento ou função específica. Isso implica que qualquer alteração no código deve ser motivada por mudanças no processo do mundo real dentro desse ator, garantindo que o código permaneça alinhado com as necessidades operacionais.

Ao seguir o SRP, as mudanças no código destinadas a um determinado departamento não devem apresentar riscos de impactar o software usado por outros departamentos. Por exemplo, no cenário de uma livraria, se houver uma modificação no processo de armazenagem que exija ajustes no código, essas adaptações não devem comprometer a funcionalidade do software utilizado pelos vendedores. Qualquer comprometimento nesse sentido poderia ser interpretado como um sintoma de Duplicação Acidental.

A Duplicação Acidental ocorre quando há uma sobreposição na implementação de certas funcionalidades entre diferentes partes do sistema. Embora essa duplicidade possa parecer redundante, dentro do contexto do SRP, ela é justificável e até mesmo desejável. Isso se deve ao fato de que, ao manter implementações separadas para cada ator, reduz-se significativamente o risco de mudanças em um departamento interferirem nos processos de outros departamentos, promovendo uma maior robustez e manutenibilidade do sistema como um todo.

Fusões

Outro problema que precisamos considerar são as fusões de código. Isso acontece com frequência em arquivos que contêm uma variedade de métodos diferentes, especialmente quando esses métodos servem a atores ou departamentos distintos dentro da organização. Por exemplo, em uma livraria, se a equipe de compras decide alterar o processo de catalogação de novos livros e, ao mesmo tempo, a equipe de vendas planeja uma mudança na interface do sistema de pagamento online, dois desenvolvedores podem acabar trabalhando nas mesmas classes relacionadas aos livros, o que pode resultar em conflitos durante a implementação das mudanças.

Esses conflitos podem levar a fusões de código, que são arriscadas, especialmente quando diferentes equipes estão trabalhando em atividades de desenvolvimento paralelas. Embora as ferramentas modernas de controle de versão possam ajudar a gerenciar essas fusões, elas nem sempre são capazes de lidar com todas as situações complexas que surgem durante o processo de integração do código-fonte.

No exemplo mencionado, a fusão do código relacionado aos processos de compras e vendas pode comprometer a qualidade e a integridade do sistema de software, além de afetar outros aspectos da operação da livraria, como as finanças da empresa e a colaboração entre equipes.

Separando o Código

No livro Clean Architecture, as soluções apresentadas para uma boa implementação do SRP se resumem a separar o código em unidades lógicas distintas. Isso geralmente envolve mover as funções relacionadas para classes diferentes. Dessa forma, cada classe é designada para ser responsável por um ator específico dentro do sistema. Esse tipo de abordagem não apenas facilita a compreensão e manutenção do código, mas também promove uma arquitetura mais coesa e flexível, capaz de se adaptar às mudanças nos requisitos e processos de negócios.

Além disso, é crucial ressaltar a importância de seguir os outros princípios SOLID, esses princípios complementam o SRP, promovendo uma arquitetura de software mais robusta e flexível.

Ao aplicar os princípios SOLID em conjunto, os desenvolvedores podem construir sistemas que são fáceis de entender, manter e estender. O OCP, por exemplo, incentiva a extensibilidade do código, permitindo que novos comportamentos sejam adicionados sem modificar o código existente. O LSP garante a consistência do comportamento das classes derivadas, facilitando a substituição de implementações. O ISP promove a coesão, garantindo que as interfaces sejam específicas para os clientes que as utilizam. E o DIP reduz o acoplamento entre os módulos, permitindo que dependências sejam injetadas em vez de instanciadas diretamente.

Conclusão

O conteúdo deste artigo foi inspirado no livro "Clean Architecture" de Robert C. Martin e no artigo "Single Responsibility Principle: It Isn’t What You Think It Is" escrito por Nicholas C. Zakas, disponível no medium. Ambos os recursos fornecem insights valiosos sobre o Princípio da Responsabilidade Única e sua aplicação na arquitetura de software.

Qualquer dúvida, dicas ou sugestões, não se esqueça de deixar nos comentário aqui em baixo.

Espero que tenha curtido 🤟

Debugando um código NodeJS que está rodando em um servidor Docker no VSCode.

Próximo post