15

In alcuni progetti MVC a cui ho lavorato, è diventato evidente che ci sono alcuni controllori problematici che sono cresciuti organicamente nelle classi di Dio - semi-divinità ciascuno nel proprio dominio, se vuoi.Controllori di Dio - Come prevenirli?

Questa domanda potrebbe essere più una questione "che cosa va dove", ma penso che sia una questione importante per quanto riguarda SRP (Principio della singola responsabilità), DRY (Non ripeterti) e mantenere le cose concise, " agile "- e non sono abbastanza esperto (con questo modello e in generale design) per essere informato su questo.

In un progetto, abbiamo un NutritionController. Nel corso del tempo è cresciuto fino ad includere queste azioni (molti con i loro rispettivi, GET, POST, e DELETE metodi):

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

Poi abbiamo un ExerciseController, che includerà molte azioni simili, come la ricerche e azioni preferite. Dovrebbero essere refactored nel loro proprio controllore in modo che sia qualcosa come così?

SearchController { 
    SearchExercise 
    SearchNutrition 
    //... etc 
} 

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

sembra solo a me che se si rompe fuori in controller separati, si sta andando a crescere un incredibilmente grande dipendenza a un certo livello a che fare con le informazioni che avrete bisogno. OPPURE avrai un'applicazione di gestione completamente generica che sarà molto difficile da gestire dato che dovrai passare attraverso così tanti cerchi per ottenere l'effetto desiderato (a livello M, V o C).

Sto pensando a questo nel modo sbagliato? Ad esempio, dovrei avere un oggetto Preferiti generico e quindi lasciare che il controller decida la vista a cui lanciarlo?

* Ci scusiamo per enunciando gli acronimi - che sto facendo così nel caso in cui nessun altro si imbatte in questa domanda ed è all'oscuro di ciò che quelle cose sono

EDIT: Tutta la logica mi esibisco è praticamente gestito nei livelli di servizio. Ad esempio, il controller invierà il "nuovo" FoodItem al servizio. Se esiste già, o c'è un errore, il servizio lo rispedirà al controller.

risposta

12

vorrei rompere il vostro primo elenco sulla base di responsabilità:

HomeController

  • Indice

FoodItemController

  • ViewFoodItem
  • AddFoodItem
  • EditFoodItem
  • DeleteFoodItem
  • SearchFoodItem

NutritionController

  • ViewNutritionSummary

FavoritesController

  • AddToFavorites
  • RemoveFromFavorites
  • ViewFavorites
  • SearchFavorites
  • approccio MVC

s' Django è quello di separare le responsabilità in "applicazioni", ognuno con i propri modelli , controller e persino modelli se necessario. Avresti un'app di cibo, un'app di nutrizione, un'app di ricerca e un'app di preferiti, molto probabilmente.

Modifica: l'OP ha menzionato che la ricerca è più specifica per ciascun controller, quindi ho eseguito tali azioni. Tuttavia, la ricerca potrebbe anche essere solo una cosa globale generale, quindi in quei casi, un SearchController andrebbe bene.

+0

Quindi, quindi, dovrei replicare le stesse cose per gli Esercizi o aggiungerli a quei controller? OSSIA il SearchController gestirà i metodi SearchExerciseItem o sarebbe un altro controller come SearchExerciseController? – MunkiPhD

+0

Se è così che l'hai impostato, la ricerca è un'azione sul controller, non un controller stesso a questo punto. Se la ricerca fosse qualcosa di generale, allora potrebbe essere il suo controller. Ho modificato la mia risposta per riflettere questo. – Soviut

+0

MVC funziona con REST in modo molto naturale: tutti i controller "controllano" un singolo tipo di risorsa e sanno come eseguire varie azioni su quella risorsa (ovvero rispondono ai vari messaggi passati a quella risorsa), con la mappatura dei tipi di risorse REST a tipi di entità modello di dominio per lo più one-to-one (che è il punto di avere un modello di dominio). – yfeldblum

1

Non ho molta familiarità con questo quadro, ma posso offrire un po 'di consigli generali. Un controller dovrebbe probabilmente sapere solo come completare una singola azione, o per invocare altri controller a singola azione per completare una sequenza di azioni correlate. Qualsiasi informazione che deve essere passata dall'azione all'azione dovrebbe probabilmente essere in qualche modo passata attraverso il livello del modello, poiché tale informazione è molto probabilmente rilevante per il modello sottostante.

+2

Non è così che funziona in molti framework MVC (ad esempio Rails, ASP.NET MVC). Un singolo controller sa come eseguire molte azioni su un singolo tipo di entità, ma non dovrebbe sapere come eseguire azioni su altri tipi di entità. – yfeldblum

2

Fai come dice Soviut. Vuoi mantenere i controllori semplici. Sembra che tu finisca con troppa logica di coordinamento nei tuoi controller. Ricorda che sono responsabili dell'aggancio di una vista e di un modello. Questa logica di coordinamento dovrebbe probabilmente essere suddivisa in servizi.

Ho questa sensazione perché si menziona la possibilità che il controllore sviluppi enormi dipendenze. Bene Se PreferitiController ha bisogno di sapere su nutrizione ed esercizio preferiti (per visualizzare nella stessa vista) non rendere il controller dipendente da 2 repository come classi. Invece, incapsula quel comportamento di coordinamento. Forse creare un servizio Preferiti che sappia come restituire sia la nutrizione che i preferiti dell'esercizio. Tale servizio potrebbe delegare a NutritionFavoritesService and ExerciseFavoritesService.In questo modo il tuo controller finisce con 1 dipendenza, stai mantenendo le cose ASCIUTTE, rafforzando l'SRP e concentrando la tua logica di business in qualche posto diverso dal controller.

+0

Ho la maggior parte della logica di coordinamento nei servizi, ma sembra come se avessi metodi molto specifici in ENTRAMBI i miei controller e livelli di servizio. Ad esempio, il controller avrà la chiamata "GetFavoriteFoodItemsForUser" al livello di servizio in cui gestisco tutto e restituisco un elenco, che il controllore quindi scarica in una vista. – MunkiPhD

+0

Ah. Cercherò di formulare alcune regole generali e aggiornare la mia risposta. Per l'esempio che hai fornito, probabilmente avrei un UserController con un metodo FavoirteFoodTiems che accetta solo HttpMetthod GET. –

+0

La tua modifica è stata chiarita un po 'per me - Grazie – MunkiPhD

1

Ho anche sperimentato questo tipo di mal di testa di manutenzione e trovo che aderire a un metodo simile a "Rails" è di grande aiuto nel mantenere i miei controller concentrati e non sfruttati.

Se mi ritrovo ad aggiungere azioni con nomi insoliti Ad es. Per utilizzare un esempio di blog, AddPostToBlog, sarebbe un flag per creare un nuovo controller di post con un'azione Create.

In altre parole, se l'azione non è né una delle azioni Indice, Nuovo, Crea, Mostra, Modifica, Aggiorna e Distruggi, quindi aggiungo un nuovo controller specifico all'azione richiesta.

Per il tuo esempio.

SearchController { 
    SearchExercise 
    SearchNutrition 
    //... etc 
} 

avrei refactoring questo per ...

SearchExerciseController { 
      Index 
    } 

    SearchNutritionController { 
      Index 
    } 

Questo può significare avere più controller, ma a mio parere questo è più facile da gestire rispetto in continua espansione controllori "Dio". Significa anche che i controller sono più auto-documentanti.

Es. L'azione SearchExercise, restituisce la vista per cercare l'esercizio o esegue effettivamente la ricerca? Probabilmente potresti accertarlo osservando i parametri e il corpo, ma non è facile come ad esempio una coppia di azioni Nuovo e Crea, o Modifica e Aggiorna.

SearchController { 
    SearchExercise  
}