了解IoC容器和依赖注入 [英] Understanding IoC Containers and Dependency Injection

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

问题描述

快速转发



我正在写这个,旨在更好地了解依赖注入和IoC容器,所以以后我可以纠正错误,并使用它来帮助我的几个朋友教他们。



截至目前,我已经尝试阅读各种框架的文档(laravel,fuel,codeigniter,symfony)和我发现框架太多的不同方面我觉得很舒服,我决定尝试只是单独学习每个主要的部分我自己在尝试在框架本身中使用它们。



我花了几个小时搜索各种含义,查看stackoverflow响应,并阅读各种文章,试图了解什么是IoC以及如何使用它来正确管理依赖关系,我相信我了解它在概念上是什么,但是我仍然认为如何实现它rectly。我认为任何阅读这个帮助我的人最好的方法是给出我目前对IoC容器和依赖注入的理解,然后让比我自己更了解的人指出我的理解不足之处。 / strong>



我的理解:




  • 依赖关系是当ClassA的实例需要一个ClassB的实例来实例化一个新的ClassA实例时。

  • 依赖注入是当ClassA通过ClassB的一个实例时,通过参数ClassA的构造函数或通过set〜DependencyNameHere〜(〜DependencyNameHere〜$ param)函数。 (这是我不完全确定的区域之一)

  • IoC容器是一个单例类(在任何给定时间只能有1个实例实例化),其中可以注册该项目的实例化对象的具体方式。 这里是一个链接,我将要描述的一个例子,以及类的定义我一直在使用的IoC容器



所以在这一点上,我开始尝试使用IoC容器来获得更多复杂的场景到目前为止,为了使用IoC容器,我受限于几乎所有我想要创建的类的一个关系,它具有在IoC容器中定义的依赖关系。如果我想创建一个继承类的类,但是只有当父类以特定方式创建,才能在IoC容器中注册,该怎么办?



所以例如:我想创建一个mysqli的子类,但是我想在IoC容器中注册这个类,只能用一个在我以前在IoC容器中注册的方式。我无法想到这样做,而不需要重复代码(因为这是一个学习项目,我试图把它保持为纯)。 以下是我正在尝试描述的更多示例。



所以这里有一些我的问题:




  • 试图做到以上可能没有打破一些OOP的原则?我知道在c + +我可以使用动态内存和一个复制构造函数来完成它,但我还没有能够在php中找到那种功能。 (我会承认,除了__construct之外,我使用任何其他魔法方法的经验很少,但是从阅读和__clone如果我理解正确,我无法在构造函数中使用它来将子类实例化为一个父类的实例)。

  • 我的所有依赖类定义应该与IoC有关? (我的IoC.php是否只有一堆require_once(dependencyClassDefinition.php)在顶部?我的反应是,有一个更好的方法,但我还没有想出一个)

  • 我应该在什么文件中注册我的对象?目前在类定义后,IoC.php文件中对IoC :: register()的所有调用。

  • 在注册一个类之前,我需要在IoC中注册一个依赖项需要依赖?由于我没有调用匿名函数,直到我实际实例化了在IoC中注册的对象,所以我猜不出来,但它仍然是一个问题。

  • 还有其他的东西忽视我应该做的还是使用?我一次尝试一次,但是我也不想知道我的代码是可重用的,最重要的是,对于我的项目,谁也不了解,可以阅读并理解它。 li>


我知道这是非常长的,只是想提前感谢任何花时间阅读它的人,甚至更多所以任何人都有分享他们的知识。

解决方案

简单地说(因为它不仅仅限于OOP世界的问题),一个依赖性是组件A需要(依赖于)组件B来执行应该做的事情的情况。在这种情况下,该词还用于描述依赖组件。要将其放在OOP / PHP术语中,请考虑以下示例:强制性汽车类比:

  class Car {

public function start(){
$ engine = new Engine();
$ engine-> vroom();
}

}

汽车 取决于引擎引擎 Car 依赖。这段代码很糟糕,因为:




  • 依赖是隐式的;您不知道它在那里,直到您检查 Car 的代码

  • 类紧密耦合;您不能将 Engine 替换为 MockEngine 进行测试或 TurboEngine 扩展了原来的一个,而不修改 Car

  • 看起来有点傻,一辆车能够建立一个引擎本身,不是吗?



依赖注入是一种解决所有这些问题的方法通过将 Car 需要引擎明确地明确提供一个:

  class Car {

protected $ engine;

public function __construct(Engine $ engine){
$ this-> engine = $ engine;
}

public function start(){
$ this-> engine-> vroom();
}

}

$ engine = new SuperDuperTurboEnginePlus(); // Engine的一个子类
$ car = new Car($ engine);

以上是构造函数注入的示例,其中依赖关系依赖对象)通过类构造函数提供给依赖(消费者)。另一种方法是在 Car 类中暴露一个 setEngine 方法,并使用它来注入一个引擎。这被称为 setter注入,主要用于在运行时被替换的依赖关系。



任何非平凡的项目由一堆相互依赖的组件组成,并且很容易失去跟踪哪里被注入的地方很快。一个依赖注入容器是一个知道如何实例化和配置其他对象的对象,知道它们与项目中其他对象的关系,并为您执行依赖注入。这使您可以集中管理所有项目(内部)依赖关系,更重要的是,可以更改/模拟一个或多个项目,而无需在代码中编辑一堆位置。



让我们来看一下这个汽车的比喻,看看什么OP试图实现一个例子。假设根据 mysqli 对象,我们有一个数据库对象。假设我们想使用一个真正的原始依赖关系indection容器类 DIC ,它暴露了两种方法:注册($ name,$ callback)注册一个在给定名称下创建一个对象的方式,并且 resolve($ name)可以从该名称获取对象。我们的容器设置将如下所示:

  $ dic = new DIC(); 
$ dic-> register('mysqli',function(){
return new mysqli('somehost','username','password');
});
$ dic-> register('database',function()use($ dic){
return new Database($ dic-> resolve('mysqli'));
} );请注意,我们正在告诉我们的容器来抓取一个 mysqli的实例
code> 自己组合一个数据库的实例。然后,为了得到一个数据库实例,其依赖自动注入,我们将简单地:

  $ database = $ dic-> resolve('database'); 

这是它的要点。一个更复杂但还比较简单易懂的PHP DI / IoC容器是 Pimple






关于OP的代码和问题:




  • 不要为您的容器使用静态类或单例(或任何其他事情); 他们都是邪恶的。查看Pimple代替

  • 决定是否要您的 mysqliWrapper class extend c code code code code code code code code code code code $ c mysqliWrapper 你正在交换另一个依赖关系。您的物品不应该知道或使用容器;否则它不再是DIC,它是服务定位器(反)模式。

  • 注册之前,您不需要 require 一个类文件在容器中,因为您不知道是否要使用该类的对象。将所有容器设置在一个地方。如果您不使用自动装载机,您可以在向容器注册的匿名功能中要求






其他资源




Quick Forward:

I'm writing this with the intention of getting a better understanding of dependency injection and IoC containers, but also so that afterwards I can correct the mistakes in it and use it to help teach a few friends of mine about them as well.

As of now I've tried reading through the documentation for various frameworks(laravel, fuel, codeigniter, symfony) and I found that there were too many different aspects of the frameworks that I needed to feel comfortable using it that I decided to try to just learn each of the major pieces individually on my own before trying to use them in the frameworks themselves.

I've spent hours googling various meanings, looking over stackoverflow responses, and reading various articles trying to understand what an IoC and how to use it to correctly manage dependencies, and I believe I understand what it is in concept, but I am still grey on how to implement it correctly. I think the best way for anybody reading this to help me is to give what my current understanding of IoC containers and dependency injection is, and then let people who have a better understanding than myself point out where my understanding falls short.

My understanding:

  • A dependency is when an instance of ClassA requires an instance of ClassB to instantiate a new instance of ClassA.
  • A dependency injection is when ClassA is passed an instance of ClassB, either through a parameter in ClassA's constructor or through a set~DependencyNameHere~(~DependencyNameHere~ $param) function. (This is one of the areas I'm not completely certain on).
  • An IoC container is a singleton Class(can only have 1 instance instantiated at any given time) where the specific way of instantiating objects of those class for this project can be registered. Here's a link to an example of what I'm trying to describe along with the class definition for the IoC container I've been using

So at this point is where I start trying use the IoC container for more complicated scenarios. As of now it seems in order to use the IoC container, I am limited to a has-a relationship for pretty much any class I want to create that has dependencies it wants to define in the IoC container. What if I want to create a class that inherits a class, but only if the parent class has been created in a specific way it was registered in the IoC container.

So for example: I want to create a child class of mysqli, but I want to register this class in the IoC container to only instantiate with the parent class constructed in a way I've previously registered in the IoC container. I cannot think of a way to do this without duplicating code (and since this is a learning project I'm trying to keep it as 'pure' as possible). Here are some more examples of what I am trying to describe.

So here are some of my questions:

  • Is what I'm trying to do above possible without breaking some principle of OOP? I know in c++ I could use dynamic memory and a copy constructor to accomplish it, but I haven't been able to find that sort of functionality in php. (I will admit that I have very little experience using any of the other magic methods besides __construct, but from reading and __clone if I understood correctly, I couldn't use in the constructor it to make the child class being instantiated a clone of an instance of the parent class).
  • Where should all my dependency class definitions go in relation to the IoC? (Should my IoC.php just have a bunch of require_once('dependencyClassDefinition.php') at the top? My gut reaction is that there is a better way, but I haven't come up with one yet)
  • What file should I be registering my objects in? Currently doing all the calls to IoC::register() in the IoC.php file after the class definition.
  • Do I need to register a dependency in the IoC before I register a class that needs that dependency? Since I'm not invoking the anonymous function until I actually instantiate an object registered in the IoC, I'm guessing not, but its still a concern.
  • Is there anything else I'm overlooking that I should be doing or using? I'm trying to take it one step at a time, but I also don't want to know that my code will be reusable and, most importantly, that somebody who knows nothing about my project can read it and understand it.

I know this is extremely long, and just wanted to give a thanks in advance to anybody who took the time to read it, and even more so to anybody shares their knowledge.

解决方案

Put simply (because it's not a problem limited to OOP world only), a dependency is a situation where component A needs (depends on) component B to do the stuff it's supposed to do. The word is also used to describe the depended-on component in this scenario. To put this in OOP/PHP terms, consider the following example with the obligatory car analogy:

class Car {

    public function start() {
        $engine = new Engine();
        $engine->vroom();
    }

}

Car depends on Engine. Engine is Car's dependency. This piece of code is pretty bad though, because:

  • the dependency is implicit; you don't know it's there until you inspect the Car's code
  • the classes are tightly coupled; you can't substitute the Engine with MockEngine for testing purposes or TurboEngine that extends the original one without modifying the Car.
  • It looks kind of silly for a car to be able to build an engine for itself, doesn't it?

Dependency injection is a way of solving all these problems by making the fact that Car needs Engine explicit and explicitly providing it with one:

class Car {

    protected $engine;

    public function __construct(Engine $engine) {
        $this->engine = $engine;
    }

    public function start() {
        $this->engine->vroom();
    }

}

$engine = new SuperDuperTurboEnginePlus(); // a subclass of Engine
$car = new Car($engine);

The above is an example of constructor injection, in which the dependency (the depended-on object) is provided to the dependent (consumer) through the class constructor. Another way would be exposing a setEngine method in the Car class and using it to inject an instance of Engine. This is known as setter injection and is useful mostly for dependencies that are supposed to be swapped at run-time.

Any non-trivial project consists of a bunch of interdependent components and it gets easy to lose track on what gets injected where pretty quickly. A dependency injection container is an object that knows how to instantiate and configure other objects, knows what their relationship with other objects in the project are and does the dependency injection for you. This lets you centralize the management of all your project's (inter)dependencies and, more importantly, makes it possible to change/mock one or more of them without having to edit a bunch of places in your code.

Let's ditch the car analogy and look at what OP's trying to achieve as an example. Let's say we have a Database object depending on mysqli object. Let's say we want to use a really primitive dependency indection container class DIC that exposes two methods: register($name, $callback) to register a way of creating an object under the given name and resolve($name) to get the object from that name. Our container setup would look something like this:

$dic = new DIC();
$dic->register('mysqli', function() {
    return new mysqli('somehost','username','password');
});
$dic->register('database', function() use($dic) {
    return new Database($dic->resolve('mysqli'));
});

Notice we're telling our container to grab an instance of mysqli from itself to assemble an instance of Database. Then to get a Database instance with its dependency automatically injected, we would simply:

$database = $dic->resolve('database');

That's the gist of it. A somewhat more sophisticated but still relatively simple and easy to grasp PHP DI/IoC container is Pimple. Check its documentation for more examples.


Regarding OP's code and questions:

  • Don't use static class or a singleton for your container (or for anything else for that matter); they're both evil. Check out Pimple instead.
  • Decide whether you want your mysqliWrapper class extend mysql or depend on it.
  • By calling IoC from within mysqliWrapper you're swapping one dependency for another. Your objects shouldn't be aware of or use the container; otherwise it's not DIC anymore it's Service Locator (anti)pattern.
  • You don't need to require a class file before registering it in the container since you don't know if you're going to use an object of that class at all. Do all your container setup in one place. If you don't use an autoloader, you can require inside the anonymous function you register with the container.

Additional resources:

这篇关于了解IoC容器和依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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