访问DI容器 [英] Accessing the DI container

查看:67
本文介绍了访问DI容器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开始一个新项目,并建立工作基础.出现了一些问题,我可能会在这里问很多,希望我能找到一些答案.

第一步是处理对象的依赖关系.我已经决定采用依赖注入设计模式(这是我的新手)来为应用程序处理所有这些操作.

在实际编码时,我遇到了一个问题.如果一个类具有多个依赖关系,并且您想通过构造函数传递多个依赖关系(这样,在实例化对象后就不能更改它们).

如何使用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类,例如,在需要访问不同模型的控制器中?我应该静态地调用它还是将其传递给需要它的每个类?我认为最后一个想法不可行.

谢谢大家.

解决方案

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

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

  1. 建筑
  2. 执行

在其最极端的形状中,在执行部分中找不到new运算符.所有这些都移入了建筑"部分. (实际上,这将被减弱.)

所有构造都发生-在构造部分.它会创建自下而上执行所需的对象图.因此,让我们假设,它应该构造A:

  • A取决于B,并且
  • B取决于C.

然后

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

因此,不必将C作为构造函数参数传递给A.这个小例子并不能说明太多,这多少减少了必须传递的对象数量./p>

不应该将依赖项注入器本身传递到执行部分.这是每个人(包括我自己)初次接触DI时都会犯的基本错误之一.问题在于,这将完全模糊构造与执行之间的界线.换句话说,这将违反《得墨meter耳法律》 .或用模式来说:最终将依赖注入"模式降级"到服务定位器模式.如果这确实是降级的话,这是有争议的,但是在任何情况下,将依赖注入器用作服务定位器通常不是一个好主意.

因此,每当需要在执行过程中向其中一个构造的对象提供生成其他对象的能力时,只需传递简单的提供程序(Java DI框架

比方说,B取决于C.

  • 如果B仅需要C的一个固定实例,那么您就不需要提供者-您只需使用构造函数参数C构造B.
  • 如果B在执行期间需要创建C的更多实例,则只需使用get()方法编写一个名为CProvider的类,该类可以创建C的新实例.然后将CProvider的实例传递给C B的构造函数,并将Provider存储在B的实例字段中.现在B在需要新的C实例时可以调用cProvider.get().

提供程序是构造代码的一部分,因此您可以使用new C(...)!另一方面,它们不属于执行代码,因此您不应在其中执行任何逻辑.

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

I'm starting a new project and setting up the base to work on. A few questions have risen and I'll probably be asking quite a few in here, hopefully I'll find some answers.

First step is to handle dependencies for objects. I've decided to go with the dependency injection design pattern, to which I'm somewhat new, to handle all of this for the application.

When actually coding it I came across a problem. If a class has multiple dependencies and you want to pass on multiple dependencies via the constructor (so that they cannot be changed after you instantiate the object).

How do you do it without passing an array of dependencies, using call_user_func_array(), eval() or Reflection? This is what i'm looking for:

<?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];
        }
    }
}

Again, I would like to avoid the most costly methods to call the class. Any other suggestions?

Also, how do I access the DI class inside classes, for example, in controllers that need to access different models? Should I call it statically or pass it along each class that would require it? I don't think the last idea is feasible.

Thanks everyone.

解决方案

[Before I start, let me say that I'm mostly a Java programmer - with only a little bit of PHP knowledge. But I'll simply try to get the most important concepts across without language specifics.]

Dependency Injection is based on two parts of code:

  1. Construction
  2. Execution

In its most extreme shape, there are no new operators to be found in the Execution part. All of them are moved into the Construction part. (In practice, this will be toned down.)

All of the construction happens - in the Construction part. It creates the graph of objects needed for Execution bottom up. So let's assume, it should construct A:

  • A depends on B, and
  • B depends on C.

Then

  • C is constructed first.
  • Then B is constructed with C as a parameter.
  • Then A is constructed with B as a parameter.

So C doesn't have to be passed as a constructor parameter to A. This small example doesn't illustrate strongly enough, how much this reduces the amount of objects that have to be passed around to quite a small number.

The Dependency Injector itself should not be passed into the Execution part. This is one of the basic mistakes everyone (including myself) tries to make, when they first come in contact with DI. The problem is, that this would completely blur the lines between Construction and Execution. Another way to say it is, that it would violate the Law of Demeter. Or in pattern speak: It would eventually "degrade" the Dependency Injection pattern to the Service Locator pattern. It's debatable, if this is really a degradation, but in any case it's usually not a good idea to misuse the Dependency Injector as a Service Locator.

So whenever you need to give one of your constructed objects the capability to produce other objects during execution, instead of passing the Dependency Injector, you would only pass simple Providers (a term used by the Java DI framework Guice). These are rather simple classes that can only create a certain kind of object. They have similarities with a factory.

First try to pass the required dependencies directly to the constructor.

So, to sum it up:

  • Build objects bottom-up.
  • Only pass as few dependencies as required to create an object.
  • Once your done, start executing.
  • During execution, you can still fetch newly created objects by using Providers.

But don't take it too far: Simple objects can still be created without a Provider :-)

And now, all you'll have to do is to translate this stuff into quality code. Maybe others can help you out with a few PHP examples.

Addendum: A little bit more about Providers

As noted above, the notion "Provider" (a specialized factory) is a bit specific to the Java DI framework Guice. This framework can automatically create a Provider for any type of object. However, the concept is generally useful for DI. The only difference is, that without the help of Guice or a similar framework, you'll have to write the Providers yourself - but that's quite easy:

Let's say, B depends on C.

  • If B just needs one fixed instance of C, then you don't need a Provider - you can simply construct B with the constructor argument C.
  • If B needs to create more instances of C during execution, then just write a class called CProvider with a get() method, that can create a new instance of C. Then pass an instance of CProvider into the constructor of B, and store the Provider in an instance field of B. Now B can call cProvider.get() when it needs a new instance of C.

Providers are part of the Construction code, so you're allowed to use new C(...)! On the other hand, they're not part of the Execution code, so you shouldn't have any execution logic there.

CProvider can be passed into multiple constructors of course. You can also write multiple versions CProvider1, CProvider2, ... - where each can construct different versions of C objects with different properties. Or you simple instantiate CProvider multiple times with different arguments.

这篇关于访问DI容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆