Come determinare se una classe soddisfa il principio della responsabilità singola?


34

Il principio della responsabilità singola si basa sul principio dell'alta coesione. La differenza tra i due è che una classe altamente coesa presenta una serie di responsabilità fortemente correlate, mentre le classi che aderiscono a SRP hanno una sola responsabilità.

Ma come possiamo determinare se una determinata classe presenta un insieme di responsabilità ed è quindi altamente coesa, o se ha una sola responsabilità e quindi aderisce a SRP? In altre parole, non è più o meno soggettivo, dal momento che alcuni potrebbero considerare una classe molto granulare (e come tale crederanno che la classe aderisca a SRP), mentre altri potrebbero considerarla non abbastanza granulare?



Risposte:


21

Perché sì, è molto soggettivo, ed è oggetto di molti dibattiti accesi, con la faccia arrossata, ai programmatori.

Non c'è davvero nessuna risposta e la risposta potrebbe cambiare man mano che il tuo software diventa più complesso. Quella che una volta era una singola attività ben definita può alla fine diventare più attività poco definite. Anche questo è sempre un problema. Come si sceglie il modo corretto di suddividere un programma in attività?

L'unico consiglio che posso dare è questo: usa il tuo miglior giudizio (e quello dei tuoi colleghi). E ricorda che gli errori possono (di solito) essere corretti se li prendi abbastanza presto.


Vorrei che l'informatica fosse più simile alla scienza reale. La soggettività non ha posto nella scienza vera. Mentre i principi SOLIDI vanno bene per conto proprio, devono essere ripetuti per minimizzare la soggettività e massimizzare l'obiettività. Non è ancora successo, il che mi fa mettere in dubbio la loro legittimità nel mondo reale.
DarkNeuron,

13

Bob Martin (Zio Bob), che ha originato i principi SOLID di cui SRP è il primo, dice a questo proposito (sto parafrasando, non ricordo le parole reali):

Una classe dovrebbe avere solo un motivo per cambiare

Se ha più di un motivo, non aderisce a SRP.


14
Questo è solo ripetere la definizione, ma in realtà aderire a SRP è ancora piuttosto soggettivo.
Andy,

7

Posso darti diverse regole pratiche.

  • Quanto è facile nominare la classe? Se è difficile nominare una classe, probabilmente sta facendo troppo.
  • Quanti metodi pubblici ha la classe? 7 +/- 2 è una buona regola empirica. Se la classe ha più di questo, dovresti pensare di dividerlo in più classi.
  • Esistono gruppi coerenti di metodi pubblici utilizzati in contesti separati?
  • Quanti metodi privati ​​o membri dei dati ci sono? Se la classe ha una struttura interna complessa, probabilmente dovresti rifattorarla in modo che gli interni siano raggruppati in classi più piccole separate.
  • E la regola empirica più semplice: quanto è grande la classe? Se hai un file di intestazione C ++ contenente una singola classe che è lunga più di un paio di centinaia di righe, probabilmente dovresti dividerlo.

2
Per quanto riguarda il tuo secondo punto, vedi uxmyths.com/post/931925744/…
Cameron Martin

7
Non sono molto d'accordo su 7 +/- 2: il principio della responsabilità singola riguarda la coesione semantica, non i numeri arbitrari.
Jacques B

1
La regola del pollice non necessita di prove scientifiche indipendenti. Il moderno metodo scientifico è vecchio di secoli, l'architettura e l'ingegneria sono millenni. La regola empirica per i metodi pubblici è "diversi" e nessun parametro è "pochi". In altre notizie, anche se i disegni di alcuni bambini mostrano che altrimenti le braccia delle persone non escono di testa [citazione necessaria].
abuzittin gillifirca,

@CameronMartin A seconda della configurazione, l'interfaccia di una classe potrebbe non essere prontamente disponibile per la lettura. La ricerca di un'interfaccia utente non è quasi la stessa cosa della scrittura del codice: se devo consultare la documentazione ogni minuto, sto almeno raddoppiando il tempo necessario per svolgere un lavoro reale.
Più chiaro il

6

I singoli principi di responsabilità affermano che ogni modulo software dovrebbe avere solo un motivo per cambiare. In un recente articolo, lo zio Bob ha spiegato "motivo per cambiare",

Tuttavia, mentre pensi a questo principio, ricorda che le ragioni del cambiamento sono le persone. Sono le persone che richiedono modifiche. E non vuoi confondere quelle persone, o te stesso, mescolando insieme il codice a cui molte persone diverse tengono per diversi motivi.

Ha ulteriormente spiegato il concetto con un esempio QUI .


Questo è un ottimo articolo, scritto dall'uomo stesso.
MrDustpan,

4

Per rispondere a questo, fai un passo indietro e considera l' intenzione del principio della responsabilità unica. Perché in primo luogo è un principio di progettazione raccomandato?

Lo scopo del principio è quello di "compartimentare" la base di codice, quindi il codice relativo a una singola "responsabilità" è isolato in una singola unità. Ciò semplifica la ricerca e la comprensione del codice e, soprattutto, significa che le modifiche alla "responsabilità" avranno un impatto solo su una singola unità di codice.

Quello che non vuoi assolutamente mai in un sistema è quando una piccola possibilità fa fallire o modificare il comportamento di un'altra parte del codice apparentemente non correlata. L'SRP aiuta a isolare bug e modifiche.

Allora che cos'è una "responsabilità"? È qualcosa che potrebbe plausibilmente cambiare indipendentemente da altri cambiamenti. Supponi di avere un programma in grado di salvare alcune impostazioni in un file di configurazione XML e di poterle leggere nuovamente dal file. È una singola responsabilità o "caricare" e "salvare" due diverse responsabilità? Qualsiasi tipo di modifica al formato o alla struttura del file richiederebbe la modifica del carico e della logica di salvataggio. È quindi una singola responsabilità che dovrebbe essere rappresentata da una singola classe. Ora considera un'app che può esportare alcuni dati in formato CVS, Excel e XML. In questo caso, è facile immaginare che un formato possa cambiare senza influire sull'altro. Se decidi di modificare il delimitatore nel formato CVS, non dovrebbe influire sull'output di Excel.


2

OO afferma che le classi sono un raggruppamento di dati una funzionalità. Questa definizione lascia molto spazio all'interpretazione soggettiva.

Sappiamo che le classi dovrebbero essere definite in modo chiaro e semplice. Ma, al fine di definire una tale classe, dobbiamo avere un'idea chiara di come una classe si adatta al design complessivo. Senza requisiti di tipo a cascata che, paradossalmente, sono considerati un anti-pattern ... questo è difficile da raggiungere.

Siamo in grado di implementare un design di classe con un'architettura che funziona nella maggior parte dei casi, come MVC. Nelle applicazioni MVC assumiamo solo di avere dati, un'interfaccia utente e un requisito per la comunicazione tra i due.

Con un'architettura di base, è più facile identificare i casi in cui le singole regole di responsabilità vengono infrante. Ad esempio, passare un'istanza di un controllo utente a un modale.


1

Per motivi di discussione, farò emergere un corso di JUCE chiamato AudioSampleBuffer . Ora questa classe esiste per contenere uno snippet (o forse uno snippet piuttosto lungo) di audio. Conosce il numero di canali, il numero di campioni (per canale), sembra essere impegnato nel float IEEE a 32 bit piuttosto che avere una rappresentazione numerica variabile o dimensione delle parole (ma questo non è un problema con me). Ci sono funzioni membro che ti consentono di ottenere numChannels o numSample e puntatori su qualsiasi canale particolare. È possibile prolungare o ridurre un AudioSampleBuffer. Presumo che il primo zero-pads il buffer mentre il secondo si tronca.

Esistono alcuni membri privati ​​di questa classe che vengono utilizzati per allocare spazio nell'heap speciale utilizzato da JUCE.

Ma questo è ciò che manca a AudioSampleBuffer (e su questo ho avuto diverse discussioni con Jules): un membro ha chiamato SampleRate. Come potrebbe mancare quello?

L'unica responsabilità che un AudioSampleBuffer deve soddisfare è quella di rappresentare adeguatamente l'audio fisico che si sente che i suoi campioni rappresentano. Quando inserisci AudioSampleBuffer da qualcosa che legge un file audio o da uno stream, c'è un parametro aggiuntivo che devi ottenere e passarlo insieme a AudioSampleBuffer ai metodi di elaborazione (diciamo che è un filtro) che deve conoscere la frequenza di campionamento o, alla fine, a un metodo che riproduce il buffer per essere ascoltato (o trasmesso in streaming da qualche altra parte). Qualunque cosa.

Ma quello che devi fare è continuare a passare questo SampleRate, che è inerente all'audio specifico che vive in AudioSampleBuffer, ovunque e ovunque. Ho visto il codice in cui una costante 44100.0f è stata passata a una funzione, perché il programmatore non sembrava sapere cos'altro fare.

Questo è un esempio di mancato rispetto della sua unica responsabilità.


1

Un modo concreto può essere fatto, in base a ciò che hai detto: che l'alta coesione comporta una singola responsabilità che puoi misurare. Una classe coesiva massima ha tutti i campi utilizzati in tutti i metodi. Mentre una classe coesiva massima non è sempre possibile né desiderabile avere, è ancora meglio raggiungerla. Con questo obiettivo di progettazione della classe è abbastanza facile dedurre che la tua classe non può avere molti metodi o campi (alcuni dicono al massimo 7).

Un altro modo è dalle basi pure di OOP - modello dopo oggetti reali. È molto più facile vedere la responsabilità degli oggetti reali. Tuttavia, se l'oggetto reale è troppo complesso, suddividilo in più oggetti conta ciascuno ha la propria responsabilità.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.