【问题标题】:PHP MVC - Why passing controller instance to view class?PHP MVC - 为什么将控制器实例传递给视图类?
【发布时间】:2014-02-17 17:04:05
【问题描述】:

我读过很多 MVC 文章,例如 this sitethis。我还是不明白为什么要将控制器传递给视图类?

<?php
$model = new Model();
$controller = new Controller($model);
$view = new View($controller, $model);

if (isset($_GET['action']) && !empty($_GET['action'])) {
    $controller->{$_GET['action']}();
}

echo $view->output();

什么时候我们可以在引导或前端控制器中调用控制器动作,那么这样做的目的是什么?

$view = new View($controller, $model);

【问题讨论】:

  • 第一个链接,作者公开:“注意,控制器被传递给视图,因为在桌面应用程序中,视图需要在正确的控制器上创建回调,所以它需要知道哪个控制器正在使用。这仍然发生在网络上,但它是一种更微妙的方式。在第 2 部分中,我将展示如何在网络上解决这个问题。",尽管我不同意。
  • 感谢您的快速回复。这意味着,当我在控制器中只有 1 个方法时(只是动作),所以我不需要将控制器传递给查看类?
  • 那篇文章的作者错误地将控制器传递给视图。 MVC 模式中的控制器负责改变模型层和视图的状态。 View 不负责更改控制器。我感觉作者想应用经典的 MVC 模式,却没有真正理解视图和控制器之间的通信是如何处理的。

标签: php model-view-controller


【解决方案1】:

理论

在宏观设计级别,MVC 是一种架构决策,它表明开发中必须分离三个概念。模型、视图和控制器。

为避免在 SO 上出现更多“MVC 理论”重复,这是一个非常受欢迎的答案,其中解释了 MVC 的基础知识:https://stackoverflow.com/a/5864000/1311025

MVC 的实现

您必须选择适合您的项目要求和变更观点的实施。

例如。如果我认为我的数据库将来会改变(我不知道什么时候,我只是怀疑它会改变),我会做一个完全解耦我的数据库的实现,creating a layer that abstracts the implementation of it from the controller. 并实现一个工厂,为你提供访问数据库的正确实例。

如果我认为我的视图会改变,那么我将在视图中使用观察者模式来解耦控制器和视图。控制器和视图将通过事件进行通信。

然后,您可以按照满足您需求的软件设计模式构建您自己的 MVC 实现。


现在,根据您提供的代码,

<?php
$model = new Model();
$controller = new Controller($model);
$view = new View($controller, $model);

if (isset($_GET['action']) && !empty($_GET['action'])) {
    $controller->{$_GET['action']}();
}

echo $view->output();

思想是在类(模型、视图和控制器)中分离职责,对我来说,这是一个将视图与模型耦合的示例。但如果我们希望视图、控制器和模型永远像这样并且永远不会改变,那可能是对的。
同样通过这种实现,控制器具有一个状态,这反映了这将永远不会扩展(或者不是以简单的方式)。

免责声明:这是我为一个简单、快速和小项目的实现。 如果您需要一个更严肃的解决方案,而质量不是一个选项, 那么您必须遵循更严格的准则。

在我看来,我会选择至少将视图与模型分离。

<?php
$controller = new Controller();
if (isset($_GET['action']) && !empty($_GET['action'])) {
    $controller->doAction($_GET['action']);
}

控制器不必处理全局 php 变量($_GET):

<?php
Controller {
     public function __construct() {}

     public function doAction($action) {
        $model = new User();
        $model->setName("user36279");

        //This just transforms the user model in an associative array
        $UserDTO = $model->toDTO();
        $view = new UserView();
        //We don't send the model to the view increasing decoupling  
        $view->render($UserDTO);
     } 
}

但这将是我的实现。现在由您来决定要解耦多少,想要多少可维护性、复杂性和可测试性。

编辑:
有人指出:

[...] 你的“控制器”实际上负责路由、检索 来自模型层的信息和响应的呈现,其中 当然违反了 SRP 和 SoC。底线:所有这些都是 完全错误

他是对的,我的代码违反了 SRP。但是看看这个问题:
Does the traditional use of the controller in MVC lead to a violation of the Single Responsibility Principle?

如果你想遵循 SRP,你可以分解你的控制器 进入 Dispatcher 和 Actions; Dispatcher 调度控制到 它的行动[...]

为什么我们不经常看到这种情况?因为控制器通常是“广告 hoc" 实现,不是叶级的具体类 泛化,并不意味着被子类化。这里使用了类 更方便地对代码进行分组,操作几乎可以肯定 非公开的(可能是私有的,可能是受保护的),“仅仅是”内部的 实现细节。

选择如何决定调度什么动作,数量和 可能动作的多样性,高,调度和动作是 紧密耦合。 因此,在实践中,将 在一个地方一起编码。

SOLID 原则是设计面向对象的重要指南。这里的一般规则是承认不同的选择并选择最适合您需求的选择。原则是指导方针而不是规则,遵循原则的主要优势是一组通用的解决方案,这些解决方案与语言无关,对于新手和专家开发人员来说都易于使用和理解,并且我们的设计简单。

底线:何时需要应用 SRP(单一责任原则)并因此应用 SOLID,取决于您。

【讨论】:

  • -1 “领域模型”是描述给定项目积累知识的概念。 它与 MVC 无关。 模型不是对象也不是文件。基本上,你完全错了。而你的“控制器”实际上负责路由、从模型层检索信息和呈现响应,这当然违反了 SRP 和 SoC。底线:所有这些都是完全错误的
  • Tomas,请允许我通过感谢您做出如此广泛的努力来平衡上述贡献者。如果每个答案都一样彻底!有一些标签吸引了相当多的关注,您会发现,mvc 就是其中之一。
  • Tomas,请允许我说这与 teresko 所说的完全一样,以平衡上述贡献。不要再混淆人们了。此外,您的代码还有其他代码气味,例如在这种情况下使用 new 有点矛盾 The controller will be stateless, and completely reusable and testeable 您提供的代码不是...
  • 感谢您指出错误。为避免重复,我提供了关于 MVC 的 tereško 答案的链接(更完整)。对于其余的答案,我将发出更大的警告,这是我对一个快速而小项目的务实实施。实现总是与 OP 真正需要的以及纯粹主义者真正想要看到的不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-09
相关资源
最近更新 更多