Come determinare se una classe rispetta il principio della responsabilità unica?

32

Il principio di responsabilità unica si basa sul principio di alta coesione. La differenza tra i due è che le classi altamente coese presentano una serie di responsabilità strongmente correlate, mentre le classi che aderiscono all'SRP hanno una sola responsabilità.

Ma in che modo determiniamo se una particolare classe presenta un insieme di responsabilità ed è quindi solo 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 crederà che la classe aderisca all'SRP), mentre altri potrebbero considerarla non sufficientemente dettagliata?

    
posta user1483278 28.06.2012 - 20:20
fonte

8 risposte

18

Perché sì è molto soggettivo, ed è il soggetto di molti entusiasti programmatori di dibattiti dal volto arrossato.

Non c'è davvero una risposta, e la risposta potrebbe cambiare via via che il software diventa più complesso. Quello che una volta era un singolo compito ben definito potrebbe alla fine diventare più compiti scarsamente definiti. Anche questo è sempre lo sfregio. Come scegli il modo corretto di dividere un programma in attività?

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

    
risposta data 28.06.2012 - 21:16
fonte
12

Bob Martin (Zio Bob), che ha originato i principi SOLID di cui SRP è il primo, dice di questo (sto parafrasando, non posso ricordare le parole effettive):

A class should only have one reason to change

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

    
risposta data 28.06.2012 - 20:25
fonte
7

Posso darti diverse regole pratiche.

  • Quanto è facile nominare la classe? Se una classe è difficile da nominare, probabilmente sta facendo troppo.
  • Quanti metodi pubblici ha la classe? 7 +/- 2 è una buona regola empirica. Se la classe ha qualcosa in più, dovresti pensare a suddividerla in più classi.
  • Esistono gruppi coesivi di metodi pubblici utilizzati in contesti separati?
  • Quanti metodi privati o membri di dati ci sono? Se la classe ha una struttura interna complessa, è probabile che la si debba rifattorizzare in modo tale che gli interni siano impacchettati in classi più piccole separate.
  • E la più semplice delle regole: quanto è grande la classe? Se hai un file di intestazione C ++ contenente una singola classe che supera le duecento righe, dovresti probabilmente dividerlo.
risposta data 28.06.2012 - 23:11
fonte
6

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

However, as you think about this principle, remember that the reasons for change are people. It is people who request changes. And you don't want to confuse those people, or yourself, by mixing together the code that many different people care about for different reasons.

Ha ulteriormente spiegato il concetto con un esempio QUI .

    
risposta data 11.05.2015 - 07:57
fonte
3

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

Lo scopo del principio è di "compartimentare" il codice base, in modo che il codice relativo a una singola "responsabilità" sia isolato in una singola unità. Ciò rende più facile trovare e comprendere il codice e, cosa più importante, significa che le modifiche alla "responsabilità" avranno un impatto solo su una singola unità di codice.

Ciò che non si vuole assolutamente in un sistema è quando una piccola possibilità fa sì che qualche altra parte apparentemente non correlata del codice fallisca o cambi il comportamento. L'SRP aiuta a isolare bug e modifiche.

Quindi che cosa è una "responsabilità" allora? È qualcosa che potrebbe teoricamente cambiare indipendentemente da altri cambiamenti. Supponiamo che tu abbia un programma che può salvare alcune impostazioni in un file di configurazione XML e può leggerle di nuovo dal file. Si tratta di una singola responsabilità, oppure "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 in grado di esportare alcuni dati in formato CVS, Excel e XML. In questo caso, è facile immaginare che un formato possa cambiare senza influenzare l'altro. Se si decide di modificare il delimitatore nel formato CVS, non dovrebbe influire sull'output di Excel. Quindi in questo caso dovrebbe essere rappresentato da tre classi separate (presumibilmente condividendo la stessa interfaccia).

    
risposta data 27.11.2016 - 13:00
fonte
2

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

Sappiamo che le classi dovrebbero essere definite chiaramente e facilmente. Ma, al fine di definire una tale classe, dobbiamo avere una nozione chiara di come una classe si inserisce nel progetto generale. Senza requisiti di tipo a cascata che, paradossalmente, sono considerati un anti-pattern ... questo è difficile da raggiungere.

Possiamo implementare un design di classe con un'architettura che funziona nella maggior parte dei casi, come MVC. Nelle applicazioni MVC si presuppone solo di avere dati, un'interfaccia utente e un requisito per i due di comunicare.

Con un'architettura di base, è più facile identificare i casi in cui le singole regole di responsabilità vengono violate. PER ESEMPIO. Passare un'istanza di un controllo utente a un modale.

    
risposta data 28.06.2012 - 21:46
fonte
1

Solo per motivi di discussione, presenterò una lezione da JUCE chiamata AudioSampleBuffer . Ora questa classe esiste per contenere uno snippet (o forse un frammento piuttosto lungo) di audio. Conosce il numero di canali, il numero di campioni (per canale), sembra essere impegnato su un float IEEE a 32 bit piuttosto che avere una rappresentazione numerica variabile o parole (ma non è un problema con me). Ci sono funzioni membro che ti permettono di ottenere numChannels o numSamples e puntatori su qualsiasi canale particolare. Puoi rendere AudioSampleBuffer più lungo o più corto. Presumo che il precedente zero pad il buffer mentre il secondo tronca.

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

Ma questo è ciò che manca AudioSampleBuffer (e ho discusso parecchie discussioni con Jules): un membro chiamato SampleRate . In che modo potrebbe mancare?

La singola responsabilità che un AudioSampleBuffer deve soddisfare è quella di rappresentare adeguatamente l'audio fisico percepito dai suoi campioni. Quando inserisci un AudioSampleBuffer da qualcosa che legge un file sonoro o da uno stream, c'è un parametro addizionale che devi ottenere e passarlo insieme a AudioSampleBuffer ai metodi di elaborazione (diciamo che è un filtro) che ha bisogno di conoscere la frequenza di campionamento o, alla fine, a un metodo che riproduce il buffer per essere ascoltato (o lo trasmette in qualche altro posto). Qualunque sia.

Ma quello che devi fare è continuare a passare questo SampleRate, che è inerente allo specifico audio che vive nell'AudioSampleBuffer, ovunque. Ho visto 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à.

    
risposta data 29.06.2015 - 23:05
fonte
1

Si può fare un modo concreto, basato su ciò che hai detto, che un'elevata coesione porta un'unica responsabilità che puoi misurare la coesione. Una classe coesiva massima ha tutti i campi utilizzati in tutti i metodi. Mentre una classe coesiva massimale non è sempre possibile né desiderabile avere la migliore soluzione per raggiungerla. Avendo questo obiettivo di design di 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 di contenimento, ognuno dei quali ha la propria responsabilità.

    
risposta data 30.06.2015 - 12:01
fonte

Leggi altre domande sui tag