15

In einigen MVC-Projekten, an denen ich gearbeitet habe, ist es offensichtlich geworden, dass es einige problematische Controller gibt, die organisch zu Gott-Klassen gewachsen sind - demi-gods jeweils in ihrem eigenen Bereich, wenn du so willst.Gott-Controller - Wie man sie verhindert?

Diese Frage könnte eher eine Frage sein, "was wohin geht", aber ich denke, es ist eine wichtige Frage in Bezug auf SRP (Prinzip der einfachen Verantwortung), DRY (Do not Repeat Yourself) und die Dinge kurz halten, " agil "- und ich bin nicht genug erfahren (mit diesem Muster und im allgemeinen Design), um darüber informiert zu sein.

In einem Projekt haben wir einen NutritionController. Im Laufe der Zeit ist es gewachsen, diese Aktionen enthalten (viele mit ihren jeweiligen, GET, POST und DELETE-Methoden):

Index (home controller) 
ViewFoodItem 
AddFoodItem 
EditFoodItem 
DeleteFoodItem 
ViewNutritionSummary 
SearchFoodItem 
AddToFavorites 
RemoveFromFavorites 
ViewFavorites 

Dann haben wir eine ExerciseController, die viele ähnliche Aktionen, wie zum Beispiel die umfassen wird Suchvorgänge und Favoritenaktionen. Sollten diese in ihren eigenen Controller umgestaltet werden, so dass es so etwas ist?

SearchController { 
    SearchExercise 
    SearchNutrition 
    //... etc 
} 

FavoritesController { 
    ViewNutritionFavorites 
    AddToNutritionFavorites 
    AddToExerciseFavorites 
    EditNutritionFavorites 
    EditExerciseFavorites 
    //... etc 
} 

Es scheint nur zu mir, dass, wenn man sie bricht in separate Controller, Sie gehen eine unglaublich große Abhängigkeit zu einem gewissen Grad wachsen mit den Informationen umgehen, die Sie benötigen. ODER Sie werden eine komplett generische Anwendung haben, die sehr schwierig zu handhaben ist, da Sie durch so viele Ringe springen müssen, um den gewünschten Effekt zu erhalten (entweder auf M-, V- oder C-Level).

Ich denke über den falschen Weg nach? Zum Beispiel, sollte ich ein generisches Favoriten-Objekt haben und dann den Controller entscheiden lassen, in welche Ansicht er geworfen werden soll?

* Sorry für die Akronyme buchstabieren - ich bin so in Fall tun sonst jemand über diese Frage kommt und ahnungslos, was diese Dinge sind

EDIT: die Logik I Alle auszuführen ist ziemlich viel in den Service-Schichten behandelt. Zum Beispiel sendet der Controller das "neue" FoodItem an den Service. Wenn es bereits vorhanden ist oder ein Fehler vorliegt, wird der Dienst den Controller erneut zur Verfügung stellen.

Antwort

12

Ich würde brechen Sie Ihre erste Liste nach oben basiert auf Verantwortung:

Homecontroller

  • Index

FoodItemController

  • ViewFoodItem
  • AddFoodItem
  • EditFoodItem
  • DeleteFoodItem
  • SearchFoodItem

NutritionController

  • ViewNutritionSummary

FavoritesController

  • AddToFavorites
  • RemoveFromFavorites
  • ViewFavorites
  • SearchFavorites

Django ‚s Ansatz zu MVC ist Verantwortlichkeiten in "Anwendungen" zu trennen, die jeweils mit ihren eigenen Modellen , Controller und sogar Vorlagen, falls erforderlich. Sie haben höchstwahrscheinlich eine Google Essen App, eine Nutrition App, eine Such App und eine Favoriten App.

Edit: Das OP erwähnt, dass die Suche für jeden Controller spezifischer ist, also habe ich diese Aktionen gemacht. Die Suche kann jedoch auch nur eine allgemeine globale Sache sein. In diesen Fällen wäre ein SearchController in Ordnung.

+0

Also würde ich die gleichen Dinge für die Übungen replizieren oder sie diesen Controllern hinzufügen? I.E. Würde der SearchController die SearchExerciseItem-Methoden behandeln, oder wäre das ein anderer Controller wie SearchExerciseController? – MunkiPhD

+0

Wenn dies so eingerichtet ist, ist die Suche eine Aktion auf dem Controller, nicht ein Controller selbst zu diesem Zeitpunkt. Wäre das Suchen etwas Allgemeines, könnte es dann sein eigener Controller sein. Ich habe meine Antwort geändert, um dies zu reflektieren. – Soviut

+0

MVC arbeitet auf sehr natürliche Weise mit REST: Alle Ihre Controller "steuern" eine einzige Art von Ressource und wissen, wie verschiedene Aktionen auf dieser Ressource ausgeführt werden (dh auf verschiedene an diese Ressource übergebene Nachrichten reagieren) zu Domänenmodell-Entitätstypen meist eins zu eins (das ist der Punkt, an dem ein Domänenmodell vorhanden ist). – yfeldblum

1

Ich bin nicht so vertraut mit diesem Framework, aber ich kann ein wenig allgemeine Ratschläge geben. Ein Controller sollte wahrscheinlich nur wissen, wie er eine einzelne Aktion ausführt oder andere Einzelaktions-Controller aufruft, um eine Abfolge verwandter Aktionen abzuschließen. Alle Informationen, die von Aktion zu Aktion weitergegeben werden müssen, sollten wahrscheinlich irgendwie durch die Modellschicht geleitet werden, da diese Information höchstwahrscheinlich für das zugrunde liegende Modell relevant ist.

+2

Das funktioniert nicht in vielen MVC-Frameworks (z. B. Rails, ASP.NET MVC). Ein einzelner Controller weiß, wie viele Aktionen für eine einzige Art von Entität ausgeführt werden, aber er sollte nicht wissen, wie er Aktionen für andere Arten von Entitäten ausführen soll. – yfeldblum

2

Tun Sie, wie Soviut sagt. Sie möchten die Controller einfach halten. Es hört sich an, als ob Sie in Ihren Controllern zu viel Koordinationslogik haben. Denken Sie daran, dass sie dafür verantwortlich sind, eine Ansicht und ein Modell anzuhängen. Diese Koordinationslogik sollte wahrscheinlich in Dienste aufgeteilt werden.

Ich bekomme dieses Gefühl, weil Sie die Möglichkeit Ihres Controllers erwähnen, große Abhängigkeiten zu wachsen. Nun, wenn FavoritesController über Ernährung und Übungsfavoriten wissen muss (um in der gleichen Ansicht angezeigt zu werden), machen Sie Ihren Controller nicht abhängig von 2 Repositories wie Klassen. Umhüllen Sie stattdessen dieses Koordinationsverhalten. Vielleicht erstellen Sie einen Favoriteservice, der weiß, wie Sie sowohl Ernährungs- als auch Trainingsfavoriten zurückgeben können. Dieser Dienst kann an NutritionFavoritesDienst und ÜbungFavoritesService delegieren.Auf diese Weise erhalten Sie als Controller nur eine Abhängigkeit, Sie behalten die DRY-Position, erzwingen die SRP und konzentrieren Ihre Geschäftslogik an einem anderen Ort als dem Controller.

+0

Ich habe die meisten der Koordinationslogik in den Diensten, aber es scheint, als ob ich sehr spezifische Methoden in BEIDE meine Controller und Service-Schichten haben. Beispielsweise wird der Controller den 'GetFavoriteFoodItemsForUser'-Aufruf an die Dienstschicht senden, wo ich alles handle und eine Liste zurückgebe, die der Controller dann zu einer Ansicht ablegt. – MunkiPhD

+0

Ah. Ich werde versuchen, einige allgemeine Regeln aufzustellen und meine Antwort zu aktualisieren. Für das von dir bereitgestellte Beispiel hätte ich wahrscheinlich einen UserController mit einer Methode FavoirteFoodTiems, die nur HttpMethod GET akzeptiert. –

+0

Ihre Bearbeitung hat sich für mich ein wenig geklärt - Danke – MunkiPhD

1

Ich habe auch diese Art von Maintenance-Kopfschmerzen erlebt und halte es für eine "Rails" -ähnliche Methode, sehr hilfreich zu sein, meine Controller fokussiert und unblotiert zu halten.

Wenn ich finde, dass ich Aktionen mit ungewöhnlichen Namen hinzufüge, z. Um ein Blogging-Beispiel, AddPostToBlog, zu verwenden, wäre das ein Flag zum Erstellen eines neuen Post-Controllers mit einer Create-Aktion.

Mit anderen Worten, wenn die Aktion keine der Aktionen Index, Neu, Erstellen, Anzeigen, Bearbeiten, Aktualisieren und Zerstören ist, füge ich einen neuen Controller hinzu, der für die Aktion spezifisch ist, die ich benötige.

Für Ihr Beispiel.

SearchController { 
    SearchExercise 
    SearchNutrition 
    //... etc 
} 

würde ich Refactoring dies ...

SearchExerciseController { 
      Index 
    } 

    SearchNutritionController { 
      Index 
    } 

Dies kann bedeuten, mehrere Controller zu haben, aber meiner Meinung nach das ist einfacher zu verwalten als je zuvor „Gott“ Controller erweitert. Es bedeutet auch, dass die Controller selbstdokumentierter sind.

Eg. Gibt die SearchExercise-Aktion die Ansicht zurück, um die Übung zu durchsuchen, oder führt sie die Suche tatsächlich durch? Sie könnten dies wahrscheinlich durch die Betrachtung von Parametern und Körper feststellen, aber es ist nicht so einfach wie zum Beispiel ein Aktionspaar Neu und Erstellen oder Bearbeiten und Aktualisieren.

SearchController { 
    SearchExercise  
}