Escrevendo classes coesas aplicando SRP
Com certeza, a coesão é uma das palavras amplamente reconhecidas pelos programadores que trabalham com linguagens orientadas a objeto. Seu significado é bastante claro: uma classe é considerada coesa quando possui uma única responsabilidade, evitando abranger mais de um contexto no sistema. Por exemplo, se uma classe é encarregada de representar uma nota fiscal, ela se dedica exclusivamente a essa tarefa, enquanto as responsabilidades relacionadas a uma fatura estarão atribuídas a outra classe.
É indiscutivel a necessidade de se criar entidades coesas em um sistema orientado a objetos. Os motivos são:
- são mais simples de serem mantidas;
- possuem menos código;
- maior reusabilidade;
Considere o exemplo abaixo:
class Student {
registerStudent() {
// some logic
}
calculateStudentResults() {
// some logic
}
sendEmail() {
// some logic
}
}
Por que a classe acima viola o princípio da responsabilidade única? Esta classe Student tem três responsabilidades:
- registrar alunos;
- calcular seus resultados;
- enviar e-mails aos alunos;
Embora o código acima funciona perfeitamente, porém ele apresentará alguns desafios significativos. A reutilização desse código em outras classes ou objetos não é possível, pois a classe possui uma lógica muito interconectada, dificultando a correção de erros. À medida que a base de código cresce, a complexidade da lógica também aumenta, tornando ainda mais complicado compreender o que está ocorrendo.
Considere um novo membro da equipe de desenvolvimento se juntando a um projeto com esse tipo de lógica, onde a base de código contém cerca de duas mil linhas, todas confinadas em uma única classe 🤬.
Agora vamos consertar isso!
class StudentRegister {
registerStudent() {
// some logic
}
}
class StudentResult {
calculateStudentResult() {
// some logic
}
}
class StudentEmails {
sendEmail() {
// some logic
}
}
Agora separamos cada funcionalidade em nosso programa. Podemos chamar as classes em qualquer lugar que quisermos para usá-las em nosso código.
Os exemplos que usamos mostram apenas que cada classe tem um método – isso é principalmente para simplificar. Você pode ter quantos métodos quiser, mas eles devem estar vinculados à responsabilidade da classe.
Agora que separamos a lógica, nosso código fica mais fácil de entender, pois cada funcionalidade principal tem sua própria classe. Podemos testar os erros com mais eficiência. O código agora é reutilizável. Antes, só podíamos usar essas funcionalidades dentro de uma classe, mas agora elas podem ser usadas em qualquer classe. O código também é de fácil manutenção e escalabilidade porque, em vez de ler linhas de código interconectadas, separamos preocupações para que possamos nos concentrar nos recursos nos quais queremos trabalhar.