1 php model-view-controller constructor design-patterns dependency-injection
我正在做一个个人 HMVC 项目:
static或global),没有单例。所有需要的依赖项都被注入到抽象控制器的构造函数中。如果我想覆盖这个构造函数,那么我也必须在子控制器的构造函数中传递所有这些依赖项。
class UsersController extends AbstractController {
private $authentication;
public function __construct(
Config $config
, Request $request
, Session $session
, View $view
, Response $response
, Logger $logger
, Authentication $authentication // Domain model service
) {
parent::__construct(/* All dependencies except authentication service */);
$this->authentication = $authentication;
}
// Id passed by routing.
public function authenticateUser($id) {
// Use the authentication service...
}
}
Run Code Online (Sandbox Code Playgroud)
依赖项列表将进一步增长。这需要改变。于是我在想:
Response将成为视图的依赖项。Request,Session,Logger,等;Request, Session,Logger,等;AbstractController了。我正在努力寻找一种优雅的方式来处理这项任务,我将不胜感激任何建议。谢谢你。
小智 5
我会回答我自己的问题。当我写它时,我已经很好地概述了许多有经验的开发人员关于 MVC 和 MVC 结构中的依赖注入的建议。
因此,无论我做了什么,这些解决方案似乎都不适合我的 HMVC 项目的结构。所以,我进一步挖掘,直到我意识到缺失的环节是什么。为此,我非常感谢汤姆巴特勒,他是以下伟大文章的创造者:
他的作品基于对 MVC 概念的深入、充分论证的分析。它们不仅很容易理解,而且还可以通过不言自明的例子来支持。一句话:对 MVC 和开发者社区的杰出贡献。
我将进一步写的内容只是用我自己的话来介绍他的原则,考虑以某种方式完成它们,提供更紧凑的视角,并展示我在我的应用程序中实施它们时所遵循的步骤项目。此处描述的有关该主题、想法和原则以及工作流程的所有功劳均归功于Tom Butler。
那么,我的 HMVC 项目中缺少的链接是什么?它被命名为SPARATION OF CONCERNS。
为简单起见,我将尝试通过仅提及一个控制器、一个控制器操作、一个视图、一个模型(域对象)和一个模板(文件)来解释这一点,并将它们引入User上下文。
Web 上最常描述的 MVC 概念 - 也由我研究过的一些流行框架实现 - 以让控制器控制视图和模型的原则为中心。为了在屏幕上显示一些东西,你必须告诉控制器——他进一步通知视图加载和渲染模板。如果这个显示过程也暗示使用一些模型数据,那么控制器也会操纵模型。
以经典的方式,控制器创建和动作调用过程包括两个步骤:
编码:
$controller = new UserController(/* Controller dependencies */);
$controller->{action}(/* Action dependencies */);
Run Code Online (Sandbox Code Playgroud)
这意味着,控制器负责一切。所以,难怪为什么一个控制器必须注入这么多依赖项。
但是控制器是否应该参与或负责在屏幕上有效地显示任何类型的信息?不,这应该是视图的责任。为了实现这一点,让我们开始将视图与控制器分离——从不需要任何模型的前提出发。涉及的步骤是:
output在视图中的屏幕上显示信息的方法。output视图的方法:和代码:
class UserView {
//....
// Display information on screen.
public function output () {
return $this
->load('<template-name>')
->render(array(<data-to-display>))
;
}
//....
}
$controller = new UserController(/* (less) controller dependencies */);
$view = new UserView(/* View dependencies */);
$controller->{action}(/* Action dependencies */);
echo $view->output();
Run Code Online (Sandbox Code Playgroud)
通过完成上面的五个步骤,我们设法将控制器与视图完全分离。
但是,有一个方面,我们之前假设过:我们没有使用任何模型。那么控制器在这个星座中的作用是什么?答案是:没有。控制器应该仅作为某个存储位置(数据库、文件系统等)和视图之间的中间人存在。否则,例如仅以某种格式在屏幕上输出一些信息,output视图的方法就完全足够了。
如果模特被带到现场,事情就会改变。但是应该在哪里注射呢?在控制器中还是在视图中?同时。它们共享相同的模型实例。在这一刻,控制器凭借自己的权利获得了中间人的角色——介于存储和视图之间。一种理论形式是:
$model = new UserModel;
$controller = new UserController($model, /* Other controller dependencies */);
$view = new UserView($model, /* Other view dependencies */);
$controller->{action}(/* Action dependencies */);
echo $view->output();
Run Code Online (Sandbox Code Playgroud)
这样做,控制器可以更改模型的状态并确保它保存在存储系统中。视图读取并显示相同的模型实例及其状态。控制器通过模型将显示逻辑信息传递给视图。问题是,这些信息不属于模型应该专有的业务逻辑。他们只是显示逻辑参与者。
为了避免将显示逻辑责任交给模型,我们必须在图中引入一个新组件:视图模型。控制器和视图将共享一个视图模型实例,而不是共享模型对象。只有这个将接收模型作为依赖项。一个实现:
$model = new UserModel;
$viewModel = new UserViewModel($model, /* Other view-model dependencies */);
$controller = new UserController($viewModel /* Other controller dependencies */);
$view = new UserView($viewModel, /* Other view dependencies */);
$controller->{action}(/* Action dependencies */);
echo $view->output();
Run Code Online (Sandbox Code Playgroud)
工作流程可以这样描述:
output方法中,视图从视图模型中读取值并请求模型根据它们查询存储。视图模型不属于域模型,所有域对象都驻留在域模型中,真正的业务逻辑发生在域模型中。它也不属于操作域对象、存储库和数据映射器的服务层。它属于应用程序模型,例如应用程序逻辑发生的地方。视图模型获得从控制器获取显示逻辑状态并将其传递给控制器的唯一责任。
可以看出,只有视图模型“接触”了模型。控制器和视图不仅彼此完全解耦,而且与模型完全解耦。这种方法最重要的方面是,所涉及的所有组件中的每一个都只获得它应该获得的责任。
利用这种组件分离和依赖注入容器,控制器依赖过多的问题就消失了。并且可以以一种非常灵活的方式应用我的问题中提供的所有选项的组合。无需记住其中一个组件(模型、视图或控制器)承担了太多责任。
| 归档时间: |
|
| 查看次数: |
1114 次 |
| 最近记录: |