Das Prinzip der Einzelverantwortung besagt, dass "eine Klasse einen Grund für die Änderung haben sollte".
Im MVC-Muster besteht die Aufgabe des Controllers darin, zwischen der Ansicht und dem Modell zu vermitteln. Es bietet eine Schnittstelle für die Ansicht, um vom Benutzer auf der GUI ausgeführte Aktionen zu melden (z. B. Aufrufen der Ansicht controller.specificButtonPressed()
), und kann die entsprechenden Methoden für das Modell aufrufen, um seine Daten zu manipulieren oder seine Operationen aufzurufen (z. B. model.doSomething()
). .
Das bedeutet, dass:
- Der Controller muss mit der GUI vertraut sein, um der Ansicht eine geeignete Oberfläche zum Melden von Benutzeraktionen anbieten zu können.
- Es muss auch über die Logik im Modell Bescheid wissen, um die entsprechenden Methoden im Modell aufrufen zu können.
Dies hat zwei Gründe, sich zu ändern : eine Änderung in der Benutzeroberfläche und eine Änderung in der Geschäftslogik.
Wenn sich die Benutzeroberfläche ändert, z. B. eine neue Schaltfläche hinzugefügt wird, muss der Controller möglicherweise eine neue Methode hinzufügen, damit die Ansicht einen Benutzer über das Drücken dieser Schaltfläche informiert.
Und wenn sich die Geschäftslogik im Modell ändert, muss der Controller möglicherweise Änderungen vornehmen, um die richtigen Methoden für das Modell aufzurufen.
Daher hat der Controller zwei mögliche Gründe für eine Änderung . Bricht es SRP?
quelle
Antworten:
Wenn Sie konsequent weiter über das SRP nachdenken, werden Sie feststellen, dass "Einzelverantwortung" eigentlich ein schwammiger Begriff ist. Unser menschliches Gehirn ist irgendwie in der Lage, zwischen verschiedenen Verantwortlichkeiten zu unterscheiden, und mehrere Verantwortlichkeiten können zu einer "allgemeinen" Verantwortung zusammengefasst werden. Stellen Sie sich zum Beispiel vor, in einer normalen 4-Personen-Familie ist ein Familienmitglied für die Zubereitung des Frühstücks verantwortlich. Um dies zu tun, muss man Eier kochen und Brot toasten und natürlich eine gesunde Tasse grünen Tee zubereiten (ja, grüner Tee ist am besten). Auf diese Weise können Sie "Frühstück machen" in kleinere Teile zerlegen, die zusammen zu "Frühstück machen" abstrahiert werden. Beachten Sie, dass jedes Stück auch eine Verantwortung darstellt, die z. B. an eine andere Person delegiert werden kann.
Zurück zur MVC: Wenn die Vermittlung zwischen Modell und Ansicht nicht eine, sondern zwei Aufgaben hat, welche Abstraktionsebene müsste dann darüber liegen und diese beiden kombinieren? Wenn Sie keine finden können, haben Sie sie entweder nicht richtig abstrahiert oder es gibt keine, was bedeutet, dass Sie alles richtig verstanden haben. Und ich denke, dass dies bei einem Controller der Fall ist, der eine Ansicht und ein Modell verwaltet.
quelle
Wenn eine Klasse "zwei mögliche Änderungsgründe" hat, verstößt sie gegen die SRP.
Ein Controller sollte normalerweise leichtgewichtig sein und die alleinige Verantwortung haben, die Domäne / das Modell als Reaktion auf ein GUI-gesteuertes Ereignis zu manipulieren. Wir können jede dieser Manipulationen als Anwendungsfälle oder Merkmale betrachten.
Wenn eine neue Schaltfläche auf der GUI hinzugefügt wird, sollte der Controller nur dann Änderungen vornehmen müssen, wenn diese neue Schaltfläche eine neue Funktion darstellt (dh im Gegensatz zu derselben Schaltfläche, die auf Bildschirm 1 vorhanden war, aber auf Bildschirm 2 noch nicht vorhanden war, und dies ist dann der Fall hinzugefügt zu Bildschirm 2). Es müsste auch eine entsprechende neue Änderung im Modell geben, um diese neue Funktionalität / Funktion zu unterstützen. Der Controller hat nur noch die Verantwortung, die Domäne / das Modell als Reaktion auf ein GUI-gesteuertes Ereignis zu manipulieren.
Wenn sich die Geschäftslogik im Modell ändert, weil ein Fehler behoben wurde, und der Controller sich ändern muss, ist dies ein Sonderfall (oder das Modell verletzt möglicherweise das Open-Closed-Prinzip). Wenn sich die Geschäftslogik im Modell ändert, um eine neue Funktionalität / Funktion zu unterstützen, hat dies nicht unbedingt Auswirkungen auf den Controller - nur, wenn der Controller diese Funktion verfügbar machen muss (was fast immer der Fall wäre, andernfalls, warum sie hinzugefügt würde) das Domain-Modell, wenn es nicht verwendet wird). In diesem Fall muss also auch der Controller geändert werden, um das Manipulieren des Domänenmodells auf diese neue Weise als Reaktion auf ein GUI-gesteuertes Ereignis zu unterstützen.
Wenn sich der Controller ändern muss, weil beispielsweise die Persistenzschicht von einer Einfachdatei in eine Datenbank geändert wird, verstößt der Controller mit Sicherheit gegen die SRP. Wenn der Controller immer auf derselben Abstraktionsebene arbeitet, kann dies zur Erzielung von SRP beitragen.
quelle
Der Controller verstößt nicht gegen SRP. Wie Sie feststellen, ist es seine Aufgabe, zwischen den Modellen und der Ansicht zu vermitteln.
Das Problem mit Ihrem Beispiel ist jedoch, dass Sie Controller-Methoden mit der Logik in der Ansicht verknüpfen, d
controller.specificButtonPressed
. H. Wenn Sie die Methoden so benennen, dass sie den Controller an Ihre GUI binden, ist es Ihnen nicht gelungen, die Dinge richtig zu abstrahieren. Der Controller sollte bestimmte Aktionen ausführen, dhcontroller.saveData
odercontroller.retrieveEntry
. Das Hinzufügen einer neuen Schaltfläche in der GUI bedeutet nicht unbedingt, dass der Steuerung eine neue Methode hinzugefügt wird.Ein Knopfdruck in der Ansicht bedeutet, etwas zu tun, aber was auch immer das ist, könnte auf eine beliebige Anzahl von anderen Wegen oder gar nicht durch die Ansicht ausgelöst worden sein.
Aus dem Wikipedia-Artikel über SRP
Der Controller kümmert sich nicht um die Ansicht, sondern nur darum, dass er bei einem Aufruf einer seiner Methoden der Ansicht bestimmte Daten zur Verfügung stellt. Es muss nur die Funktionalität des Modells kennen, sofern bekannt ist, dass Methoden aufgerufen werden müssen, über die das Modell verfügt. Mehr als das weiß es nicht.
Zu wissen, dass für ein Objekt eine Methode zum Aufrufen verfügbar ist, ist nicht dasselbe wie zu wissen, wie es funktioniert.
quelle
specificButtonsPressed()
ist, dass ich gelesen habe, dass die Ansicht nichts über die Funktionalität ihrer Schaltflächen und anderer GUI-Elemente wissen sollte. Es wurde mir beigebracht, dass beim Drücken einer Taste die Ansicht einfach dem Controller gemeldet werden sollte und der Controller entscheiden sollte, was dies bedeutet (und dann die entsprechenden Methoden für das Modell aufrufen sollte). Das Aufrufen der Ansichtcontroller.saveData()
bedeutet, dass die Ansicht wissen muss, was dieser Tastendruck bedeutet, abgesehen von der Tatsache, dass er gedrückt wurde.specificButtonPressed()
), wäre der Controller in der Tat nicht so sehr an die GUI gebunden. Sollte ich diespecificButtonPressed()
Methoden fallen lassen? Hat der Vorteil, den ich mit diesen Methoden sehe, für Sie einen Sinn? OderbuttonPressed()
lohnt es sich nicht , Methoden in der Steuerung zu haben?specificButtonPressed()
Methoden im Controller darin besteht, dass die Ansicht von der Bedeutung des Tastendrucks vollständig getrennt wird . Der Nachteil ist jedoch, dass der Controller in gewisser Weise an die GUI gebunden ist. Welcher Ansatz ist besser?foo
, oder genauso einfachfireZeMissiles
. Es wird nur wissen, dass es an eine bestimmte Funktion berichten soll. Es weiß nicht, was die Funktion tut, nur dass sie es aufruft. Der Controller kümmert sich nicht darum, wie seine Methoden aufgerufen werden, nur dass er auf bestimmte Weise reagiert, wenn dies der Fall ist.Eine einzige Verantwortung des Controllers ist der Vertrag, der zwischen der Ansicht und dem Modell vermittelt. Die Ansicht sollte nur für die Anzeige verantwortlich sein, das Modell sollte nur für die Geschäftslogik verantwortlich sein. Es liegt in der Verantwortung der Controller, diese beiden Verantwortlichkeiten miteinander zu verbinden.
Das ist alles schön und gut, aber sich ein bisschen von der akademischen Welt abzuwenden. Ein Controller in MVC besteht im Allgemeinen aus vielen kleineren Aktionsmethoden. Diese Aktionen entsprechen im Allgemeinen dem, was man tun kann. Wenn ich Produkte verkaufe, werde ich wahrscheinlich einen ProductController haben. Dieser Controller verfügt über Aktionen wie GetReviews, ShowSpecs, AddToCart ect ...
Die Ansicht hat die SRP zum Anzeigen der Benutzeroberfläche, und ein Teil dieser Benutzeroberfläche enthält eine Schaltfläche mit der Aufschrift AddToCart.
Der Controller verfügt über die SRP, um alle am Prozess beteiligten Ansichten und Modelle zu kennen.
Die AddToCart-Aktion des Controllers verfügt über die spezifische SRP, die alle Personen kennt, die beteiligt sein müssen, wenn ein Artikel einem Warenkorb hinzugefügt wird.
Das Produktmodell verfügt über die SRP zum Modellieren der Produktlogik und das ShoppingCart-Modell über die SRP zum Modellieren, wie Artikel für eine spätere Prüfung gespeichert werden. Das Benutzermodell verfügt über eine SRP, die den Benutzer modelliert, der seinem Einkaufswagen Material hinzufügt.
Sie können und sollten Modelle wiederverwenden, um Ihr Geschäft zu erledigen, und diese Modelle müssen an einem bestimmten Punkt in Ihrem Code gekoppelt werden. Die Steuerung steuert jede Art und Weise, wie die Kopplung erfolgt.
quelle
Controller haben tatsächlich nur eine Verantwortung: den Anwendungsstatus basierend auf Benutzereingaben zu ändern.
source: wikipedia
Wenn Sie stattdessen Rails-artige "Controller" haben (die aktive Record-Instanzen und dumme Templates unter einen Hut bringen) , dann ist SRP natürlich ein Problem.
Andererseits sind Anwendungen im Rails-Stil anfangs nicht wirklich MVC.
quelle