问题描述
当我是laravel的初学者时,谁能用简单的方式解释依赖项注入和IoC容器.谢谢
Can anyone explain dependency injection and IoC container in simple and easy words as i am beginner to laravel. thanks
推荐答案
答案变得比我希望的更长.我提供了一些背景信息.不过,如果您正在寻找短期解释,请阅读IoC容器的第一段和大胆的段落.
依赖注入是一种设计模式,它按照名称说明进行操作.它将对象注入到其他对象的构造函数或方法中,使一个对象依赖于一个或多个其他对象.
Dependency Injection is a design pattern, that does what the name states. It injects objects into the constructor or methods of other objects, so that one object depends on one or more other objects.
<?php
class DatabaseWriter {
protected $db;
public function __construct(DatabaseAdapter $db)
{
$this->db = $db;
}
public function write()
{
$this->db->query('...');
}
}
您可以看到我们要求类构造函数传递一个DatabaseAdapter
实例.由于我们是在构造函数中执行此操作的,因此没有该类的对象将无法实例化:我们正在注入依赖项.现在,我们知道DatabaseAdapter
始终存在于类中,我们可以轻松地依赖它.
You can see that we require the classes constructor a DatabaseAdapter
instance to be passed. Since we are doing that in the constructor, an object of that class cannot be instantiated without it: We are injecting a dependency. Now, that we know that the DatabaseAdapter
is always existent within the class we can easily rely on it.
write()
方法仅在适配器上调用方法,因为我们肯定知道它的存在,因为我们使用了DI.
The write()
method just calls methods on the adapter, since we definitely know it exists because we made use of DI.
使用DI而不是滥用静态类,上帝对象和诸如此类的其他东西的巨大优势是,您可以轻松地跟踪依赖项的来源.
The enormous advantage of using DI instead of misusing static classes, god objects, and other stuff like that is, that you can easily track down where the dependencies come from.
另一个巨大的优势是,您可以轻松换出依赖项.如果要使用依赖项的另一种实现,只需将其传递给构造函数即可.您不再需要寻找硬编码实例即可交换它们.
The other huge advantage is, that you can easily swap out dependencies. If you want to use another implementation of your dependency just pass it to the constructor. You don't need to hunt down hardcoded instances anymore in order to be able to swap them.
撇开使用依赖注入的事实,您可以轻松地对类进行单元测试,因为您可以模拟依赖,而使用硬编码的依赖几乎是不可能的.
Leaving aside the fact that by using Dependency Injection you will easily be able to Unit-Test your classes, since you can just mock the dependencies which is hardly possible with hardcoded dependencies.
上面说明的依赖注入的类型称为构造函数注入.这仅意味着将依赖项作为参数传递给类构造函数.然后将依赖项存储为属性,从而使其在该类的所有方法中可用.这里的最大优点是,如果不传递依赖项,则该类的对象将不存在.
The type of Dependency Injection explained above is called Constructor Injection. That simply means that dependencies are passed as arguments to the classes constructor. The dependencies are then stored as properties and thus made available in all methods of the class. The big advantage here is, that a object of the class cannot exist without passing the dependencies.
此类型利用专用方法来注入依赖项.而不是使用构造函数.使用Setter Injection的优点是,您可以在对象创建后将其添加到对象..通常用于可选依赖项. Setter注入还可以使您的构造函数更整洁,并且仅在需要它们的方法中才具有依赖项.
This type makes use of dedicated methods to inject dependencies. Instead of using the constructor. The advantage of using Setter Injection is, that you can add dependencies to an object after it has been created. It's commonly used for optional dependencies. Setter Injection is also great to declutter your constructor and have your dependencies only in methods where you need them.
<?php
class RegisterUserService {
protected $logger;
public function setLogger( Logger $logger )
{
$this->logger = $logger;
}
public function registerUser()
{
// Do stuff to register the user
if($this->logger)
$this->logger->log("User has been registered");
}
}
$service = new RegisterUserService;
$service->registerUser(); // Nothing is Logged
$service->setLogger(new ConcreteLogger);
$service->registerUser(); // Now we log
可以实例化该对象而没有任何依赖关系.有一种方法可以注入依赖项(setLogger()
),可以选择调用该依赖项.现在由方法实现决定是否使用依赖项(如果未设置).
The object can be instantiated without any dependencies. There is a method to inject the dependency (setLogger()
) which can be called optionally. Now it's up to the methods implementation to either make use of the dependency or not (if it's not set).
值得指出的是,对Setter Injection应当保持谨慎.在尚未注入的依赖项上调用方法或访问属性将导致令人讨厌的Fatal error: Call to a member function XXX() on a non-object
.因此,每次访问依赖项时,都必须首先对其进行空检查.一种更干净的方法是使用空对象模式,然后将依赖项移至构造函数中(如可选参数,如果不传递任何内容,则在类内部创建null对象)
It's worth pointing out to be cautious with Setter Injection. Calling methods or accessing attributes on a dependency that has not yet been injected will result in a nasty Fatal error: Call to a member function XXX() on a non-object
. Therefore everytime the dependency is accessed it has to be null-checked first. A much cleaner way to go about this would be to use the Null Object Pattern and to move the dependency into the constructor (as optional argument, if nothing is passed the null object is created inside the class)
基本上,接口注入的思想是,在接口中定义了注入依赖项的方法.需要依赖的类必须实现接口.因此,确保了可以将所需的依赖项正确地注入到依赖项对象中.这是前面解释的Setter注入的更严格形式.
The idea of interface injection is basically, that the method(s) to inject a dependency is defined in an interface. The class that is going to need the dependency must implement the interface. Hereby it is ensured that the needed dependency can be properly injected into the dependent object. It's a stricter form of the previously explained Setter Injection.
<?php
interface Database {
public function query();
}
interface InjectDatabaseAccess {
// The user of this interface MUST provide
// a concrete of Database through this method
public function injectDatabase( Database $db );
}
class MySQL implements Database {
public function query($args)
{
// Execute Query
}
}
class DbDoer implements InjectDatabaseAccess {
protected $db;
public function injectDatabase( Database $db )
{
$this->db = $db;
}
public function doSomethingInDb($args)
{
$this->db->query();
}
}
$user = new DbDoer();
$user->injectDatabase( new MySQL );
$user->doSomethingInDb($stuff);
接口注入的意义尚有争议.我个人从未使用过它.我更喜欢构造函数注入.这样一来,我就可以完成完全相同的任务,而无需在喷射器侧附加接口.
The meaningfulness of Interface Injection is open to dispute. Personally I have never used it. I prefer Constructor Injection over it. That allows me to accomplish the exact same task without the need of additional interfaces on the injectors side.
我们不仅可以依赖于具体的实例,还可以依赖于抽象.
Instead of depending on a concrete instance we can also depend on abstractions.
<?php
class DatabaseWriter {
protected $db;
public function __construct(DatabaseAdapterInterface $db)
{
$this->db = $db;
}
public function write()
{
$this->db->query('...');
}
}
现在,我们反转了控件.现在,我们不再依赖具体实例,而是可以注入使用类型提示接口的任何实例. .该接口会确保后面的具体实例实现我们将要使用的所有方法,以便我们仍可以在依赖类中依赖它们.
Now we inverted the control. Instead of relying on a concrete instance, we can now inject any instance that consumes the type hinted interface. The interface takes care that the later concrete instance implements all the methods that we are going to use, so that we can still rely on them in the dependent classes.
请确保您得到了它,因为它是IoC容器的核心概念.
Make sure you get that one, because it is a core concept of the IoC-Container.
在最短的时间内,我想到的就是这样描述IoC容器:
In the shortest terms I can think of I would describe the IoC container like that:
如果我们以上面的示例为例,请想象DatabaseAdapter
本身具有它自己的依赖性.
If we take our example above, imagine that the DatabaseAdapter
itself has it's own dependencies.
class ConcreteDatabaseAdapter implements DatabaseAdapterInterface{
protected $driver;
public function __construct(DatabaseDriverInterface $driver)
{
$this->driver = $driver;
}
}
因此,为了能够使用DatabaseAdapter
,您需要将DatabaseDriverInterface
抽象的实例作为依赖项进行传递.
So in order to be able to use the DatabaseAdapter
you would need to pass in a instance of the DatabaseDriverInterface
abstraction as a dependency.
但是您的DatabaseWriter
类不知道,也不应该. DatabaseWriter
不应在意DatabaseAdapter
的工作方式,而应仅在意DatabaseAdapter
的传递而不是在如何创建它.这就是IoC容器派上用场的地方.
But your DatabaseWriter
class does not know about it, nor should it. The DatabaseWriter
should not care about how the DatabaseAdapter
works, it should only care about that a DatabaseAdapter
is passed and not how it needs to be created. That's where the IoC-Container comes in handy.
App::bind('DatabaseWriter', function(){
return new DatabaseWriter(
new ConcreteDatabaseAdapter(new ConcreteDatabaseDriver)
);
});
正如我已经说过的那样,DatabaseWriter
本身对它的依赖关系一无所知.但是IoC容器了解所有这些信息,也知道在哪里可以找到它们.因此,最终当要实例化DatabaseWriter
类时,首先会被问到如何实例化该IoC容器.这就是IoC容器的作用.
As I already said, the DatabaseWriter
itself does not know anything about the dependencies of it's dependencies. But the IoC-Container knows all about them and also knows where to find them. So eventually when the DatabaseWriter
class is going to be instantiated, the IoC-Container first is asked how it needs to be instantiated. That's what the IoC-Container does.
简单地说(如果您喜欢设计模式).它是依赖注入容器(我在上面已经解释过)和 服务定位器 .
Simply spoken (if you are into design patterns). It's a bit of a combination of a Dependency Injection Container (which I already explained above) and a Service Locator.
这篇关于简单来说,Laravel IoC容器是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!