访问DI容器

时间:2010-03-24 01:31:02

标签: php dependency-injection

我正在开始一个新项目并建立基础来开展工作。有几个问题已经上升,我可能会在这里问一些问题,希望我会找到一些答案。

第一步是处理对象的依赖关系。我已经决定使用依赖注入设计模式,我有点新的,为应用程序处理所有这些。

实际编码时,我遇到了一个问题。如果一个类有多个依赖项,并且你想通过构造函数传递多个依赖项(以便在实例化对象后它们不能被更改)。

如何在不传递依赖项数组的情况下使用call_user_func_array(),eval()或Reflection?这就是我要找的:

<?php

class DI
{
    public function getClass($classname)
    {
        if(!$this->pool[$classname]) {
            # Load dependencies
            $deps = $this->loadDependencies($classname);

            # Here is where the magic should happen
            $instance = new $classname($dep1, $dep2, $dep3);

            # Add to pool
            $this->pool[$classname] = $instance;

            return $instance;
        } else {
                return $this->pool[$classname];
        }
    }
}

同样,我想避免使用最昂贵的方法来调用该类。还有其他建议吗?

另外,如何在类中访问DI类,例如,在需要访问不同模型的控制器中?我应该静态地调用它还是将它传递给需要它的每个类?我不认为最后的想法是可行的。

谢谢大家。

3 个答案:

答案 0 :(得分:22)

[在开始之前,让我说我主要是一名Java程序员 - 只有一点点PHP知识。但我会简单地尝试在没有语言细节的情况下获得最重要的概念。]

依赖注入基于代码的两部分:

  1. 构建
  2. 执行
  3. 在最极端的形状中,在执行部分中找不到new运算符。所有这些都被移到了构造部分。 (在实践中,这将会降低。)

    所有施工都在 - 施工部分。它创建了自下而上执行所需对象的图形。所以我们假设,它应该构建A:

    • A取决于B和
    • B取决于C.

    然后

      首先构建
    • C。
    • 然后用C作为参数构造B.
    • 然后用B作为参数构造A。

    所以C不必作为构造函数参数传递给A.这个小例子没有足够强烈地说明,这减少了多少必须传递给极少数的对象的数量。 / p>

    不应将依赖注入器本身传递到执行部分。当他们第一次接触DI时,这是每个人(包括我自己)试图犯下的基本错误之一。问题是,这将完全模糊构造和执行之间的界限。另一种说法是,它会违反Law of Demeter。或者在模式中说:它最终会将依赖注入模式“降级”为服务定位器模式。这是有争议的,如果这确实是一种退化,但无论如何将滥用注入者作为服务定位器滥用通常不是一个好主意

    因此,无论何时需要为构造对象之一提供在执行期间生成其他对象的能力,而不是传递依赖注入器,您只能传递简单的Providers(Java DI框架使用的术语Guice )。这些是相当简单的类,只能创建某种对象。他们与工厂有相似之处。

    首先尝试将所需的依赖项直接传递给构造函数。

    所以,总结一下:

    • 自下而上构建对象。
    • 仅传递创建对象所需的少量依赖项。
    • 完成后,开始执行。
    • 在执行期间,您仍然可以使用提供程序获取新创建的对象。

    但是不要太过分:在没有提供者的情况下仍然可以创建简单的对象: - )

    现在,您所需要做的就是将这些内容翻译成高质量的代码。也许其他人可以通过一些PHP示例来帮助你。

    附录:关于提供商的更多信息

    如上所述,“Provider”(专业工厂)这一概念对Java DI框架Guice来说有点特殊。此框架可以自动为任何类型的对象创建提供程序。但是,这个概念通常对DI有用。唯一的区别是,没有Guice或类似框架的帮助,你必须自己编写提供商 - 但这很容易:

    让我们说,B取决于C。

    • 如果B只需要一个C的固定实例,那么你不需要一个Provider - 你只需用构造函数参数C构造B.
    • 如果B需要在执行期间创建更多的C实例,那么只需用CProvider方法编写一个名为get()的类,即可创建C的新实例。然后传递{的实例{1}}进入B的构造函数,并将Provider存储在B的实例字段中。现在B可以在需要新的C实例时调用CProvider

    提供者是构造代码的一部分,因此您可以使用cProvider.get()!另一方面,它们不是执行代码的一部分,所以你不应该有任何执行逻辑。

    new C(...)当然可以传递给多个构造函数。您还可以编写多个版本CProviderCProvider1,... - 其中每个版本都可以构造具有不同属性的不同版本的C对象。或者您使用不同的参数简单地多次实例化CProvider2

答案 1 :(得分:2)

您应该考虑使用IOC容器来管理您的依赖项。一个好的IOC容器应该为你在依赖的构造函数之间传递依赖关系。

现有question询问PHP的IOC容器选项。

答案 2 :(得分:2)

看起来您正在尝试滚动自己的依赖注入容器。为什么不使用已存在的广告,例如SymfonyCraftySphicy