Ist das Prinzip der einheitlichen Verantwortung auf Funktionen anwendbar?

17

Laut Robert C. Martin heißt es in der SRP:

Es sollte nie mehr als einen Grund geben, warum sich eine Klasse ändert.

In seinem Buch Clean Code , Kapitel 3: Functions, zeigt er jedoch den folgenden Codeblock:

    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);
        }
    }

Und dann heißt es:

Es gibt verschiedene Probleme mit dieser Funktion. Erstens ist es groß, und wenn neue Mitarbeitertypen hinzugefügt werden, wird es wachsen. Zweitens macht es ganz klar mehr als eine Sache. Drittens verstößt es gegen das Einzelverantwortungsprinzip (Single Responsibility Principle, SRP), weil es mehr als einen Grund für eine Änderung gibt . [Hervorhebung von mir]

Erstens dachte ich, dass die SRP für Klassen definiert wurde, aber es stellt sich heraus, dass sie auch für Funktionen gilt. Zweitens, wie kommt es, dass diese Funktion mehr als einen Grund hat, sich zu ändern ? Ich kann nur sehen, dass es sich aufgrund eines Mitarbeiterwechsels ändert.

Enrique
quelle
5
Dies scheint ein Lehrbuch für Polymorphismus zu sein.
Wchargin
Dies ist ein sehr interessantes Thema. Besteht die Möglichkeit, dass Sie die folgende Lösung zu diesem Problem hinzufügen? Ich würde sagen, dass man eine CalculatePay-Funktion in jede Mitarbeiterklasse einfügt, aber das wäre schlecht, da jetzt jede Mitarbeiterklasse geändert werden kann, weil: 1. Die Zahlungsberechnungen. 2. Hinzufügen weiterer Eigenschaften der Klasse usw.
Stav Alfi

Antworten:

13

Ein häufig übersehenes Detail des Grundsatzes der einheitlichen Verantwortung ist, dass die "Gründe für Änderungen" nach Anwendungsfallakteuren gruppiert sind (eine vollständige Erklärung finden Sie hier ).

In Ihrem Beispiel muss die calculatePayMethode geändert werden, wenn neue Arten von Mitarbeitern erforderlich sind. Da ein Mitarbeitertyp möglicherweise nichts mit einem anderen zu tun hat, wäre es ein Verstoß gegen das Prinzip, wenn Sie ihn zusammenhalten, da sich die Änderung auf verschiedene Benutzergruppen (oder Anwendungsfallakteure) im System auswirken würde.

Nun zur Frage, ob das Prinzip für Funktionen gilt: Auch wenn Sie in nur einer Methode eine Verletzung haben, ändern Sie eine Klasse aus mehr als einem Grund, es handelt sich also immer noch um eine Verletzung von SRP.

MichelHenrich
quelle
1
Ich habe versucht, das verlinkte Youtube-Video anzusehen, aber nach 10 Minuten ohne Erwähnung der Gruppierung nach Anwendungsfallakteuren habe ich aufgegeben. Die ersten 6 Minuten schwirren alle über Entropie, ohne ersichtlichen Grund. Wenn Sie im Video einen Ort angeben, an dem er darüber zu diskutieren beginnt, wäre dies hilfreich.
Michael Shaw
@MichaelShaw Versuchen Sie, ab 10:40 Uhr zuzusehen. Onkel Bob erwähnt, dass sich der Code "aus verschiedenen Gründen aufgrund verschiedener Personen ändern wird". Ich denke, das könnte es sein, worauf MichelHenrich uns hinweisen will.
Enrique
Ich habe das gesamte 50-minütige Youtube-Video angesehen, von dem die Mehrheit nicht darüber sprach, was es erklären sollte. Ich bemerkte um 16:00 Uhr, dass er sich bereits mit diesem Thema befasst hatte, und er kehrte nie mehr darauf zurück. Die "Erklärung" läuft im Wesentlichen darauf hinaus: "Verantwortung" in der SRP bedeutet nicht, dass es "unterschiedliche Gründe für eine Änderung" bedeutet, was wirklich "Änderungen auf Antrag verschiedener Personen" bedeutet, was wirklich "Änderungen an der SRP" bedeutet Anfrage von verschiedenen Rollen, die Menschen spielen ". Die "Erklärung" klärt nichts, sie ersetzt ein vages Wort durch ein anderes.
Michael Shaw
2
@MichaelShaw Wie im Zitat aus dem Buch, wenn Sie verschiedene Mitarbeitertypen einführen müssen, müssen Sie die Mitarbeiterklasse ändern. Verschiedene Rollen können für die Bezahlung verschiedener Mitarbeitertypen verantwortlich sein. In diesem Fall muss die Mitarbeiterklasse für mehr als eine Rolle geändert werden, wodurch die SRP verletzt wird.
MichelHenrich
1
@MichaelShaw Ja, Sie haben Recht - SRP hängt davon ab, wie die Organisation organisiert ist. Genau aus diesem Grund füge ich allen meinen Kommentaren "darf" oder "darf" hinzu :). Selbst in diesen Fällen verstößt der Code möglicherweise nicht gegen SRP, aber mit Sicherheit gegen OCP.
MichelHenrich
3

Auf Seite 176, Kapitel 12: Entstehung, enthält das Buch im Abschnitt mit dem Titel Minimale Klassen und Methoden eine Art Korrektur, indem Folgendes angegeben wird:

In dem Bemühen, unsere Klassen und Methoden klein zu halten, werden möglicherweise zu viele kleine Klassen und Methoden erstellt. Diese Regel legt also nahe, dass wir auch unsere Funktion und Klassenanzahl niedrig halten

und

Hohe Klassen- und Methodenzahlen sind manchmal das Ergebnis sinnlosen Dogmatismus.

Offensichtlich spricht er von Dogmatismus, wenn er der SRP folgt, um ganz feine, unschuldige Methoden wie calculatePay()oben zu brechen .

Mike Nakis
quelle
3

Wenn Herr Martin die SRP auf eine Funktion anwendet, erweitert er implizit seine Definition der SRP. Da die SRP eine OO-spezifische Formulierung eines allgemeinen Prinzips ist und eine gute Idee ist, wenn sie auf Funktionen angewendet wird, sehe ich darin kein Problem Definition).

Ich sehe auch nicht mehr als einen Grund für eine Änderung, und ich glaube nicht, dass es hilfreich ist, die SRP in Bezug auf "Verantwortlichkeiten" oder "Gründe für eine Änderung" zu betrachten. Im Grunde geht es der SRP darum, dass Software-Entitäten (Funktionen, Klassen usw.) eines tun und es gut machen sollten.

Wenn Sie sich meine Definition ansehen, ist sie nicht weniger vage als der übliche Wortlaut der SRP. Das Problem mit den üblichen Definitionen der SRP ist nicht, dass sie zu vage sind, sondern dass sie versuchen, etwas zu genau zu sagen, das im Wesentlichen vage ist.

Wenn Sie sich ansehen, was calculatePaypassiert, dann handelt es sich eindeutig um eine einzige Sache: Versand nach Typ. Da Java über integrierte Methoden für typbasiertes Versenden verfügt, calculatePayist es unelegant und nicht idiomatisch. Daher sollte es umgeschrieben werden, jedoch nicht aus den angegebenen Gründen.

Michael Shaw
quelle
-2

Du hast recht @Enrique. Unabhängig davon, ob es sich um eine Funktion oder eine Methode einer Klasse handelt, bedeutet die SRP, dass Sie in diesem Codeblock nur eines tun.

Die "Grund zu ändern" -Anweisung ist manchmal etwas irreführend, aber wenn Sie ändern calculateSalariedPayoder calculateHourlyPayoder die Aufzählung von Employee.typeSie müssen diese Methode ändern.

In Ihrem Beispiel die Funktion:

  • prüft den Typ des Mitarbeiters
  • ruft eine andere Funktion auf, die das Geld nach dem Typ berechnet

Meiner Meinung nach handelt es sich nicht direkt um eine SRP-Verletzung, da die Switch-Cases und die Calls nicht kürzer geschrieben werden können, wenn man an Employee denkt und die Methoden bereits existieren. Auf jeden Fall handelt es sich um einen eindeutigen Verstoß gegen das Open-Closed-Prinzip (OCP), da Sie beim Hinzufügen von Mitarbeitertypen "case" -Statements anhängen müssen. Es handelt sich also um eine schlechte Implementierung: Refactor.

Wir wissen nicht, wie das 'Geld' berechnet werden soll, aber der einfachste Weg ist, eine EmployeeSchnittstelle und einige konkrete Implementierungen mit getMoneyMethoden zu haben. In diesem Fall ist die gesamte Funktion überflüssig.

Wenn es komplizierter ist, es zu berechnen, könnte man das Besuchermuster verwenden, das auch nicht 100% SRP ist, aber es ist mehr OCP als ein Schalterfall.

Aitch
quelle
2
Sie sind sich nicht sicher, wie Sie 2 Aufgaben der Funktion auflisten können, sagen aber, dass dies keine SRP-Verletzung ist.
JeffO
@JeffO: Das sind nicht zwei Dinge, das sind zwei Teile einer Sache: Aufrufen der entsprechenden Funktion basierend auf dem Typ.
Michael Shaw