简单来说什么是 Laravel IoC Container?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24484782/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is Laravel IoC Container in simple words?
提问by user3254411
Can anyone explain dependency injection and IoC container in simple and easy words as i am beginner to laravel. thanks
任何人都可以用简单易懂的语言解释依赖注入和 IoC 容器,因为我是 laravel 的初学者。谢谢
回答by thpl
The answer became longer than I wanted it to be in the first place. I included a bit of background information. Still if you're looking for short term explanations, read the first paragraph of the IoC-Container and the bold passages.
答案变得比我最初希望的要长。我包括了一些背景信息。如果您正在寻找短期解释,请阅读 IoC-Container 的第一段和粗体段落。
Dependency Injection
依赖注入
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('...');
}
}
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.
您可以看到我们要求类构造函数DatabaseAdapter
传递一个实例。由于我们在构造函数中这样做,没有它就无法实例化该类的对象:我们正在注入依赖项。现在,我们知道DatabaseAdapter
总是存在于类中,我们可以轻松地依赖它。
The write()
method just calls methods on the adapter, since we definitely know it exists because we made use of DI.
该write()
方法只是调用适配器上的方法,因为我们肯定知道它存在,因为我们使用了 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.
使用 DI 而不是误用静态类、god 对象和其他类似的东西的巨大优势在于,您可以轻松地跟踪依赖项的来源。
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 three types of Dependency Injection
依赖注入的三种类型
Constructor Injection
构造函数注入
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
二传手注射
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.
这种类型使用专用方法来注入依赖项。而不是使用构造函数。使用 Setter 注入的优点是,您可以在创建对象后为其添加依赖项。它通常用于可选依赖项。Setter 注入也可以很好地整理您的构造函数,并且仅在您需要它们的方法中拥有您的依赖项。
<?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
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).
可以在没有任何依赖关系的情况下实例化对象。有一种注入依赖项 ( setLogger()
) 的方法,可以选择性地调用它。现在取决于方法实现是否使用依赖项(如果未设置)。
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 Patternand to move the dependency into the constructor (as optional argument, if nothing is passed the null object is created inside the class)
值得指出的是,使用 Setter 注入要谨慎。在尚未注入的依赖项上调用方法或访问属性将导致令人讨厌的Fatal error: Call to a member function XXX() on a non-object
. 因此,每次访问依赖项时,都必须先对其进行空检查。一个更简洁的方法是使用空对象模式并将依赖项移动到构造函数中(作为可选参数,如果没有传递任何内容,则在类中创建空对象)
Interface Injection
接口注入
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.
接口注入的基本思想是,在接口中定义注入依赖项的方法。需要依赖的类必须实现接口。从而确保所需的依赖可以正确地注入到依赖对象中。它是之前解释过的 Setter 注入的一种更严格的形式。
<?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.
接口注入的意义是有争议的。我个人从未使用过它。我更喜欢构造函数注入而不是它。这使我能够完成完全相同的任务,而无需注入器端的额外接口。
Dependency Inversion
依赖倒置
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 hintedinterface. 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.
现在我们反转控制。我们现在可以注入任何使用类型提示接口的实例,而不是依赖于具体实例。接口负责后面的具体实例实现我们将要使用的所有方法,以便我们仍然可以在依赖类中依赖它们。
Make sure you get that one, because it is a core concept of the IoC-Container.
确保你得到那个,因为它是 IoC-Container 的核心概念。
The IoC Container
IoC 容器
In the shortest terms I can think of I would describe the IoC container like that:
用我能想到的最短术语来说,我会这样描述 IoC 容器:
The IoC-Container is a component that knows how instances are created and knows of alltheir underlying dependencies and how to resolve them.
IoC-Container 是一个组件,它知道实例是如何创建的,知道它们的所有底层依赖关系以及如何解决它们。
If we take our example above, imagine that the DatabaseAdapter
itself has it's own dependencies.
如果我们以上面的例子为例,想象一下它DatabaseAdapter
本身有它自己的依赖关系。
class ConcreteDatabaseAdapter implements DatabaseAdapterInterface{
protected $driver;
public function __construct(DatabaseDriverInterface $driver)
{
$this->driver = $driver;
}
}
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.
因此,为了能够使用 ,DatabaseAdapter
您需要传入DatabaseDriverInterface
抽象的实例作为依赖项。
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.
但是你的DatabaseWriter
班级不知道,也不应该知道。TheDatabaseWriter
不应该关心如何DatabaseAdapter
工作,它应该只关心 aDatabaseAdapter
被传递而不是它需要如何创建。这就是 IoC-Container 派上用场的地方。
App::bind('DatabaseWriter', function(){
return new DatabaseWriter(
new ConcreteDatabaseAdapter(new ConcreteDatabaseDriver)
);
});
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.
正如我已经说过的,它DatabaseWriter
本身对它的依赖项的依赖项一无所知。但是 IoC-Container 知道所有关于它们的信息,也知道在哪里可以找到它们。所以最终当DatabaseWriter
类要被实例化时,首先询问 IoC-Container需要如何实例化。这就是 IoC-Container 所做的。
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.
简单地说(如果您喜欢设计模式)。它有点像依赖注入容器(我已经在上面解释过)和服务定位器的组合。
回答by mhndev
consider you have a Car class like bellow :
考虑你有一个像下面这样的 Car 类:
class Car
{
protected $wheels;
protected $engine;
public function __construct()
{
$this->engine = new Engine('BMW Engine');
$this->wheels = array(
new Wheel('some Wheel Brand'),
new Wheel('some Wheel Brand'),
new Wheel('some Wheel Brand'),
new Wheel('some Wheel Brand')
);
}
public function go()
{
//do fun stuff here
}
}
now think about reusing your code and for example you want to have another car class which is for Toyota company. you should write exactly above code with just a little editing as below :
现在考虑重用你的代码,例如你想要另一个丰田公司的汽车类。您应该编写完全上面的代码,只需稍加编辑,如下所示:
class AnotherCar
{
protected $wheels;
protected $engine;
public function __construct()
{
$this->engine = new Engine('Toyota Engine');
$this->wheels = array(new Wheel('some Wheel Brand'),
new Wheel('some Wheel Brand'),
new Wheel('some Wheel Brand'),
new Wheel('some Wheel Brand'));
}
public function go()
{
//do fun stuff here
}
}
your code will grow and grow and will be a pain if you don't use DI(Dependency Injection) or IoC(Inversion Of Control).
如果您不使用 DI(依赖注入)或 IoC(控制反转),您的代码会不断增长并且会很痛苦。
lets talk about what are the above code problems : 1- its not reusable . 2- it doesn't care about performance (Volume of code in your Ram , ...) 3- your code grows and you will not understand it yourself after a time . ...
让我们谈谈上面的代码问题是什么: 1- 不可重用。2- 它不关心性能(您的 Ram 中的代码量,...) 3- 您的代码会增长,一段时间后您将无法理解它。...
so lets use DI and solve the problem
所以让我们使用 DI 来解决问题
class Car
{
protected $wheels;
protected $engine;
public function __construct($myWheels , $myEngine)
{
$this->engine = $myEngine;
$this->wheels = $myWheels;
}
public function go()
{
//do fun stuff here
}
}
now easily you can create every car object with above Car class like below :
现在,您可以轻松地创建具有上述 Car 类的每个汽车对象,如下所示:
$BMW = new Car (new Engine('BMW' , array(new Wheel('wheel beand') ,new Wheel('wheel beand') ,new Wheel('wheel beand') ,new Wheel('wheel beand') )));
see ! much easier ! also your code is much readable and reusable !
看 !容易多了!您的代码也非常可读和可重用!
lets consider you have a big project that have so many classes that probably lot of objects and classes depends on other objects and classes.
让我们考虑一下你有一个大项目,它有很多类,可能很多对象和类依赖于其他对象和类。
it will bother you to keep in mind while working that which class depends on what classes and ... this is where IOC comes in and solve the problems.
在工作时记住哪个课程取决于哪个课程会打扰你......这就是IOC进来并解决问题的地方。
IOC pattern or maybe solution is not really easy to understand you should face to problems I mentioned earlier . IOC just is neede in big projects ! in java Spring is one of the most famous IOC container and what IOC does??? it contains lots of config file ( or some IOC container configs are in source code and also some of them have user interface to configs) and this configs contain relation between class and which class for loading depend on which classes this also is a big help for lazy loading ! just when you need to load an object you load its dependecies !
IOC 模式或解决方案并不是很容易理解,您应该面对我之前提到的问题。大项目需要IOC!在java Spring 是最著名的IOC 容器之一,IOC 是做什么的???它包含大量的配置文件(或一些 IOC 容器配置在源代码中,其中一些具有配置的用户界面),并且这些配置包含类之间的关系以及加载哪个类取决于哪些类,这也是一个很大的帮助懒加载!当你需要加载一个对象时,你加载它的依赖项!