Escrevendo classes coesas aplicando SRP


#OOP

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.