Il principio di responsabilità unica è applicabile alle funzioni?

16

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

There should never be more than one reason for a class to change.

Tuttavia, nel suo libro Pulisci codice , 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:

There are several problems with this function. First, it’s large, and when new employee types are added, it will grow. Second, it very clearly does more than one thing. Third, it violates the Single Responsibility Principle (SRP) because there is more than one reason for it to change. [emphasis mine]

In primo luogo, pensavo che l'SRP fosse definito per le classi, ma risulta che è applicabile anche alle funzioni. In secondo luogo, in che modo questa funzione ha più di un motivo per cambiare ? Posso vederlo solo cambiando a causa di un cambiamento in Employee.

    
posta Enrique 08.03.2015 - 18:56
fonte

4 risposte

12

Un dettaglio spesso mancato del Principio di Responsabilità Unica è che le "ragioni del cambiamento" sono raggruppate dagli attori del caso d'uso (puoi vedere una spiegazione completa qui ).

Quindi, nel tuo esempio, il metodo calculatePay dovrà essere cambiato ogni volta che saranno necessari nuovi tipi di Dipendenti. Dal momento che un tipo di dipendente può non avere nulla a che fare con un altro, sarebbe una violazione del principio se si mantengono uniti, 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.

    
risposta data 08.03.2015 - 19:58
fonte
2

Nella pagina 176, Capitolo 12: Emergence, nella sezione intitolata Classi e metodi minimi il libro fornisce una sorta di correzione, affermando:

In an effort to make our classes and methods small, we might create too many tiny classes and methods. So this rule suggests that we also keep our function and class counts low

e

High class and method counts are sometimes the result of pointless dogmatism.

Ovviamente, sta parlando del dogmatismo nel seguire l'SRP per scomporre perfetti metodi innocenti come calculatePay() sopra.

    
risposta data 08.03.2015 - 23:33
fonte
2

Quando il signor Martin applica l'SRP a una funzione, estende implicitamente la sua definizione di SRP. Poiché l'SRP è un testo specifico OO di un principio generale, e poiché è una buona idea se applicato a funzioni, non vedo un problema con questo (anche se sarebbe stato carino se l'avesse incluso esplicitamente nel definizione).

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

Se dai un'occhiata alla mia definizione, non è meno vaga del solito testo dell'SRP. Il problema con le solite definizioni dell'SRP non è che sono troppo vaghe, ma che cercano di essere troppo specifici su qualcosa che è essenzialmente vago.

Se guardi cosa fa calculatePay , sta chiaramente facendo una sola cosa: invio basato sul tipo. Dato che Java ha dei metodi predefiniti per eseguire il dispatch di tipo, calculatePay è inelegante e non idiomatico, quindi dovrebbe essere riscritto, ma non per i motivi indicati.

    
risposta data 09.03.2015 - 03:33
fonte
-2

Hai ragione @Enrique. Indipendentemente dal fatto che si tratti di una funzione o di un metodo di una classe, l'SRP significa che in quel blocco di codice devi fare solo una cosa.

L''ragione per cambiare' è talvolta un po 'fuorviante, ma se cambi calculateSalariedPay o calculateHourlyPay o enum di Employee.type devi cambiare questo metodo.

Nel tuo esempio la funzione:

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

Secondo me non è direttamente una violazione SRP, dal momento che i casi di commutazione e le chiamate non possono essere scritti più brevi, se si pensa a Dipendente e i metodi esistono già. In ogni caso si tratta di una chiara violazione di principio aperto-chiuso (OCP) in quanto è necessario aggiungere istruzioni "caso" se si aggiungono tipi di dipendenti, quindi è una cattiva implementazione: refactoring.

Non sappiamo come deve essere calcolato il 'denaro', ma il modo più semplice è avere Employee come interfaccia e alcune implementazioni concrete con metodi getMoney . In tal caso l'intera funzione è inutile.

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

    
risposta data 08.03.2015 - 20:59
fonte

Leggi altre domande sui tag