SOLID – Single Responsibility Principle (SRP)

Boa noite, leitores! Como estão?
Hoje iniciaremos uma nova série de apenas 5 artigos abordando os princípios SOLID. Pretendo enfatizar o objetivo de cada um deste princípios devido à sua extrema importância na arquitetura de um software.
O primeiro dos princípios é a letra “S”, que corresponde ao Single Responsibility Principle. Vamos conhecê-lo?

Introdução

Os princípios SOLID foram introduzidos por um especialista em Engenharia de Software chamada Robert C. Martin, ou “Uncle Bob”, bastante conhecido pela autoria dos livros Clean Code“, “Clean Coder” e “Clean Archicteture. Estes princípios estão intimamente associados à programação Orientada a Objetos e apresentam uma série de técnicas e mecanismos para construir uma arquitetura de classes mais flexível e sustentável.

O termo SOLID, embora seja uma palavra em inglês com tradução de “sólido”, neste contexto é um acrônimo. Cada letra corresponde a um dos princípios, exigindo que todo termo seja escrito em maiúsculas.

Neste primeiro artigo, estudaremos o primeiro deles, chamado Single Responsibility Principle, ou simplesmente SRP.
Pela tradução – Princípio da Responsabilidade Única – já podemos ter uma noção do que se trata: o SRP declara que cada classe no projeto deve possuir apenas uma única responsabilidade, e nada mais do que isso.

Contexto

Para iniciar a explicação, imagine “responsabilidade” como uma “habilidade” de uma classe. Dito isso, o fato de uma classe ter mais de uma habilidade indica que o SRP não foi cumprido. Por exemplo, considere uma classe que tenha métodos para calcular estatísticas de venda e gerar um relatório gerencial. Nota-se que ela possui duas habilidades, certo? Uma delas é o cálculo das estatísticas e a outra é a geração do relatório. Neste caso, dizemos que a classe quebra o princípio de responsabilidade única.

Uma forma simples de verificar se uma classe possui mais de uma responsabilidade é atentar-se à presença da conjunção “e” ao descrever o seu papel no projeto. Portanto, se a classe calcula estatísticas e produz um relatório, significa que existem duas responsabilidades.

Outro fator que revela várias responsabilidades em uma classe é a quantidade de linhas. Classes grandes, com vários métodos, normalmente tendem a executar mais de uma tarefa, ao menos que a regra de negócio realmente seja complexa e justifique o tamanho.

A definição teórica do SRP, na realidade, traz a seguinte frase:

“A class should have one, and only one, reason to change”
(Uma classe deve ter um, e somente um, motivo para mudar)

O “motivo para mudar”, citado na frase, é o que conduz a ideia do SRP. A quantidade de motivos para mudar equivale ao número de responsabilidades que uma classe carrega. Logo, um único motivo para mudar é o que devemos almejar ao desenhar a nossa arquitetura.

Na classe de exemplo deste artigo, que calcula estatísticas e gera o relatório, observe o impacto circular:

  • Se uma modificação for realizada no cálculo de estatísticas, o relatório provavelmente será alterado para refletir essa alteração;
  • Se uma modificação for realizada no relatório, o cálculo de estatísticas provavelmente será alterado para calcular os dados requeridos que serão exibidos.

Verifica-se, então, dois motivos para mudar, quebrando o princípio de responsabilidade única.

Para satisfazer o SRP, basta extrair cada responsabilidade para uma classe separada. Dessa forma, as classes terão um objetivo único, exclusivo e apenas um motivo para mudar. Utilizando o exemplo anterior, a classe seria fragmentada em duas: uma exclusiva para o cálculo de estatísticas e outra exclusiva para a geração do relatório. Com essa ação, pode-se constatar vários benefícios:

  • Facilidade na manutenção;
  • Arquitetura com responsabilidades bem definidas;
  • Redução de impacto na arquitetura ao alterar o código;
  • Possibilidade de reaproveitamento de código, já que cada classe possui apenas uma função.

Exemplo prático

Para fixar ainda mais o conceito do SRP, partiremos para um exemplo prático.

Imagine que uma classe foi modelada para criar algumas informações de log e enviar para um endereço de e-mail. Opa, pela conjunção “e”, já sabemos que essa classe provavelmente tem mais de uma responsabilidade!

A instância da classe é consumida dessa forma:

Eis que, após um tempo, foi solicitada uma alteração no comportamento dessa classe. Ao invés de escrever o log temporariamente, devemos salvá-lo em disco para manter um histórico. Bom, já que estamos trabalhando com TStringList, basta alterar o método WriteLog e substituir uma das linhas por SaveToFile, certo?

A rotina irá funcionar?

Hmm… acho que não! O método de envio de e-mail usa a variável FsLog para compor o corpo da mensagem, porém, essa variável não é mais utilizada. Ao executar a rotina, o e-mail será enviado com a mensagem vazia.

Para ajustar esse comportamento, devemos alterar também o método SendMail para anexar o arquivo salvo:

Resultado: duas alterações, que revelam dois motivos para mudar, ou seja, duas responsabilidades.

Aplicando o SRP

Para que essa classe atenda o princípio de responsabilidade única, é preciso extrair cada responsabilidade – escrita de log e envio de e-mail – para classes separadas.

  • TLogger

  • TMailService

Por fim, para enviar o log por e-mail, deve-se utilizar instâncias das duas classes em conjunto:

Pronto, pessoal! Efetuamos os ajustes necessários para atender o Single Responsibility Principle. A classe foi dividida em duas, cada qual recebendo uma única responsabilidade. Observem que as classes ficaram pequenas e fáceis de compreender. Além disso, a classe TMailService tornou-se “genérica” e poderá ser utilizada por outras rotinas, já que não faz mais referência ao log. 🙂

Conclusão

É importante esclarecer que SRP não é aplicado somente para classes. Este mesmo princípio também se destina a métodos, seguindo basicamente o mesmo conceito – se o método realiza duas ou mais funções, cada uma delas deve ser extraída para um método exclusivo. Veja um exemplo bastante simples:

O método exibe um diálogo para selecionar o arquivo e o carrega em um componente TMemo. Duas responsabilidades. Ao aplicar o SRP, o método é fracionado para separá-las:

Embora o código tenha ficado um pouco maior, garanto que as manutenções serão bem menos custosas.
Uma das maiores vantagens é que estes novos métodos podem ser reaproveitados em outros locais do código, assim como acontece com as classes.

Por hoje é só, leitores!
Continuem acompanhando o blog. Um grande abraço!


 

André Celestino