O que é SOLID?

Single Responsability Principle

O nome, embora auto-explicativo e que muitas vezes levava esse princípio a ser explicado como “Uma classe deve ter uma, e apenas uma responsabilidade” não é necessariamente o que você deve pensar na hora de implementá-lo. Entenda esse padrão como : “Uma classe deve ter um, e apenas um motivo para mudar”. Ou seja, não crie uma classe com a função de Emitir Nota Fiscal, crie uma classe NotaFiscal, e garanta que apenas coisas que envolvem o domínio de Nota Fiscal irão altera-lá. Se uma alteração na impressora fizer a classe Nota Fiscal ser alterada, algo está incorreto.

A questão principal do SRP é o motivo para uma classe ser modificada. E esse motivo para mudança, em geral, está relacionado a um grupo de usuários ou stakeholders, que Uncle Bob chama de atores. - Desbravando Solid

Levando em conta o que dizemos acima, também podemos dizer que o SRP pode ser definido por: “Um módulo deve ser responsável por um , e apenas um ator”. O ator que se importa com a função da emissão nota fiscal é o setor de vendas, o ator que se importa com suas horas extras, o rh, o ator que se importa com salvar no banco por id é a implementação de persistência.

Cada "interessado", de negócio ou técnico, faz com que a classe tenha uma responsabilidade diferente.

Identificar classes que mudam por diversos motivos (ou atores) é simples, a coesão pode ser uma das métricas utilizadas:

Classes coesas têm uma característica semelhante: os conceitos que essas classes representam estariam relacionados e separá-los seria pouco natural. O SRP, no fim das contas, é uma outra maneira de falar sobre a necessidade de código coeso. - Desbravando Solid

Aniche (OOP E SOLID para ninjas) sugere que, para encontrar classes pouco coesas, devemos procurar classes que:

Outro fator importante é perceber a duplicação (ou pior, repetição) de código. Se seu código possui partes repetidas diversas vezes, tenha isso como um forte indicativo que essa responsabilidade provavelmente deveria estar encapsulada em algum lugar (e muito provavelmente em uma class).

Portanto, se seu sistema pega os dados, busca coisas no banco, salva como ePUB ou PDF, tudo em uma classe só, ele provavelmente não é coeso e muito menos segue o SRP. Um exemplo disso é que toda vez que a maneira que um pdf for gerado houver de mudar, você terá que mexer nessa classe principal, e se você tiver que repetir essa alteração em diversos pontos onde o código está repetido (o que já não é um bom indicador), você provavelmente terá problemas em algum momento (e mesmo que não tenha, sua manutenção definitivamente não está facilitada).

Caso real

Recentemente ajudei um amigo em um projeto pessoal, onde ele enviava emails para a confirmação de cadastro de usuários, todas essas responsabilidades ficavam dentro da mesma classe Usuário (Gerar token, enviar email, registrar usuário). A partir daí, trabalhamos para termos um código mais coeso, no momento em que um usuário é registrado, ele envia um evento de registro de usuário (o que é só um aviso falando: cadastrei um usuário). Com isso, uma classe chamada enviarEmailListener se prontificava a ouvir eventos de registro e alteração de senha de usuário e ela era a responsável por enviar emails. Portanto, com o refactor, a classe de Usuário não se preocupa com o que acontece após o registro do usuário, ela apenas notifica que isso aconteceu. Outro ponto de melhoria nisso foi a possibilidade de tornar o envio do email assíncrono, então para cadastrar um usuário, não precisavamos esperar o serviço de email fazer sua ação, ela é independente (nesse caso, não fazia sentido ser uma transação, o cadastro de um usuário não depende do email, se um erro ocorrer nessa etapa, ele pode só pedir outro email)