De acordo com a documentação da Microsoft, o artigo de princípio do Wikipedia SOLID, ou a maioria dos arquitetos de TI, devemos garantir que cada classe tenha apenas uma responsabilidade. Gostaria de saber por que, porque se todos parecem concordar com essa regra, ninguém parece concordar com os motivos dessa regra.
Alguns citam melhor manutenção, outros dizem que ele fornece testes fáceis ou torna a classe mais robusta ou de segurança. O que é correto e o que realmente significa? Por que torna a manutenção melhor, os testes mais fáceis ou o código mais robusto?
design-patterns
architecture
solid
single-responsibility
Bastien Vandamme
fonte
fonte
Respostas:
Modularidade. Qualquer linguagem decente fornecerá os meios para colar partes de código, mas não há maneira geral de descolar uma grande parte de código sem que o programador faça uma cirurgia na fonte. Ao colocar várias tarefas em uma construção de código, você aproveita a oportunidade de combinar suas partes de outras maneiras e introduz dependências desnecessárias que podem causar alterações em uma parte e afetar as outras.
O SRP é tão aplicável às funções quanto às classes, mas as principais linguagens OOP são relativamente pobres em colar funções.
fonte
Melhor manutenção, teste fácil, correção mais rápida de erros são apenas resultados (muito agradáveis) da aplicação do SRP. O principal motivo (como Robert C. Matin coloca) é:
Em outras palavras, o SRP aumenta a mudança de localidade .
SRP também promove código DRY. Contanto que tenhamos classes com apenas uma responsabilidade, poderemos optar por usá-las em qualquer lugar que desejarmos. Se temos uma classe que tem duas responsabilidades, mas precisamos apenas de uma delas e a segunda está interferindo, temos 2 opções:
fonte
É fácil criar código para corrigir um problema específico. É mais complicado criar código que corrige esse problema e permite que alterações posteriores sejam feitas com segurança. O SOLID fornece um conjunto de práticas que aprimoram o código.
Quanto a qual deles está correto: Todos os três. Todos são benefícios do uso de responsabilidade única e o motivo pelo qual você deve usá-lo.
Quanto ao que eles significam:
Tente criar código por um tempo, seguindo o princípio e revisite-o posteriormente para fazer algumas alterações. Você verá a enorme vantagem que ele oferece.
Você faz o mesmo para cada classe e acaba com mais classes, todas em conformidade com o SRP. Isso torna a estrutura mais complexa do ponto de vista das conexões, mas a simplicidade de cada classe a justifica.
fonte
Aqui estão os argumentos que, na minha opinião, apoiam a alegação de que o Princípio de Responsabilidade Única é uma boa prática. Também forneço links para mais literatura, onde você pode ler raciocínios ainda mais detalhados - e mais eloquentes que os meus:
Melhor manutenção : idealmente, sempre que uma funcionalidade do sistema precisar mudar, haverá uma e apenas uma classe que deverá ser alterada. Um mapeamento claro entre classes e responsabilidades significa que qualquer desenvolvedor envolvido no projeto pode identificar qual classe é essa. (Como notou @ MaciejChałapuk, consulte Robert C. Martin, "Código Limpo" .)
Teste mais fácil : idealmente, as classes devem ter uma interface pública tão mínima quanto possível, e os testes devem abordar apenas essa interface pública. Se você não pode testar com clareza, porque muitas partes da sua classe são privadas, 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 aos idiomas em que não há alunos 'públicos' ou 'particulares'; Ter uma pequena interface pública significa que é muito claro para o código do cliente quais partes da classe ela deve usar. (Consulte Kent Beck, "Desenvolvimento Orientado a Testes" para obter mais detalhes.)
Código robusto : seu código não falhará com mais ou menos frequência porque está bem escrito; Mas, como todo o código, seu objetivo final é não se comunicar com a máquina , mas com os desenvolvedores do companheiro (ver Kent Beck, "padrões de implementação" , Capítulo 1.) A base de código claro é mais fácil raciocinar sobre, por isso vai ser introduzido menos bugs , e menos tempo passará entre a descoberta e a correção de um bug.
fonte
Existem várias razões, mas a que eu mais gosto é a abordagem usada por muitos dos primeiros programas UNIX: Faça uma coisa bem. Já é bastante difícil fazer isso com uma coisa, e aumentando a dificuldade, mais coisas você tenta fazer.
Outro motivo é limitar e controlar os efeitos colaterais. Eu amei minha combinação de máquina de café abridor de porta. Infelizmente, o café geralmente transbordava quando recebi visitas. Esqueci de fechar a porta depois que fiz café no outro dia e alguém a roubou.
Do ponto de vista psicológico, você pode acompanhar apenas algumas coisas de cada vez. As estimativas gerais são sete mais ou menos dois. Se uma classe faz várias coisas, você precisa acompanhar todas elas de uma vez. Isso reduz sua capacidade de rastrear o que você está fazendo. Se uma classe fizer três coisas e você desejar apenas uma delas, poderá 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. Desse ponto de vista, você deseja que as aulas 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 falha.
fonte
Porque o software é orgânico. Os requisitos mudam constantemente para que você tenha que manipular os componentes com a menor 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ê remove esse muro sem nenhum apoio? A casa provavelmente entrará em colapso. No software, não queremos isso; portanto, estruturamos os aplicativos de maneira que você possa mover / substituir / modificar componentes facilmente sem causar muitos danos.
fonte
Eu sigo o pensamento:
1 Class = 1 Job
.Usando uma analogia de 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 tem apenas uma responsabilidade, seja para gerenciar a maneira como cada um dos seus respectivos subsistemas funciona ou se é um subsistema de ponto final e executa apenas uma tarefa, como levantar um dedo ou cultivar um folículo piloso.
Não confunda o fato de que pode estar atuando como gerente, e não como trabalhador. Alguns trabalhadores acabam sendo promovidos a gerente, quando o trabalho que eles estavam executando se tornou muito complicado para um processo lidar sozinho.
A parte mais complicada do que experimentei é saber quando designar uma classe como processo de operador, supervisor ou gerente. Independentemente, 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ê descobrirá que o processo geral começará a ter problemas de desempenho ou processar gargalos.
fonte
Lung
classe, eu diria que uma instância dePerson
deveria aindaBreathe
e, portanto, aPerson
classe deve conter lógica suficiente para, no mínimo, delegar as responsabilidades associadas a esse método. Eu sugeriria ainda que uma floresta de entidades interconectadas que são acessíveis apenas através de um proprietário comum geralmente é mais fácil de raciocinar do que uma floresta que possui vários pontos de acesso independentes.Person
classe tem como função delegar toda a funcionalidade associada a uma entidade monstruosamente complicada do tipo "ser humano do mundo real", incluindo a associação de suas várias partes . Ter uma entidade fazendo 500 coisas é feio, mas se é assim que funciona o sistema do mundo real, ter uma classe que delega 500 funções enquanto mantém uma identidade pode ser melhor do que ter que fazer tudo com peças desunidas.Person
mas ainda reportar a cadeia sobre sucesso / fracasso, mas não necessariamente afetando o outro. 500 funções (embora definidas como exemplo, seriam exageradas e não suportáveis), tento manter esse tipo de funcionalidade derivado e modular.Fred.vision.lookAt(George)
faz sentido, mas permitindo que o código para dizersomeEyes = Fred.vision; someTubes = Fred.digestion
parece icky uma vez que obscurece a relação entresomeEyes
esomeTubes
.Especialmente com princípios tão importantes como a responsabilidade única, eu pessoalmente esperaria que haja muitas razões pelas quais as pessoas adotam esse princípio.
Alguns desses motivos podem ser:
Observe também que o SRP vem com um conjunto de princípios do SOLID. Seguir o SRP e ignorar o resto é tão ruim quanto não fazê-lo em primeiro lugar. Portanto, você não deve avaliar o SRP por si só, mas no contexto de todos os princípios do SOLID.
fonte
... test is not affected by other responsibilities the class has
, você poderia, por favor, elaborar este?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. À medida que meus programas cresciam, 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 por toda parte. Adicionar algo novo ou remover algo antigo traria erros nas outras partes do programa. Vai saber.
Em pequenos projetos, isso pode não importar, mas em grandes projetos as coisas podem ser muito pesadelos. 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 então".
Você realmente não consegue entender a importância dos padrões de design até que seja necessário. Eu respeito os padrões porque, por experiência, posso dizer que eles tornam a manutenção de código fácil e robusta.
No entanto, assim como você, ainda estou incerto sobre o "teste fácil", porque ainda não tive a necessidade de fazer testes de unidade.
fonte
A resposta é, como outros já apontaram, todos eles estão corretos, e todos se alimentam, mais fáceis de testar facilitam a manutenção tornam mais fácil o código, mais robusto facilita a manutenção e assim por diante ...
Tudo isso se resume a um código principal - o código deve ser tão pequeno e fazer o mínimo necessário para concluir o trabalho. Isso se aplica a um aplicativo, um arquivo ou uma classe, tanto quanto a uma função. Quanto mais coisas um 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 menor o escopo em qualquer ponto específico de um aplicativo, melhor.
Escopo expandido = mais complexidade = mais maneiras de as coisas darem errado.
fonte
Se uma classe é 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 você logo enfrenta problemas com classes muito complexas.
No entanto, ter uma regra simples de “apenas uma responsabilidade” facilita saber quando você precisa de uma nova classe .
Por mais que definir “responsabilidade” seja difícil, isso não significa “faça tudo o que a especificação do aplicativo diz”, a verdadeira habilidade é saber como dividir o problema em pequenas unidades de “responsabilidade”.
fonte