Assegure-se de que cada classe tenha apenas uma responsabilidade, por quê?

36

De acordo com a documentação da Microsoft, o artigo principal do Wikipedia SOLID, ou a maioria dos arquitetos de TI, devemos garantir que cada classe tenha apenas uma responsabilidade. Eu gostaria de saber por que, porque se todos parecem concordar com essa regra, ninguém parece concordar sobre as razões dessa regra.

Alguns citam melhor manutenção, alguns dizem que fornece testes fáceis ou torna a classe mais robusta, ou segurança. O que está correto e o que isso realmente significa? Por que melhorar a manutenção, testar mais facilmente ou o código é mais robusto?

    
por Bastien Vandamme 07.04.2014 / 10:38
fonte

11 respostas

57

Modularidade. Qualquer linguagem decente lhe dará os meios para colar pedaços de código, mas não há uma maneira geral de descongelar um grande pedaço de código sem que o programador realize uma cirurgia na fonte. Ao colocar várias tarefas em uma única construção de código, você tira a si e aos outros a oportunidade de combinar suas peças de outras maneiras e introduzem dependências desnecessárias que podem fazer com que alterações em uma peça afetem as outras.

O SRP é tão aplicável a funções quanto a classes, mas as linguagens OOP tradicionais são relativamente ruins para a colagem de funções.

    
por 07.04.2014 / 13:53
fonte
30

Melhor manutenção, testes fáceis, correção mais rápida de bugs são apenas resultados (muito agradáveis) da aplicação do SRP. A principal razão (como diz Robert C. Matin) é:

A class should have one, and only one, reason to change.

Em outras palavras, o SRP aumenta a localidade de alteração .

O SRP também promove o código DRY. Contanto que tenhamos aulas que tenham apenas uma responsabilidade, podemos optar por usá-las onde quisermos. Se temos uma turma que tem duas responsabilidades, mas precisamos apenas de uma delas e a segunda é a interferência, temos duas opções:

  1. Copie e cole a turma em outra e talvez até crie outro mutante de responsabilidade mútua (eleve um pouco a dívida técnica).
  2. Divida a turma e torne-a como deveria, em primeiro lugar, o que pode ser caro devido ao uso extensivo da aula original.
por 07.04.2014 / 12:31
fonte
21

É fácil criar código para corrigir um problema específico. É mais complicado criar código que corrija esse problema, permitindo que alterações posteriores sejam feitas com segurança. O SOLID fornece um conjunto de práticas que melhoram o código.

Quanto a qual está correto: todos os três. Eles são todos benefícios de usar uma única responsabilidade e o motivo pelo qual você deve usá-la.

Quanto ao que eles querem dizer:

  • Melhor manutenção significa que é mais fácil de alterar e não muda com frequência. Como há menos código e esse código é focado em algo específico, se você precisar alterar algo que não esteja relacionado à classe, a classe não precisará ser alterada. Além disso, quando você precisa alterar a classe, desde que não precise alterar a interface pública, só precisa se preocupar com essa classe e nada mais.
  • Teste fácil significa menos testes, menos configuração. Não há tantas partes móveis na classe, portanto, o número de possíveis falhas na classe é menor e, portanto, menos casos que você precisa testar. Haverá menos campos / membros privados para configurar.
  • Por causa dos dois acima, você obtém uma classe que muda menos e falha menos e, portanto, é mais robusta.

Tente criar código por um tempo seguindo o princípio e revisite o código mais tarde para fazer algumas alterações. Você verá a enorme vantagem que isso proporciona.

Você faz o mesmo com cada turma e termina com mais turmas, todas cumprindo o SRP. Isso torna a estrutura mais complexa do ponto de vista das conexões, mas a simplicidade de cada classe justifica isso.

    
por 07.04.2014 / 11:04
fonte
4

Aqui estão os argumentos que, em minha opinião, apóiam a afirmação de que o Princípio de Responsabilidade Única é uma boa prática. Eu também forneço links para mais literatura, onde você pode ler raciocínios ainda mais detalhados - e mais eloquentes do que os meus:

  • Melhor manutenção : idealmente, sempre que uma funcionalidade do sistema tiver que mudar, haverá uma e apenas uma classe que precisa ser alterada. Um mapeamento claro entre classes e responsabilidades significa que qualquer desenvolvedor envolvido no projeto pode identificar qual classe é essa. (Como observou @ MaciejChałapuk, consulte Robert C. Martin," Código Limpo ".)

  • Teste mais fácil : idealmente, as classes devem ter o mínimo de interface pública possível, e os testes devem abordar apenas essa interface pública. Se você não pode testar com clareza, porque muitas partes de sua classe são particulares, isso é um sinal claro de que sua classe tem muitas responsabilidades e que você deve dividi-la em subclasses menores. Observe que isso se aplica também a idiomas em que não há membros da classe 'públicos' ou 'privados'; ter uma pequena interface pública significa que é muito claro para o código do cliente quais partes da classe devem ser usadas. (Consulte Kent Beck," Desenvolvimento Orientado por Testes " para mais detalhes.)

  • Código robusto : seu código não falhará mais ou menos frequentemente porque está bem escrito; mas, como todo código, seu objetivo final é não se comunicar com a máquina , mas com outros desenvolvedores (veja Kent Beck," Padrões de Implementação ", Capítulo 1.) codebase é mais fácil de se pensar, então menos bugs serão introduzidos, e menos tempo irá passar entre descobrir um bug e corrigi-lo.

por 07.04.2014 / 15:29
fonte
3

Existem vários motivos, mas o que eu gosto é a abordagem usada por muitos dos primeiros programas UNIX: Faça uma coisa bem. É muito difícil fazer isso com uma coisa e, quanto mais difícil, mais coisas você tenta fazer.

Outro motivo é limitar e controlar os efeitos colaterais. Eu amei o meu abridor de porta de combinação de cafeteira. Infelizmente, o café geralmente transbordava quando eu tinha visitantes. Eu esqueci de fechar a porta depois que fiz café no outro dia e alguém roubou.

Do ponto de vista psicológico, você só pode acompanhar algumas coisas por vez. Estimativas gerais são sete mais ou menos dois. Se uma turma faz várias coisas, você precisa acompanhar todas elas de uma só vez. Isso reduz sua capacidade de acompanhar o que você está fazendo. Se uma classe faz três coisas, e você quer apenas uma delas, você pode esgotar sua capacidade de acompanhar as coisas antes de realmente fazer qualquer coisa com a classe.

Fazer várias coisas aumenta a complexidade do código. Além do código mais simples, aumentar a complexidade aumenta a probabilidade de erros. Deste ponto de vista, você quer que as classes sejam o mais simples possível.

Testar uma classe que faz uma coisa é muito mais simples. Você não precisa verificar se a segunda coisa que a classe fez ou não aconteceu em todos os testes. Você também não precisa corrigir as condições quebradas e testar novamente quando um desses testes falhar.

    
por 08.04.2014 / 02:45
fonte
2

Porque o software é orgânico. Os requisitos mudam constantemente, então você tem que manipular os componentes com o mínimo de dor de cabeça possível. Ao não seguir os princípios do SOLID, você pode acabar com uma base de código definida em concreto.

Imagine uma casa com uma parede de concreto com carga. O que acontece quando você tira esse muro sem qualquer apoio? A casa provavelmente entrará em colapso. Em software, não queremos isso, portanto, estruturamos os aplicativos de maneira que você possa mover / substituir / modificar componentes facilmente, sem causar muitos danos.

    
por 07.04.2014 / 13:44
fonte
1

Eu sigo o pensamento: 1 Class = 1 Job .

Usando uma Analogia da Fisiologia: Motor (Sistema Neural), Respiração (Pulmões), Digestivo (Estômago), Olfativo (Observação), etc. Cada um deles terá um subconjunto de controladores, mas cada um deles tem apenas uma responsabilidade, seja É para gerenciar a maneira como cada um de seus respectivos subsistemas funciona ou se eles são um subsistema de ponto final e executam apenas uma tarefa, como levantar um dedo ou cultivar um folículo piloso.

Não confunda o fato de que pode estar agindo como um gerente em vez de um trabalhador. Alguns funcionários acabam sendo promovidos a gerente, quando o trabalho que estavam realizando se tornou muito complicado para um processo sozinho.

A parte mais complicada do que experimentei é saber quando designar um processo de Classe como Operador, Supervisor ou Gerente. Independentemente disso, você precisará observar e denotar sua funcionalidade para 1 responsabilidade (Operador, Supervisor ou Gerente).

Quando uma Classe / Objeto executa mais de uma dessas funções de tipo, você verá que o processo geral começará a ter problemas de desempenho ou a gargalos no processo.

    
por 07.04.2014 / 17:44
fonte
1

Especialmente com um princípio tão importante quanto a responsabilidade única, eu pessoalmente esperaria que houvesse muitas razões pelas quais as pessoas adotam esse princípio.

Algumas dessas razões podem ser:

  • Manutenção - o SRP garante que a mudança de responsabilidade em uma aula não afeta outras responsabilidades, tornando a manutenção mais simples. Isso porque, se cada turma tem apenas uma responsabilidade, as mudanças feitas em uma única responsabilidade são isoladas de outras responsabilidades.
  • Teste - Se uma turma tem uma responsabilidade, fica muito mais fácil descobrir como testar essa responsabilidade. Se uma turma tiver múltiplas responsabilidades, você deve certificar-se de que está testando a correta e que o teste não é afetado por outras responsabilidades que a turma tenha.

Observe também que o SRP vem com um conjunto de princípios do SOLID. Respeitar o SRP e ignorar o resto é tão ruim quanto não fazer o SRP em primeiro lugar. Portanto, você não deve avaliar o SRP sozinho, mas no contexto de todos os princípios do SOLID.

    
por 07.04.2014 / 11:05
fonte
1

A melhor maneira de entender a importância desses princípios é ter a necessidade.

Quando eu era um programador iniciante, não pensava muito no design, na verdade, nem sabia que existiam padrões de design. Como meus programas cresceram, mudar uma coisa significava mudar muitas outras coisas. Era difícil rastrear bugs, o código era enorme e repetitivo. Não havia muita hierarquia de objetos, as coisas estavam em todo lugar. Adicionar algo novo ou remover algo antigo traria erros nas outras partes do programa. Vai a figura.

Em projetos pequenos, pode não importar, mas em grandes projetos, as coisas podem ser muito apavorantes. Mais tarde, quando me deparei com os conceitos de padrões de design, eu disse a mim mesmo: "Ah, sim, fazer isso tornaria as coisas muito mais fáceis do que isso".

Você realmente não consegue entender a importância dos padrões de design até que surja a necessidade. Respeito os padrões porque, por experiência, posso dizer que eles facilitam a manutenção do código e o código é robusto.

No entanto, assim como você, ainda não tenho certeza sobre o "teste fácil", porque ainda não tive a necessidade de testar a unidade.

    
por 08.04.2014 / 00:37
fonte
1

A resposta é, como outros apontaram, todos eles estão corretos, e todos eles se alimentam um ao outro, mais fácil de testar facilita a manutenção, torna o código mais robusto, facilita a manutenção e assim por diante ...

Tudo isso se resume a uma chave principal - o código deve ser pequeno e fazer o mínimo necessário para realizar o trabalho. Isso se aplica a um aplicativo, um arquivo ou uma classe, tanto quanto a uma função. Quanto mais coisas uma parte do código faz, mais difícil é entender, manter, estender ou testar.

Eu acho que pode ser resumido em uma palavra: escopo. Preste muita atenção ao escopo dos artefatos, quanto menos coisas no escopo em qualquer ponto específico de um aplicativo, melhor.

Escopo expandido = mais complexidade = mais maneiras de dar errado.

    
por 08.04.2014 / 08:27
fonte
1

Se uma turma é muito grande, fica difícil manter, testar e entender, outras respostas cobriram essa vontade.

É possível que uma classe tenha mais de uma responsabilidade sem problemas, mas logo você tem problemas com classes muito complexas.

No entanto, ter uma regra simples de "apenas uma responsabilidade" só torna mais fácil saber quando você precisa de uma nova classe .

No entanto, definir “responsabilidade” é difícil, mas não significa “fazer tudo o que a especificação da aplicação diz”, a verdadeira habilidade está em saber como dividir o problema em pequenas unidades de “responsabilidade”.

    
por 08.04.2014 / 12:49
fonte