看Laravel的IoC容器文档只是介绍实例,但是没有说原理,之前用MVC框架都没有在意这个概念,无意中在phalcon的文档中看到这个详细的介绍,感觉豁然开朗,复制粘贴过来,主要是好久没有写东西了,现在确实很懒变得!
首先,我们假设,我们要开发一个组件命名为SomeComponent。这个组件中现在将要注入一个数据库连接。
在这个例子中,数据库连接在component中被创建,这种方法是不切实际的,这样做的话,我们将不能改变数据库连接参数及数据库类型等一些参数。
07 |
* The instantiation of the connection is hardcoded inside |
08 |
* the component so is difficult to replace it externally |
09 |
* or change its behavior |
11 |
public function someDbTask() |
13 |
$connection = new Connection(array( |
14 |
"host" => "localhost", |
16 |
"password" => "secret", |
25 |
$some = new SomeComponent(); |
为了解决上面所说的问题,我们需要在使用前创建一个外部连接,并注入到容器中。就目前而言,这看起来是一个很好的解决方案:
06 |
protected $_connection; |
09 |
* Sets the connection externally |
11 |
public function setConnection($connection) |
13 |
$this->_connection = $connection; |
16 |
public function someDbTask() |
18 |
$connection = $this->_connection; |
25 |
$some = new SomeComponent(); |
27 |
//Create the connection |
28 |
$connection = new Connection(array( |
29 |
"host" => "localhost", |
31 |
"password" => "secret", |
35 |
//Inject the connection in the component |
36 |
$some->setConnection($connection); |
现在我们来考虑一个问题,我们在应用程序中的不同地方使用此组件,将多次创建数据库连接。使用一种类似全局注册表的方式,从这获得一个数据库连接实例,而不是使用一次就创建一次。
07 |
* Returns the connection |
09 |
public static function getConnection() |
11 |
return new Connection(array( |
12 |
"host" => "localhost", |
14 |
"password" => "secret", |
24 |
protected $_connection; |
27 |
* Sets the connection externally |
29 |
public function setConnection($connection){ |
30 |
$this->_connection = $connection; |
33 |
public function someDbTask() |
35 |
$connection = $this->_connection; |
42 |
$some = new SomeComponent(); |
44 |
//Pass the connection defined in the registry |
45 |
$some->setConnection(Registry::getConnection()); |
现在,让我们来想像一下,我们必须在组件中实现两个方法,首先需要创建一个新的数据库连接,第二个总是获得一个共享连接:
06 |
protected static $_connection; |
09 |
* Creates a connection |
11 |
protected static function _createConnection() |
13 |
return new Connection(array( |
14 |
"host" => "localhost", |
16 |
"password" => "secret", |
22 |
* Creates a connection only once and returns it |
24 |
public static function getSharedConnection() |
26 |
if (self::$_connection===null){ |
27 |
$connection = self::_createConnection(); |
28 |
self::$_connection = $connection; |
30 |
return self::$_connection; |
34 |
* Always returns a new connection |
36 |
public static function getNewConnection() |
38 |
return self::_createConnection(); |
46 |
protected $_connection; |
49 |
* Sets the connection externally |
51 |
public function setConnection($connection){ |
52 |
$this->_connection = $connection; |
56 |
* This method always needs the shared connection |
58 |
public function someDbTask() |
60 |
$connection = $this->_connection; |
66 |
* This method always needs a new connection |
68 |
public function someOtherDbTask($connection) |
75 |
$some = new SomeComponent(); |
77 |
//This injects the shared connection |
78 |
$some->setConnection(Registry::getSharedConnection()); |
82 |
//Here, we always pass a new connection as parameter |
83 |
$some->someOtherDbTask(Registry::getConnection()); |
到此为止,我们已经看到了如何使用依赖注入解决我们的问题。不是在代码内部创建依赖关系,而是让其作为一个参数传递,这使得我们的程序更容易维护,降低程序代码的耦合度,实现一种松耦合。但是从长远来看,这种形式的依赖注入也有一些缺点。
例如,如果组件中有较多的依赖关系,我们需要创建多个setter方法传递,或创建构造函数进行传递。另外,每次使用组件时,都需要创建依赖组件,使代码维护不太易,我们编写的代码可能像这样:
03 |
//Create the dependencies or retrieve them from the registry |
04 |
$connection = new Connection(); |
05 |
$session = new Session(); |
06 |
$fileSystem = new FileSystem(); |
07 |
$filter = new Filter(); |
08 |
$selector = new Selector(); |
10 |
//Pass them as constructor parameters |
11 |
$some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector); |
13 |
// ... or using setters |
15 |
$some->setConnection($connection); |
16 |
$some->setSession($session); |
17 |
$some->setFileSystem($fileSystem); |
18 |
$some->setFilter($filter); |
19 |
$some->setSelector($selector); |
我想,我们不得不在应用程序的许多地方创建这个对象。如果你不需要依赖的组件后,我们又要去代码注入部分移除构造函数中的参数或者是setter方法。为了解决这个问题,我们再次返回去使用一个全局注册表来创建组件。但是,在创建对象之前,它增加了一个新的抽象层:
09 |
* Define a factory method to create SomeComponent instances injecting its dependencies |
11 |
public static function factory() |
14 |
$connection = new Connection(); |
15 |
$session = new Session(); |
16 |
$fileSystem = new FileSystem(); |
17 |
$filter = new Filter(); |
18 |
$selector = new Selector(); |
20 |
return new self($connection, $session, $fileSystem, $filter, $selector); |
这一刻,我们好像回到了问题的开始,我们正在创建组件内部的依赖,我们每次都在修改以及找寻一种解决问题的办法,但这都不是很好的做法。
一种实用和优雅的来解决这些问题,是使用容器的依赖注入,像我们在前面看到的,容器作为全局注册表,使用容器的依赖注入做为一种桥梁来解决依赖可以使我们的代码耦合度更低,很好的降低了组件的复杂性:
08 |
public function __construct($di) |
13 |
public function someDbTask() |
16 |
// Get the connection service |
17 |
// Always returns a new connection |
18 |
$connection = $this->_di->get('db'); |
22 |
public function someOtherDbTask() |
25 |
// Get a shared connection service, |
26 |
// this will return the same connection everytime |
27 |
$connection = $this->_di->getShared('db'); |
29 |
//This method also requires a input filtering service |
30 |
$filter = $this->_db->get('filter'); |
36 |
$di = new Phalcon\DI(); |
38 |
//Register a "db" service in the container |
39 |
$di->set('db', function(){ |
40 |
return new Connection(array( |
41 |
"host" => "localhost", |
43 |
"password" => "secret", |
48 |
//Register a "filter" service in the container |
49 |
$di->set('filter', function(){ |
53 |
//Register a "session" service in the container |
54 |
$di->set('session', function(){ |
58 |
//Pass the service container as unique parameter |
59 |
$some = new SomeComponent($di); |
现在,该组件只有访问某种service的时候才需要它,如果它不需要,它甚至不初始化,以节约资源。该组件是高度解耦。他们的行为,或者说他们的任何其他方面都不会影响到组件本身。
我们的实现办法¶
Phalcon\DI 是一个实现了服务的依赖注入功能的组件,它本身也是一个容器。
由于Phalcon高度解耦,Phalcon\DI 是框架用来集成其他组件的必不可少的部分,开发人员也可以使用这个组件依赖注入和管理应用程序中不同类文件的实例。
基本上,这个组件实现了 Inversion of Control 模式。基于此,对象不再以构造函数接收参数或者使用setter的方式来实现注入,而是直接请求服务的依赖注入。这就大大降低了整体程序的复杂性,因为只有一个方法用以获得所需要的一个组件的依赖关系。
此外,这种模式增强了代码的可测试性,从而使它不容易出错。
在容器中注册服务¶
框架本身或开发人员都可以注册服务。当一个组件A要求调用组件B(或它的类的一个实例),可以从容器中请求调用组件B,而不是创建组件B的一个实例。
这种工作方式为我们提供了许多优点:
我们可以更换一个组件,从他们本身或者第三方轻松创建。
在组件发布之前,我们可以充分的控制对象的初始化,并对对象进行各种设置。
我们可以使用统一的方式从组件得到一个结构化的全局实例
服务可以通过以下几种方式注入到容器:
03 |
//Create the Dependency Injector Container |
04 |
$di = new Phalcon\DI(); |
07 |
$di->set("request", 'Phalcon\Http\Request'); |
09 |
//Using an anonymous function, the instance will lazy loaded |
10 |
$di->set("request", function(){ |
11 |
return new Phalcon\Http\Request(); |
14 |
//Registering directly an instance |
15 |
$di->set("request", new Phalcon\Http\Request()); |
17 |
//Using an array definition |
18 |
$di->set("request", array( |
19 |
"className" => 'Phalcon\Http\Request' |
在上面的例子中,当向框架请求访问一个请求数据时,它将首先确定容器中是否存在这个”reqeust”名称的服务。
容器会反回一个请求数据的实例,开发人员最终得到他们想要的组件。
在上面示例中的每一种方法都有优缺点,具体使用哪一种,由开发过程中的特定场景来决定的。
用一个字符串来设定一个服务非常简单,但缺少灵活性。设置服务时,使用数组则提供了更多的灵活性,而且可以使用较复杂的代码。lambda函数是两者之间一个很好的平衡,但也可能导致更多的维护管理成本。
Phalcon\DI 提供服务的延迟加载。除非开发人员在注入服务的时候直接实例化一个对象,然后存存储到容器中。在容器中,通过数组,字符串等方式存储的服务都将被延迟加载,即只有在请求对象的时候才被初始化。
03 |
//Register a service "db" with a class name and its parameters |
05 |
"className" => "Phalcon\Db\Adapter\Pdo\Mysql", |
06 |
"parameters" => array( |
08 |
"host" => "localhost", |
10 |
"password" => "secret", |
16 |
//Using an anonymous function |
17 |
$di->set("db", function(){ |
18 |
return new Phalcon\Db\Adapter\Pdo\Mysql(array( |
19 |
"host" => "localhost", |
21 |
"password" => "secret", |
以上这两种服务的注册方式产生相同的结果。然后,通过数组定义的,在后面需要的时候,你可以修改服务参数:
3 |
$di->setParameter("db", 0, array( |
从容器中获得服务的最简单方式就是使用”get”方法,它将从容器中返回一个新的实例:
1 |
<?php $request = $di->get("request"); |
或者通过下面这种魔术方法的形式调用:
3 |
$request = $di->getRequest(); |
5 |
Phalcon\DI 同时允许服务重用,为了得到一个已经实例化过的服务,可以使用 getShared() 方法的形式来获得服务。 |
具体的 Phalcon\Http\Request 请求示例:
3 |
$request = $di->getShared("request"); |
参数还可以在请求的时候通过将一个数组参数传递给构造函数的方式:
3 |
$component = $di->get("MyComponent", array("some-parameter", "other")) |
- 【转】理解 PHP 依赖注入 | Laravel IoC容器
Laravel框架的依赖注入确实很强大,并且通过容器实现依赖注入可以有选择性的加载需要的服务,减少初始化框架的开销,下面是我在网上看到的一个帖子,写的很好拿来与大家分享,文章从开始按照传统的类设计数据 ...
- C#中的依赖注入和IoC容器
在本文中,我们将通过用C#重构一个非常简单的代码示例来解释依赖注入和IoC容器. 简介: 依赖注入和IoC乍一看可能相当复杂,但它们非常容易学习和理解. 在本文中,我们将通过在C#中重构一个非常简单的 ...
- SpringIOC的概念理解、构造器注入、setter注入、p命名空间注入、IOC容器介绍与比较
1.IOC概念理解 IOC(Inversion of Control)即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象 ...
- 理解 PHP 依赖注入 和 控制反转
理解 PHP 依赖注入 和 控制反转 要想理解 PHP 依赖注入 和 控制反转 两个概念,就必须搞清楚如下的两个问题: DI -- Dependency Injection 依赖注入 IoC -- ...
- Atitit js中的依赖注入di ioc的实现
Atitit js中的依赖注入di ioc的实现 全类名(FQCN)为标识符1 混合请求模式1 使用类内 builder 即可..2 Service locator method走ok拦2 Jav ...
- spring 依赖注入(IOC DI)
依赖注入(IOC DI) 依赖注入的两种方式: 1. set注入 Spring要求使用set注入方式的时候,Bean需要提供一个无参数的构造方法.并提供一个属性的setter方法.例如: packag ...
- [ASP.NET Core 3框架揭秘] 依赖注入:IoC模式
原文:[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式 正如我们在<依赖注入:控制反转>提到过的,很多人将IoC理解为一种“面向对象的设计模式”,实际上IoC不仅与面向对象没 ...
- 依赖注入和IOC
http://www.bbsmvc.com/archiver/csharp/thread-831-1.html 本来想使用一下Ninject的,然后搜索了很久,都没找到比较详细的关于Ninject的使 ...
- 依赖注入(IOC)二
依赖注入(IOC)二 上一章我们讲了构造注入与设值注入,这一篇我们主要讲接口注入与特性注入. 接口注入 接口注入是将抽象类型的入口以方法定义在一个接口中,如果客户类型需要获得这个方法,就需要以实现这个 ...
随机推荐
- 谷歌浏览器-如何让Chrome默认以隐身模式启动?
桌面图标右键属性,在“目标”后添加参数“ --incognito”(注意是双短划线,不包括双引号,双短划线前加一空格)就可以直接以隐身模式启动Chrome浏览器
- XAML设计器卡死
在生成工程时,存在这样一个记录: “未能找到一个或多个间接引用的程序集.分析不需要这些程序集.但是,如果没有这些程序集,分析结果可能不完整”. 表现形式既不是错误,可也不是警告.之所以关注到这个问题, ...
- C#读取shp的属性信息
一个完整的ESRI的shape文件包括一个主文件,一个索引文件,和一个dBASE表文件.主文件是一个直接存取,变记录长度文件,其中每个记录描述一 个由其顶点列表组成的shape.在索引文件中,每条记录 ...
- Bootstrap 小技巧以及相关资源整理
1, Bootstrap Bundle (http://bootstrapbundle.com/): 提供了15中不同的MVC Bootstrap模板.[扩展和更新]中搜索“Bootstrap Bu ...
- a标签中使用img后的高度多了4px
前两天,在做一个网站的时候,发现a标签中使用img后的高度多了4px,各种纠结. 最后,仔细分析,终于找到原因了,因为img是行内元素,默认display: inline; 它与文本的默认行为类似,下 ...
- static和public
static:静态. 可以设置:静态类.静态变量.静态方法. 没有使用static修饰的成员为实例成员. 静态成员的使用:通过类名. 1.不加static修饰的成员是对象成员,归每个对象所 ...
- 熔断器设计模式<转>
熔断器设计模式 如果大家有印象的话,尤其是夏天,如果家里用电负载过大,比如开了很多家用电器,就会”自动跳闸”,此时电路就会断开.在以前更古老的一种方式是”保险丝”,当负载过大,或者电路发生故障或异常时 ...
- 关于sql中constraint 前缀的用意(PK、UK、DF、CK、FK)
--主键constraint PK_字段 primary key(字段), --唯一约束constraint UK_字段 unique key(字段), --默认约束constrint DF_字段 d ...
- JQuery的复选框选中、取消、全选,全不选问题
一.必须引入JQuery库: 下面是js代码: /*** * 服务管理块>>>复选框事件处理 */ //服务管理复选框被选中.取消$(function(){ $("#Ser ...
- 微信(一) 获取openid 网页授权 C# WeChatHelper
用.Net开发微信的时候第一步就是获取微信的网页授权,获取openid. 自己做个总结,以后也好用,这里只提供了获取openid的接口,后续程序有待开发 using System; using Sys ...