Para referência - http://en.wikipedia.org/wiki/Single_responsibility_principle
Eu tenho um cenário de teste em que em um módulo do aplicativo é responsável pela criação de entradas do razão. Existem três tarefas básicas que podem ser realizadas -
- Veja as entradas do razão existentes no formato de tabela.
- Crie uma nova entrada no razão usando o botão criar.
- Clique em uma entrada do razão na tabela (mencionada no primeiro ponteiro) e veja seus detalhes na próxima página. Você pode anular uma entrada do razão nesta página.
(Existem mais algumas operações / validações em cada página, mas, por uma questão de brevidade, vou limitá-la a estas)
Então eu decidi criar três classes diferentes -
- LedgerLandingPage
- CreateNewLedgerEntryPage
- ViewLedgerEntryPage
Essas classes oferecem os serviços que poderiam ser realizados nessas páginas e os testes do Selenium usam essas classes para levar a aplicação a um estado em que eu poderia fazer certa afirmação.
Quando eu estava analisando o artigo com meu colega, ele ficou abatido e me pediu para fazer uma aula única para todos. Embora eu ainda sinta que meu design é muito limpo, duvido que esteja usando demais o princípio de responsabilidade única
fonte
Respostas:
Citando @YannisRizos aqui :
Discordo, pelo menos agora, após cerca de 30 anos de experiência em programação. Classes menores IMHO são melhores para manter
em quase todos os casos em que consigo pensarem casos como este. Quanto mais funções você colocar em uma classe, menos estrutura terá e a qualidade do seu código será degradada - a cada dia um pouco. Quando entendi Tarun corretamente, cada uma dessas três operaçõesé um caso de uso, cada uma dessas classes consiste em mais de uma função para executar a tarefa relacionada e cada uma pode ser desenvolvida e testada separadamente. Assim, a criação de classes para cada um deles agrupa as coisas que pertencem uma à outra, facilitando a identificação da parte do código a ser alterada se algo mudar.
Se você possui funcionalidades comuns entre essas 3 classes, elas pertencem a uma classe base comum, à
Ledger
própria classe (presumo que você tenha uma, pelo menos, para as operações CRUD) ou a uma classe auxiliar separada.Se você precisar de mais argumentos para criar muitas classes menores, recomendo dar uma olhada no livro de Robert Martin, "Clean Code" .
fonte
Não há uma implementação definitiva do Princípio da Responsabilidade Única, ou qualquer outro princípio. Você deve decidir com base no que é mais provável que seja alterado.
Como o @Karpie escreve em uma resposta anterior :
Essa é uma abordagem instintiva e provavelmente correta, pois o mais provável é mudar a lógica de negócios do gerenciamento de livros contábeis. Se isso acontecer, seria muito mais fácil manter uma classe do que três.
Mas isso deve ser examinado e decidido por caso. Você realmente não deve se preocupar com preocupações com minúsculas possibilidades de mudança e se concentrar na aplicação do princípio sobre o que é mais provável de mudar. Se a lógica do gerenciamento de livros contábeis for um processo multicamada e as camadas tendem a mudar independentemente ou são preocupações que devem ser separadas em princípio (lógica e apresentação), você deve usar classes separadas. É um jogo de probabilidade.
Uma pergunta semelhante sobre o uso excessivo dos princípios de design, que acho interessante: Padrões de design: quando usar e quando parar de fazer tudo usando padrões
fonte
Vamos examinar estas classes:
LedgerLandingPage: O papel desta classe é mostrar uma lista de ledgers. À medida que o aplicativo cresce, você provavelmente desejará adicionar métodos de classificação, filtragem, destaque e fusão transparente de dados de outras fontes de dados.
ViewLedgerEntryPage: Esta página mostra o razão em detalhes. Parece bem direto. Contanto que os dados sejam diretos. Você pode anular o razão aqui. Você também pode querer corrigi-lo. Ou comente ou anexe um arquivo, recibo ou número de reserva externo ou qualquer outra coisa. E depois de permitir a edição, você definitivamente deseja mostrar um histórico.
CreateLedgerEntryPage: se a
ViewLedgerEntryPage
capacidade de edição estiver completa, a responsabilidade dessa classe poderá ser cumprida editando uma "entrada em branco", que pode ou não fazer sentido.Esses exemplos podem parecer um pouco exagerados, mas o ponto é que, de um UX, estamos falando de recursos aqui. Um único recurso é definitivamente uma responsabilidade distinta e única. Como os requisitos para esse recurso podem mudar e os requisitos de dois recursos diferentes podem mudar para duas direções opostas, e você deseja ter aderido ao SRP desde o início.
Por outro lado, você sempre pode colocar uma fachada em três classes, se ter uma única interface for mais conveniente por qualquer motivo que você puder imaginar.
Existe o uso excessivo de SRP : se você precisar de uma dúzia de classes para ler o conteúdo de um arquivo, é bastante seguro assumir que você está fazendo exatamente isso.
Se você olhar para o SRP do outro lado, na verdade, diz que uma única responsabilidade deve ser assumida por uma única classe. Observe, no entanto, que as responsabilidades existem apenas dentro dos níveis de abstração. Portanto, faz sentido que uma classe de um nível de abstração mais alto cumpra sua responsabilidade delegando o trabalho ao componente de nível inferior, o que é melhor alcançado através da inversão de dependência .
fonte
Essas classes têm apenas um motivo para mudar?
Se você ainda não o conhece, pode ter entrado na armadilha especulativa de design / sobre engenharia (muitas classes, padrões de design muito complexos aplicados). Você não satisfez YAGNI . Nos estágios iniciais do ciclo de vida do software (alguns), os requisitos podem não estar claros. Portanto, a direção da mudança atualmente não é visível para você. Se você perceber isso, mantenha-o em uma ou em uma quantidade mínima de aulas. No entanto, isso traz uma responsabilidade: assim que os requisitos e a direção da mudança forem claros, é necessário repensar o design atual e fazer uma refatoração para satisfazer o motivo da mudança.
Se você já sabe que existem três razões diferentes para a mudança e que elas são mapeadas para essas três classes, basta aplicar a dose certa de SRP. Novamente, isso traz alguma responsabilidade: se você estiver errado ou os requisitos futuros mudarem inesperadamente, será necessário repensar o design atual e fazer uma refatoração para satisfazer o motivo da mudança.
O ponto inteiro é:
Se você tiver essa mentalidade, modelará o código sem muito medo de design especulativo / sobre engenharia.
No entanto, sempre há o fator tempo: para muitas alterações, você não terá o tempo necessário. A refatoração custa tempo e esforço. Muitas vezes encontrei situações em que sabia que precisava alterar o design, mas devido a restrições de tempo, tive que adiá-lo. Isso vai acontecer e não pode ser evitado. Descobri que é mais fácil refatorar sob código de engenharia do que sobre código de engenharia. O YAGNI me fornece uma boa orientação: em caso de dúvida, mantenha a quantidade de classes e a aplicação de padrões de design no mínimo.
fonte
Há três motivos para chamar o SRP como um motivo para dividir uma classe:
Há três razões para se recusar a dividir uma classe:
Quando olho para o seu caso e imagino alterações, algumas delas causarão alterações em várias classes (por exemplo, adicione um campo ao razão, você precisará alterar as três), mas muitas não o farão - por exemplo, adicionando classificação à lista de registros ou alterando as regras de validação ao adicionar uma nova entrada do razão. A reação do seu colega é provavelmente porque é difícil saber onde procurar um bug. Se algo parece errado na lista de livros, é porque foi adicionado incorretamente ou há algo errado com a lista? Se houver uma discrepância entre o resumo e os detalhes, o que está errado?
Também é possível que seu colega pense que as alterações nos requisitos, o que significa que você precisará alterar todas as três classes, são muito mais prováveis do que as alterações nas quais você mudaria apenas uma. Essa poderia ser uma conversa realmente útil para se ter.
fonte
Eu acho que se o ledger fosse uma tabela em db, sugiro colocar essas tarefas thress em uma classe (por exemplo, DAO). Se o ledger tiver mais lógica e não for uma tabela em db, sugiro que devemos criar mais classe para executar essas tarefas (pode ser de duas ou três classes) e forneça uma classe de fachada simplesmente para o cliente.
fonte
IMHO você não deveria estar usando aulas. Não há abstrações de dados aqui. Os ledgers são um tipo de dados concreto. Os métodos para manipulá-los e exibi-los são funções e procedimentos. Certamente haverá muitas funções comumente usadas para serem compartilhadas, como formatar uma data. Há pouco espaço para abstrair e ocultar dados em uma classe nas rotinas de exibição e seleção.
Há algum caso para ocultar o estado em uma classe para edição do razão.
Quer você esteja abusando ou não do SRP, você está abusando de algo mais fundamental: você está usando demais a abstração. É um problema típico, gerado pela noção mítica OO é um paradigma de desenvolvimento são.
A programação é 90% concreta: o uso da abstração de dados deve ser raro e cuidadosamente projetado. A abstração funcional, por outro lado, deve ser mais comum, pois os detalhes da linguagem e a escolha dos algoritmos precisam ser separados da semântica da operação.
fonte
Você só precisa de uma classe, e a única responsabilidade dessa classe é gerenciar registros .
fonte