Il principio di responsabilità singola è applicabile alle funzioni?


17

Secondo Robert C. Martin, l'SRP afferma che:

Non ci dovrebbe mai essere più di un motivo per cambiare una classe .

Tuttavia, nel suo libro Clean Code , capitolo 3: Funzioni, mostra il seguente blocco di codice:

    public Money calculatePay(Employee e) throws InvalidEmployeeType {
        switch (e.type) {
            case COMMISSIONED:
                return calculateCommissionedPay(e);
            case HOURLY:
                return calculateHourlyPay(e);
            case SALARIED:
                return calculateSalariedPay(e);
            default:
                throw new InvalidEmployeeType(e.type);
        }
    }

E poi afferma:

Esistono diversi problemi con questa funzione. Innanzitutto, è grande e quando vengono aggiunti nuovi tipi di dipendenti, crescerà. In secondo luogo, fa chiaramente più di una cosa. In terzo luogo, viola il principio di responsabilità singola (SRP) perché esiste più di una ragione per cambiare . [enfasi mia]

In primo luogo, ho pensato che l'SRP fosse definito per le classi, ma si scopre che è applicabile anche alle funzioni. In secondo luogo, in che modo questa funzione ha più di un motivo per cambiare ? Posso solo vederlo cambiare a causa di un cambiamento sul Dipendente.


5
Sembra un caso da manuale per il polimorfismo.
wchargin

Questo è un argomento molto interessante. È possibile aggiungere la seguente soluzione a questo problema? Vorrei che uno mettesse una fucile calcola in ogni classe di dipendenti, ma sarebbe un male perché ora ogni classe di dipendenti può essere cambiata perché: 1. I calcoli di pagamento. 2. aggiunta di altre proprietà alla classe ecc.
Stav Alfi,

Risposte:


13

Un dettaglio spesso mancato del Principio della singola responsabilità è che i "motivi del cambiamento" sono raggruppati per attori del caso d'uso (puoi vedere una spiegazione completa qui ).

Quindi, nel tuo esempio, il calculatePaymetodo dovrà essere modificato ogni volta che sono richiesti nuovi tipi di dipendenti. Poiché un tipo di dipendente potrebbe non avere nulla a che fare con un altro, sarebbe una violazione del principio se li tenessi insieme, poiché la modifica interesserebbe diversi gruppi di utenti (o attori del caso d'uso) nel sistema.

Ora, se il principio si applica alle funzioni: anche se hai una violazione in un solo metodo, stai ancora cambiando una classe per più di un motivo, quindi è ancora una violazione di SRP.


1
Ho provato a guardare il video di YouTube collegato, ma dopo 10 minuti senza menzionare il raggruppamento per attori del caso d'uso, ho rinunciato. I primi 6 minuti sono tutti sconclusionati dall'entropia, senza una ragione apparente. Se hai indicato una posizione nel video in cui inizia a discuterne, sarebbe utile.
Michael Shaw,

@MichaelShaw Prova a guardare dalle 10:40 in poi. Lo zio Bob afferma che il codice "cambierà per motivi diversi, a causa di persone diverse". Penso che potrebbe essere quello che MichelHenrich sta cercando di indicarci.
Enrique

Ho finito di guardare l'intero video di YouTube di 50 minuti, la maggior parte dei quali non riguardava ciò che avrebbe dovuto chiarire. Ho notato alle 16:00 che era già passato da quell'argomento, e non ci è mai tornato. La "spiegazione" si riduce essenzialmente a questo: "responsabilità" in SRP non significa che, significa "diverse ragioni per il cambiamento", che in realtà significa "cambiamenti su richiesta di persone diverse", che in realtà significa "cambiamenti al richiesta di ruoli diversi che le persone svolgono ". La "spiegazione" non chiarisce nulla, sostituisce una parola vaga con un'altra.
Michael Shaw,

2
@MichaelShaw come nella citazione dal libro, se devi introdurre diversi tipi di dipendenti, devi cambiare la classe Employee. Diversi ruoli possono essere responsabili del pagamento di diversi tipi di dipendenti, quindi in questo caso la classe Employee deve essere cambiata per più di un ruolo, quindi la violazione di SRP.
MichelHenrich

1
@MichaelShaw sì, hai ragione - SRP dipende da come è organizzata l'organizzazione. Questo è esattamente il motivo per cui aggiungo "may" o "might" a tutti i miei commenti :). Tuttavia, anche in quei casi, mentre il codice potrebbe non violare SRP, esso viola sicuramente OCP.
MichelHenrich

3

A pagina 176, Capitolo 12: Emergenza, nella sezione Classi e metodi minimi il libro fornisce in qualche modo una correzione, affermando:

Nel tentativo di ridurre le nostre classi e metodi, potremmo creare troppe piccole classi e metodi. Quindi questa regola suggerisce che manteniamo bassi anche i nostri conteggi di funzioni e classi

e

Conteggi di classe e metodi elevati sono talvolta il risultato di un dogmatismo inutile.

Ovviamente, sta parlando di dogmatismo nel seguire la SRP per abbattere perfettamente piccoli metodi innocenti come calculatePay()sopra.


3

Quando il signor Martin applica l'SRP a una funzione, sta implicitamente estendendo la sua definizione di SRP. Poiché l'SRP è una formulazione specifica di OO di un principio generale, e poiché è una buona idea quando applicata alle funzioni, non vedo alcun problema con quello (anche se potrebbe essere bello se lo avesse incluso esplicitamente nel definizione).

Non vedo più di una ragione per cambiare, e non credo che pensare all'SRP in termini di "responsabilità" o "ragioni per cambiare" sia utile. In sostanza, l'SRP sta ottenendo che le entità software (funzioni, classi, ecc.) Dovrebbero fare una cosa e farlo bene.

Se dai un'occhiata alla mia definizione, non è meno vago della solita formulazione dell'SRP. Il problema con le normali definizioni dell'SRP non è che sono troppo vaghe, ma che cercano di essere troppo specifiche su qualcosa che è essenzialmente vago.

Se guardi cosa calculatePayfa, sta chiaramente facendo una cosa sola: spedire in base al tipo. Dal momento che Java ha dei modi integrati per eseguire la spedizione basata sul tipo, calculatePayè inelegante e non idiomatico, quindi dovrebbe essere riscritto, ma non per i motivi dichiarati.


-2

Hai ragione @Enrique. Non importa se è una funzione o un metodo di una classe, l'SRP significa che in quel blocco di codice fai solo una cosa.

L'affermazione "motivo per cambiare" a volte è un po 'fuorviante, ma se cambi calculateSalariedPayo calculateHourlyPayo l'enum di Employee.typete devi cambiare questo metodo.

Nel tuo esempio la funzione:

  • controlla il tipo di dipendente
  • chiama un'altra funzione che calcola il denaro in base al tipo

Secondo me non è direttamente una violazione di SRP, poiché i casi di cambio e le chiamate non possono essere scritti più brevi, se si pensa al Dipendente e i metodi esistono già. Comunque è una chiara violazione del principio aperto-chiuso (OCP) in quanto è necessario aggiungere dichiarazioni "case" se si aggiungono tipi di dipendenti, quindi è una cattiva implementazione: rifattorizzarla.

Non sappiamo come calcolare il "denaro", ma il modo più semplice è avere Employeecome interfaccia e alcune implementazioni concrete con i getMoneymetodi. In tal caso, l'intera funzione è inutile.

Se è più complicato calcolarlo, si potrebbe usare il modello visitatore che non è anche SRP al 100% ma è più OCP di un caso di switch.


2
Non sono sicuro di come puoi elencare 2 cose che la funzione fa, ma dire che non è una violazione di SRP.
JeffO

@JeffO: Non sono 2 cose, sono 2 parti di una cosa: chiamare la funzione appropriata in base al tipo.
Michael Shaw,
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.