3

我正在构建一个根据 MVC 原则运行并利用依赖注入的 PHP 框架。我想我的前控制器部分已关闭;有一个工作路由器实例化一个控制器实例并根据请求的 URI 调用适当的操作。

接下来是依赖注入。我想实现一个使用反射解决依赖关系的容器。在这样做时,我认为我的控制器遇到了问题。

有许多我称之为“系统依赖项”的东西需要可用于派生的控制器类。我实际上还没有创建所有这些依赖项,但控制器可以访问诸如 InputProvider 之类的服务(封装 get/post 参数或命令行参数),也许还有输出依赖项,这似乎是明智的。理想情况下,我会使用框架的 Container 将这些依赖项注入到控制器的构造函数中——但这是我遇到问题的地方。

如果我对控制器中的系统依赖项使用构造函数注入,那么如果派生控制器实现自己的构造函数,我将强制派生控制器管理基本控制器的依赖项。这似乎不是最用户友好的。另一种选择是对系统依赖项使用 setter 注入,但如果派生控制器在其构造函数中需要这些系统依赖项,则它们将无法访问这些系统依赖项。

我看到的唯一能提供两全其美的解决方案是让我的控制器成为单例。他们有一个私有构造函数,所以我可以安全地使用 setter 注入,而不必担心派生类的构造函数。相反,会有一个可覆盖的 initialize() 方法(假设我让方法注入以某种方式工作),它基本上履行了构造函数的角色(例如,派生控制器的初始化程序)。这样,构造函数注入被 initialize() 方法中的方法注入所取代,其中所有系统依赖项都可用,而无需派生控制器来管理它们。

但是,快速的谷歌搜索似乎一致认为单例控制器是不好的做法。我非常不确定如何进行。我可能对此进行了过度设计,但除了希望我的应用程序能够面向未来和可维护之外,我还认为它是应用最佳实践的一个小练习,所以我想“正确”地做事。
我认为这种情况下的最佳实践是将管理所需系统依赖项的责任传递给派生控制器。如果派生控制器确实需要它,则可能只应实例化依赖项。如果派生控制器可能永远不会使用它,为什么还要在基本控制器中注入 InputProvider?但与此同时,我不断回归用户友好性,$this->input成员可用,例如在像 CodeIgniter 这样的框架中。

我非常感谢对我的困境所做的任何和所有贡献。我也为文字墙道歉,但我想不出任何代码示例可以让解释工作变得更容易,因为现在对我来说这一切都太抽象了!

真诚的,
一个严重撕裂的个体

4

1 回答 1

1

几种解决方案是可能的:

  • 禁止控制器使用__construct():将其设为私有public final,并使控制器覆盖类似的东西init()并从构造函数中调用它。然后构造函数将注入所有依赖项(反射?其他东西?),以便它们都准备好在init().

  • 您可以使用现有的 DI 库,如PHP-DI(免责声明:我致力于此),它允许您定义依赖项,但在构造函数中可以使用它们(神奇的是,是的)。

像这样的东西:

<?php
use DI\Annotations\Inject;

class FooController {
    /**
     * @Inject
     * @var Bar
     */
    private $bar;

    public function __construct() {
        // The dependency is already injected
        $this->bar->sayHello();
    }

    public function setBar(Bar $bar) {
        return $this->bar = $bar;
    }
}

例如,这就是我使用 Zend Framework 1 的方式。我不能使用构造函数,所以我注入属性。这是ZF1 集成项目

于 2013-02-01T11:28:46.213 回答