上帝控制者 - 如何预防他们?

时间:2009-06-21 16:09:06

标签: asp.net-mvc model-view-controller design-patterns god-object

在我一直在研究的一些MVC项目中,很明显有一些有问题的控制器已经有机地成长为上帝阶级 - 如果你愿意的话,每个人都在他们自己的领域。

这个问题可能更像是“什么地方”,但我认为这是一个关于SRP(单一责任原则),DRY(不要重复自己)和保持简洁的重要问题,“敏捷“ - 我没有足够的经验(有这种模式和一般设计)对此有所了解。

在一个项目中,我们有一个NutritionController。随着时间的推移,它已经发展到包含这些行为(许多行动都有各自的GET,POST和DELETE方法):

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

然后我们有一个ExerciseController,它将包含许多类似的操作,例如搜索和收藏夹操作。这些应该重构到他们自己的控制器中,以便它是这样的吗?

SearchController {
    SearchExercise
    SearchNutrition
    //... etc
}

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

在我看来,如果你将它们分解为单独的控制器,你将在某种程度上增加一个令人难以置信的大依赖性来处理你需要的信息。或者你将拥有一个非常难以处理的完全通用的处理应用程序,因为你必须跳过这么多的箍来获得你想要的效果(在M,V或C级别)。

我在考虑错误的方法吗?例如,我应该有一个通用的收藏夹对象,然后让控制器决定将它扔到哪个视图?

*很抱歉拼写出缩略语 - 我这样做是为了防止其他人遇到这个问题并且对这些东西是什么一无所知

修改 我执行的所有逻辑几乎都在服务层中处理。例如,控制器将'new'FoodItem发送给服务。如果它已经存在,或者它出现错误,服务会将其冒泡回控制器。

4 个答案:

答案 0 :(得分:12)

我会根据责任打破你的第一个清单:

<强>的HomeController

  • 索引

<强> FoodItemController

  • ViewFoodItem
  • AddFoodItem
  • EditFoodItem
  • DeleteFoodItem
  • SearchFoodItem

NutritionController

  • ViewNutritionSummary

<强> FavoritesController

  • 添加addToFavorites
  • RemoveFromFavorites
  • ViewFavorites
  • SearchFavorites

Django对MVC的处理方法是将职责分离为“应用程序”,每个应用程序都有自己的模型,控制器,甚至模板(如有必要)。你很可能有一个Food应用程序,一个营养应用程序,一个搜索应用程序和一个收藏夹应用程序。

编辑:OP提到搜索更具体到每个控制器,所以我做了那些动作。但是,搜索也可能只是一般的全局事情,因此在这些情况下,SearchController就可以了。

答案 1 :(得分:2)

苏维埃说。您希望保持控制器简单。听起来你的控制器中的协调逻辑太多了。请记住,他们负责连接视图和模型。这种协调逻辑应该可以分解为服务。

我有这种感觉,因为你提到了控制器增加巨大依赖性的可能性。好吧如果FavoritesController需要了解营养和锻炼收藏(在同一视图中显示),不要让你的控制器依赖类似于2个存储库。相反,封装了这种协调行为。也许创建一个了解如何返回营养和锻炼收藏的FavoritesService。该服务可能委托给NutritionFavoritesService和ExerciseFavoritesService。这样你控制器最终只有1个依赖,你要保持干燥,强制执行SRP,并将业务逻辑集中在控制器以外的某个地方。

答案 2 :(得分:1)

我对这个框架不太熟悉,但我可以提供一些一般的建议。控制器应该只知道如何完成单个操作,或者调用其他单个操作控制器来完成一系列相关操作。任何必须在操作之间传递的信息都应该以某种方式通过模型层传递,因为该信息很可能与底层模型相关。

答案 3 :(得分:1)

我也经历过这些类型的维护问题,并且发现坚持使用“Rails”这样的方法非常有助于保持控制器的集中和非浮动状态。

如果我发现自己添加了不寻常名字的动作,例如。使用博客示例AddPostToBlog,这将是一个标志,用于创建具有Create操作的新Post控制器。

换句话说,如果操作不是索引,新建,创建,显示,编辑,更新和销毁操作之一,那么我会添加一个特定于我需要的操作的新控制器。

为您的例子。

SearchController {
    SearchExercise
    SearchNutrition
    //... etc
}

我会把它重构为......

   SearchExerciseController {
           Index   
   }

   SearchNutritionController {
           Index   
   }

这可能意味着拥有更多的控制器,但在我看来,这比管理“神”控制器更容易管理。这也意味着控制器更加自我记录。

EG。 SearchExercise操作,返回View以搜索练习还是实际执行搜索?您可以通过查看参数和正文来确定这一点,但它并不像“新建”和“创建”或“编辑和更新”操作对那样简单。

SearchController {
    SearchExercise       
}