Assicurati che ogni classe abbia una sola responsabilità, perché?

36

Secondo la documentazione di Microsoft, l'articolo sui principi SOLID di Wikipedia, o la maggior parte degli architetti IT, dobbiamo garantire che ogni classe abbia una sola responsabilità. Vorrei sapere perché, perché se tutti sembrano essere d'accordo con questa regola nessuno sembra essere d'accordo sulle ragioni di questa regola.

Alcuni citano una migliore manutenzione, alcuni dicono che fornisce test facili o rende la classe più robusta, o di sicurezza. Cosa è corretto e cosa significa in realtà? Perché rende la manutenzione migliore, test più semplice o il codice più robusto?

    
posta Bastien Vandamme 07.04.2014 - 10:38
fonte

11 risposte

57

La modularità. Qualsiasi linguaggio decente ti darà i mezzi per incollare pezzi di codice, ma non esiste un modo generale per scollare una grande porzione di codice senza che il programmatore esegua interventi chirurgici alla fonte. Intralciando un sacco di compiti in un unico costrutto di codice, rubi te stesso e gli altri all'opportunità di combinare i suoi pezzi in altri modi, e introduce dipendenze non necessarie che potrebbero causare modifiche a un pezzo per influenzare gli altri.

SRP è applicabile tanto alle funzioni quanto alle classi, ma i linguaggi OOP mainstream sono relativamente poveri nelle funzioni di incollaggio.

    
risposta data 07.04.2014 - 13:53
fonte
30

Una migliore manutenzione, test facili, una risoluzione dei bug più rapida sono solo (molto piacevoli) i risultati dell'applicazione di SRP. Il motivo principale (come dice Robert C. Matin) è:

A class should have one, and only one, reason to change.

In altre parole, SRP aumenta la località di modifica .

SRP promuove anche il codice ASCIUTTO. Finché abbiamo classi che hanno una sola responsabilità, possiamo scegliere di usarle ovunque vogliamo. Se abbiamo una classe che ha due responsabilità, ma abbiamo bisogno solo di una di esse e la seconda interferisce, abbiamo 2 opzioni:

  1. Copia-incolla la classe in un'altra e magari crea anche un'altra mutante multi-responsabile (aumenta leggermente il debito tecnico).
  2. Dividi la classe e fallo come dovrebbe essere, in primo luogo, che può essere costoso a causa dell'uso esteso della classe originale.
risposta data 07.04.2014 - 12:31
fonte
21

È facile creare codice per risolvere un problema particolare. È più complicato creare codice che corregge quel problema consentendo al tempo stesso di apportare modifiche in modo sicuro. SOLID offre una serie di pratiche che migliorano il codice.

Per quanto riguarda quale è corretto: tutti e tre. Sono tutti benefici dell'utilizzo di una singola responsabilità e il motivo per cui dovresti usarlo.

Riguardo a cosa significano:

  • Una migliore manutenzione significa che è più facile da cambiare e non cambia più spesso. Poiché c'è meno codice e quel codice è focalizzato su qualcosa di specifico, se è necessario modificare qualcosa che non è correlato alla classe, la classe non ha bisogno di essere modificata. Inoltre, quando è necessario modificare la classe, finché non è necessario modificare l'interfaccia pubblica, è necessario preoccuparsi solo di quella classe e nient'altro.
  • Test facili significa meno test, meno setup. Non ci sono tante parti mobili nella classe, quindi il numero di possibili errori nella classe è minore e, quindi, un numero inferiore di casi da testare. Ci saranno meno campi / membri privati da configurare.
  • A causa dei due precedenti, ottieni una classe che cambia meno e fallisce di meno, e quindi è più robusta.

Prova a creare codice per un po 'seguendo il principio, e rivisita quel codice in seguito per apportare alcune modifiche. Vedrai l'enorme vantaggio che fornisce.

Fai lo stesso per ogni classe e finisci con più classi, tutte conformi a SRP. Rende la struttura più complessa dal punto di vista delle connessioni, ma la semplicità di ogni classe lo giustifica.

    
risposta data 07.04.2014 - 11:04
fonte
4

Ecco gli argomenti che, a mio avviso, supportano l'affermazione secondo cui il Principio di Responsabilità Unica è una buona pratica. Fornisco anche collegamenti a ulteriori pubblicazioni, dove puoi leggere ragionamenti ancora più dettagliati - e più eloquenti dei miei:

  • Migliore manutenzione : idealmente, ogni volta che una funzionalità del sistema deve cambiare, ci sarà una sola classe che deve essere cambiata. Una chiara mappatura tra classi e responsabilità significa che ogni sviluppatore coinvolto nel progetto può identificare quale classe sia. (Come ha notato @ MaciejChałapuk, vedi Robert C. Martin," Pulisci codice ".)

  • Test più facili : idealmente, le classi dovrebbero avere un'interfaccia pubblica minima possibile e i test dovrebbero riguardare solo questa interfaccia pubblica. Se non riesci a testare con chiarezza, perché molte parti della tua classe sono private, allora questo è un chiaro segnale che la tua classe ha troppe responsabilità e che dovresti suddividerla in sottoclassi più piccole. Si noti che questo si applica anche alle lingue in cui non ci sono membri della classe "pubblici" o "privati"; avere una piccola interfaccia pubblica significa che è molto chiaro al codice client quali parti della classe si suppone usino. (Vedi Kent Beck," Test-Driven Development " per ulteriori dettagli.)

  • Codice robusto : il tuo codice non fallirà più o meno spesso perché è ben scritto; ma, come tutto il codice, il suo obiettivo finale è non comunicare con la macchina , ma con altri sviluppatori (vedi Kent Beck," Modelli di implementazione ", Capitolo 1.) Un chiaro codebase è più facile da ragionare, quindi verranno introdotti meno bug e passerà meno tempo tra la scoperta di un bug e il suo fixing.

risposta data 07.04.2014 - 15:29
fonte
3

Ci sono una serie di ragioni, ma quella che mi piace è l'approccio utilizzato da molti dei primi programmi UNIX: fai bene una cosa. È abbastanza difficile farlo con una cosa, e aumentando sempre più le cose che provi a fare.

Un altro motivo è limitare e controllare gli effetti collaterali. Ho adorato il mio apriporta combinato per la macchina del caffè. Sfortunatamente, il caffè di solito traboccava quando avevo dei visitatori. Ho dimenticato di chiudere la porta dopo aver fatto il caffè l'altro giorno e qualcuno l'ha rubato.

Dal punto di vista psicologico, puoi solo tenere traccia di alcune cose alla volta. Le stime generali sono sette più o meno due. Se una classe fa più cose, devi tenerle tutte in una volta. Questo riduce la tua capacità di tenere traccia di ciò che stai facendo. Se una classe fa tre cose e ne vuoi solo una, puoi esaurire la tua capacità di tenere traccia delle cose prima che tu faccia effettivamente qualcosa con la classe.

Fare cose multiple aumenta la complessità del codice. Oltre al codice più semplice, l'aumento della complessità aumenta la probabilità di errori. Da questo punto di vista vuoi che le lezioni siano il più semplici possibile.

Testare una classe che fa una cosa è molto più semplice. Non è necessario verificare che la seconda cosa che la classe ha fatto o non sia avvenuta per ogni test. Inoltre, non è necessario correggere le condizioni non funzionanti e ripetere il test quando uno di questi test ha esito negativo.

    
risposta data 08.04.2014 - 02:45
fonte
2

Perché il software è organico. I requisiti cambiano continuamente, quindi devi manipolare i componenti con il minor numero possibile di mal di testa. Non seguendo i principi SOLIDI, si può finire con una base di codice che è impostata in concreto.

Immagina una casa con un muro di cemento portante. Cosa succede quando estrai questo muro senza alcun supporto? La casa probabilmente crollerà. Nel software non lo vogliamo, quindi strutturiamo le applicazioni in modo tale da poter spostare / sostituire / modificare facilmente i componenti senza causare molti danni.

    
risposta data 07.04.2014 - 13:44
fonte
1

Seguo il pensiero: 1 Class = 1 Job .

Utilizzo di un'animologia di fisiologia: motore (sistema neurale), respirazione (polmoni), digestivo (stomaco), olfattivo (osservazione), ecc. Ognuno di questi avrà un sottoinsieme di controllori, ma ciascuno di essi ha solo una responsabilità, sia è in grado di gestire il modo in cui ciascuno dei rispettivi sottosistemi funziona o se si tratta di un sottosistema di end point e di eseguire solo 1 compito, come sollevare un dito o far crescere un follicolo pilifero.

Non confondere il fatto che potrebbe agire come manager anziché come lavoratore. Alcuni lavoratori possono essere promossi a Manager, quando il lavoro che stavano eseguendo è diventato troppo complicato per un processo da gestire da solo.

La parte più complicata di ciò che ho sperimentato è sapere quando designare un corso come Operatore, Supervisore o Manager. Indipendentemente da ciò, sarà necessario osservare e denotare la sua funzionalità per 1 responsabilità (Operatore, Supervisore o Manager).

Quando una classe / oggetto esegue più di 1 di questi ruoli di tipo, scoprirai che il processo generale inizierà ad avere problemi di prestazioni, o elaborerà strozzature.

    
risposta data 07.04.2014 - 17:44
fonte
1

Specialmente con un principio così importante come la responsabilità unica, personalmente mi aspetto che ci siano molte ragioni per le quali la gente adotta questo principio.

Alcuni di questi potrebbero essere:

  • Manutenzione - SRP garantisce che la modifica della responsabilità in una classe non influenzi altre responsabilità, semplificando la manutenzione. Questo perché se ogni classe ha una sola responsabilità, le modifiche apportate a una sola responsabilità sono isolate da altre responsabilità.
  • Test - Se una classe ha una responsabilità, è molto più facile capire come testare questa responsabilità. Se una classe ha più responsabilità, devi assicurarti di testare quella corretta e che il test non sia influenzato da altre responsabilità della classe.

Si noti inoltre che SRP viene fornito con un insieme di principi SOLID. Attenersi a SRP e ignorare il resto è altrettanto grave che non fare SRP in primo luogo. Quindi non dovresti valutare SRP da solo, ma nel contesto di tutti i principi SOLID.

    
risposta data 07.04.2014 - 11:05
fonte
1

Il modo migliore per capire l'importanza di questi principi è di averne bisogno.

Quando ero un programmatore alle prime armi, non pensavo molto alla progettazione, infatti non sapevo nemmeno che esistessero schemi di design. Man mano che i miei programmi crescevano, cambiare una cosa significava cambiare molte altre cose. Era difficile rintracciare i bug, il codice era enorme e ripetitivo. Non c'era molta gerarchia di oggetti, le cose erano dappertutto. L'aggiunta di qualcosa di nuovo o la rimozione di qualcosa di vecchio comportano errori nelle altre parti del programma. Vai a capire.

Su piccoli progetti, potrebbe non avere importanza, ma nei grandi progetti le cose possono essere davvero da incubo. Più tardi, quando mi sono imbattuto in concetti di design pattern, ho detto a me stesso, "oh yah, fare questo avrebbe reso le cose molto più facili allora".

Non riesci davvero a capire l'importanza dei modelli di design fino a quando non si presenta la necessità. Rispetto gli schemi perché, per esperienza, posso dire che rendono la manutenzione del codice semplice e robusta.

Tuttavia, proprio come te, sono ancora incerto riguardo ai "test facili", perché non ho ancora avuto la necessità di entrare nei test delle unità.

    
risposta data 08.04.2014 - 00:37
fonte
1

La risposta è, come altri hanno sottolineato, tutti sono corretti, e si integrano l'uno con l'altro, più facile da testare rende la manutenzione più semplice rende il codice più robusto rende la manutenzione più facile e così via ...

Tutto questo si riduce a un principio chiave - il codice dovrebbe essere piccolo e fare il minimo necessario per portare a termine il lavoro. Questo vale per un'applicazione, un file o una classe tanto quanto per una funzione. Più cose fa un pezzo di codice, più difficile è capire, mantenere, estendere o testare.

Penso che possa essere riassunto in una sola parola: scope. Prestare molta attenzione alla portata degli artefatti, minore è il numero di oggetti in un punto particolare in un'applicazione, meglio è.

Ambito esteso = più complessità = più modi in cui le cose vanno male.

    
risposta data 08.04.2014 - 08:27
fonte
1

Se una classe è troppo grande, diventa difficile mantenere, testare e capire, altre risposte hanno coperto questa volontà.

È possibile che una classe abbia più di una responsabilità senza problemi, ma presto si riscontrano problemi con classi troppo complesse.

Tuttavia avere una semplice regola di "una sola responsabilità" rende semplicemente più facile sapere quando hai bisogno di una nuova classe .

Tuttavia definire "responsabilità" è difficile, non significa "fare tutto quello che dice la specifica dell'applicazione", la vera abilità è sapere come suddividere il problema in piccole unità di "responsabilità".

    
risposta data 08.04.2014 - 12:49
fonte