Como determinar se uma classe atende ao princípio de responsabilidade única?


34

O princípio da responsabilidade única baseia-se no princípio da alta coesão. A diferença entre as duas é que uma classe altamente coesa apresenta um conjunto de responsabilidades fortemente relacionadas, enquanto as classes aderentes ao SRP têm apenas uma responsabilidade.

Mas como determinamos se uma classe específica apresenta um conjunto de responsabilidades e, portanto, é altamente coesa ou se possui apenas uma responsabilidade e, portanto, adere ao SRP? Em outras palavras, não é mais ou menos subjetiva, pois alguns podem considerar uma classe muito granular (e, como tal, acreditarão que a classe adere ao SRP), enquanto outros podem considerá-la não granular o suficiente?



Respostas:


21

Por que sim, é muito subjetivo e é o assunto de muitos debates acalorados e enfrentados pelos programadores.

Não há realmente nenhuma resposta, e a resposta pode mudar à medida que seu software se torna mais complexo. O que antes era uma única tarefa bem definida pode eventualmente se tornar várias tarefas mal definidas. Isso é sempre o problema também. Como você escolhe a maneira correta de dividir um programa em tarefas?

O único conselho que posso dar é o seguinte: use seu melhor julgamento (e de seus colegas de trabalho). E lembre-se de que os erros podem (geralmente) ser corrigidos se você os detectar em breve.


Eu gostaria que a ciência da computação fosse mais como a ciência real. A subjetividade não tem lugar na ciência real. Embora os princípios do SOLID sejam excelentes por si só, eles precisam ser repetidos para minimizar a subjetividade e maximizar a objetividade. Isso ainda não aconteceu, o que me faz questionar sua legitimidade no mundo real.
DarkNeuron

13

Bob Martin (tio Bob), que originou os princípios SOLID dos quais o SRP é o primeiro, diz sobre isso (estou parafraseando, não consigo lembrar as palavras reais):

Uma classe deve ter apenas um motivo para mudar

Se tiver mais de um motivo, não aderirá ao SRP.


14
Isso é apenas repetir a definição, na verdade, mas aderir ao srp ainda é bastante subjetivo.
Andy

7

Eu posso lhe dar várias regras de ouro.

  • Quão fácil é nomear a classe? Se uma classe é difícil de nomear, provavelmente está fazendo muito.
  • Quantos métodos públicos a classe possui? 7 +/- 2 é uma boa regra de ouro. Se a classe tiver mais do que isso, pense em dividi-la em várias classes.
  • Existem grupos coesos de métodos públicos usados ​​em contextos separados?
  • Quantos métodos privados ou membros de dados existem? Se a classe tiver uma estrutura interna complexa, você provavelmente deve refatorá-la para que os internos sejam empacotados em classes menores separadas.
  • E a regra mais fácil: qual é o tamanho da turma? Se você tiver um arquivo de cabeçalho C ++ que contenha uma única classe com mais de algumas centenas de linhas, provavelmente deverá dividi-lo.

2
Em relação ao seu segundo ponto, consulte uxmyths.com/post/931925744/…
Cameron Martin

7
Discordo totalmente de 7 +/- 2 - o princípio da responsabilidade única é sobre coesão semântica, não sobre números arbitrários.
JacquesB

1
A regra de ouro não precisa de prova científica independente. O método científico moderno tem séculos de idade, a arquitetura e a engenharia têm milênios. A regra geral para métodos públicos é "vários" e nenhum dos parâmetros é "alguns". Em outras notícias, embora alguns desenhos de crianças mostrem o contrário, os braços das pessoas não saem de suas cabeças [citação necessário].
gillifirca abuzittin

@CameronMartin Dependendo da sua configuração, a interface de uma classe pode ou não estar prontamente disponível para você ler. Pesquisando uma interface do usuário não é o mesmo que escrever código - se eu precisar consultar a documentação a cada minuto, estou dobrando, pelo menos, o tempo necessário para realizar qualquer trabalho real.
Clearer

6

Os Princípios de Responsabilidade Única afirmam que cada módulo de software deve ter apenas um motivo para mudar. Em um artigo recente, o tio Bob explicou "razões para mudar",

No entanto, ao pensar sobre esse princípio, lembre-se de que as razões da mudança são as pessoas. São as pessoas que solicitam mudanças. E você não deseja confundir essas pessoas, ou a si mesmo, misturando o código com o qual muitas pessoas diferentes se importam por diferentes razões.

Ele explicou ainda o conceito com um exemplo AQUI .


Esse é um ótimo artigo, escrito pelo próprio homem.
MrDustpan

4

Para responder a isso, dê um passo atrás e considere a intenção do princípio de responsabilidade única. Por que é um princípio de design recomendado em primeiro lugar?

O objetivo do princípio é "compartimentar" a base de código; portanto, o código relacionado a uma única "responsabilidade" é isolado em uma única unidade. Isso facilita a localização e a compreensão do código e, mais importante, significa que as alterações na "responsabilidade" afetarão apenas uma única unidade de código.

O que você absolutamente nunca deseja em um sistema é quando, quando uma pequena chance, faz com que outra parte do código aparentemente não relacionada falhe ou mude o comportamento. O SRP ajuda a isolar bugs e alterações.

Então, o que é uma "responsabilidade" então? É algo que pode ser concebível mudar independentemente de outras mudanças. Digamos que você tenha um programa que pode salvar algumas configurações em um arquivo de configuração XML e pode lê-las novamente no arquivo. Essa é uma responsabilidade única ou é "carregar" e "salvar" duas responsabilidades diferentes? Qualquer tipo de alteração no formato ou na estrutura do arquivo exigiria alterações na lógica de carregamento e gravação. Portanto, é uma responsabilidade única que deve ser representada por uma única classe. Agora considere um aplicativo que pode exportar alguns dados nos formatos CVS, Excel e XML. Nesse caso, é fácil imaginar que um formato possa mudar sem afetar o outro. Se você decidir alterar o delimitador no formato CVS, ele não deve afetar a saída do Excel.


2

OO diz que as classes são um agrupamento de dados, uma funcionalidade. Essa definição deixa muito espaço para interpretação subjetiva.

Sabemos que as classes devem ser definidas de forma clara e fácil. Mas, para definir essa classe, precisamos ter uma noção clara de como uma classe se encaixa no design geral. Sem requisitos do tipo cascata que, paradoxalmente, são considerados um antipadrão ... isso é difícil de alcançar.

Podemos implementar um design de classe com uma arquitetura que funcione na maioria dos casos, como o MVC. Nos aplicativos MVC, assumimos apenas dados, uma interface do usuário e um requisito para que os dois se comuniquem.

Com uma arquitetura básica, é mais fácil identificar casos em que regras de responsabilidade única estão sendo violadas. EG Passando uma instância de um Controle de Usuário para um Modal.


1

Apenas para fins de discussão, apresentarei uma aula do JUCE chamada AudioSampleBuffer . Agora, essa classe existe para armazenar um trecho (ou talvez um trecho bastante longo) de áudio. Ele sabe que o número de canais, o número de amostras (por canal), parece estar comprometido com a flutuação IEEE de 32 bits, em vez de ter uma representação numérica variável ou tamanho de palavra (mas isso não é um problema para mim). Existem funções de membro que permitem obter o numChannels ou numSamples e ponteiros para qualquer canal específico. Você pode tornar um AudioSampleBuffer mais longo ou mais curto. Presumo que o primeiro zere o buffer, enquanto o último trunca.

Existem alguns membros particulares dessa classe que são usados ​​para alocar espaço no heap especial que o JUCE usa.

Mas é isso que está faltando no AudioSampleBuffer (e eu tive várias discussões com Jules sobre isso): um membro chamado SampleRate. Como poderia estar faltando isso?

A única responsabilidade que um AudioSampleBuffer precisa cumprir é representar adequadamente o áudio físico que se ouve que suas amostras representam. Quando você insere um AudioSampleBuffer de algo que lê um arquivo de som ou de um fluxo, há um parâmetro adicional que você deve obter e passá-lo junto com o AudioSampleBuffer para métodos de processamento (por exemplo, é um filtro) que precisa conhecer a taxa de amostragem ou, eventualmente, para um método que reproduz o buffer para ser ouvido (ou o transmite para outro lugar). Tanto faz.

Mas o que você precisa fazer é continuar passando esse SampleRate, que é inerente ao áudio específico que está no AudioSampleBuffer, em todos os lugares. Eu vi código em que uma constante 44100.0f foi passada para uma função, porque o programador não parecia saber mais o que fazer.

Este é um exemplo de falha no cumprimento de sua única responsabilidade.


1

Uma maneira concreta pode ser feita, com base no que você disse - que a alta coesão leva a uma única responsabilidade que você pode medir a coesão. Uma classe coesa máxima possui todos os campos usados ​​em todos os métodos. Embora nem sempre seja possível nem desejável uma classe coesa máxima, ainda é melhor chegar a isso. Tendo esse objetivo de design de classe, é muito fácil deduzir que sua classe não pode ter muitos métodos ou campos (alguns dizem no máximo 7).

Outra maneira é o básico do OOP - modelo após objetos reais. É muito mais fácil ver a responsabilidade dos objetos reais. No entanto, se o objeto real for muito complexo, divida-o em vários objetos contendo cada um deles com sua própria responsabilidade.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.