依赖注入与服务定位器_第1页
依赖注入与服务定位器_第2页
依赖注入与服务定位器_第3页
依赖注入与服务定位器_第4页
依赖注入与服务定位器_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、依赖注入与服务定位器(Dependency Injection/Service Location)¶接下来的例子有些长,但解释了为什么我们使用依赖注入与服务定位器. 首先,假设我们正在开发一个组件,叫SomeComponent,它执行的内容现在还不重要。 我们的组件需要依赖数据库的连接。在下面第一个例子中,数据库的连接是在组件内部建立的。这种方法是不实用的;事实上这样做的话,我们不能改变创建数据库连接的参数或者选择不同的数据库系统,因为连接是当组件被创建时建立的。<?phpclass SomeComponent /* * 连接数据库的实例是被写死在组件的内部 * 因此,我们很难

2、从外部替换或者改变它的行为 */ public function someDbTask() $connection = new Connection(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo" ); / . $some = new SomeComponent();$so

3、me->someDbTask();为了解决这样的情况,我们建立一个setter,在使用前注入独立外部依赖。现在,看起来似乎是一个不错的解决办法:<?phpclass SomeComponent protected $_connection; /* * 设置外部传入的数据库的连接实例 */ public function setConnection($connection) $this->_connection = $connection; public function someDbTask() $connection = $this->_connection; / .

4、 $some = new SomeComponent();/建立数据库连接实例$connection = new Connection(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo");/向组件注入数据连接实例$some->setConnection($connect

5、ion);$some->someDbTask();想一下,假设我们使用这个组件在应用内的好几个地方都用到,然而我们在注入连接实例时还需要建立好几次数据的连接实例。 如果我们可以获取到数据库的连接实例而不用每次都要创建新的连接实例,使用某种全局注册表可以解决这样的问题:<?phpclass Registry /* * 返回数据库连接实例 */ public static function getConnection() return new Connection(array( "host" => "localhost", "us

6、ername" => "root", "password" => "secret", "dbname" => "invo" ); class SomeComponent protected $_connection; /* * 设置外部传入的数据库的连接实例 */ public function setConnection($connection) $this->_connection = $connection; public function someDbT

7、ask() $connection = $this->_connection; / . $some = new SomeComponent();/把注册表中的连接实例传递给组件$some->setConnection(Registry:getConnection();$some->someDbTask();现在,让我们设想一下,我们必须实现2个方法,第一个方法是总是创建一个新的连接,第二方法是总是使用一个共享连接:<?phpclass Registry protected static $_connection; /* * 建立一个新的连接实例 */ protected

8、 static function _createConnection() return new Connection(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo" ); /* * 只建立一个连接实例,后面的请求只返回该连接实例 */ public static funct

9、ion getSharedConnection() if (self:$_connection=null) $connection = self:_createConnection(); self:$_connection = $connection; return self:$_connection; /* * 总是返回一个新的连接实例 */ public static function getNewConnection() return self:_createConnection(); class SomeComponent protected $_connection; /* * 设置

10、外部传入的数据库的连接实例 */ public function setConnection($connection) $this->_connection = $connection; /* * 这个方法总是需要共享连接实例 */ public function someDbTask() $connection = $this->_connection; / . /* * 这个方法总是需要新的连接实例 */ public function someOtherDbTask($connection) $some = new SomeComponent();/注入共享连接实例$some

11、->setConnection(Registry:getSharedConnection();$some->someDbTask();/这里我们总是传递一个新的连接实例$some->someOtherDbTask(Registry:getConnection();到目前为止,我们已经看到依赖注入怎么解决我们的问题了。把依赖作为参数来传递,而不是建立在内部建立它们,这使我们的应用更加容易维护和更加解耦。不管怎么样,长期来说,这种形式的依赖注入有一些缺点。例如,如果这个组件有很多依赖, 我们需要创建多个参数的setter方法来传递依赖关系,或者建立一个多个参数的构造函数来传递它们

12、,另外在使用组件前还要每次都创建依赖,这让我们的代码像这样不易维护:<?php/创建依赖实例或从注册表中查找$connection = new Connection();$session = new Session();$fileSystem = new FileSystem();$filter = new Filter();$selector = new Selector();/把实例作为参数传递给构造函数$some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);/ . 或者使用s

13、etter$some->setConnection($connection);$some->setSession($session);$some->setFileSystem($fileSystem);$some->setFilter($filter);$some->setSelector($selector);假设我们必须在应用的不同地方使用和创建这些对象。如果当你永远不需要任何依赖实例时,你需要去删掉构造函数的参数,或者去删掉注入的setter。为了解决这样的问题,我们再次回到全局注册表创建组件。不管怎么样,在创建对象之前,它增加了一个新的抽象层:<?p

14、hpclass SomeComponent / . /* * Define a factory method to create SomeComponent instances injecting its dependencies */ public static function factory() $connection = new Connection(); $session = new Session(); $fileSystem = new FileSystem(); $filter = new Filter(); $selector = new Selector(); return

15、 new self($connection, $session, $fileSystem, $filter, $selector); 瞬间,我们又回到刚刚开始的问题了,我们再次创建依赖实例在组件内部!我们可以继续前进,找出一个每次能奏效的方法去解决这个问题。但似乎一次又一次,我们又回到了不实用的例子中。一个实用和优雅的解决方法,是为依赖实例提供一个容器。这个容器担任全局的注册表,就像我们刚才看到的那样。使用依赖实例的容器作为一个桥梁来获取依赖实例,使我们能够降低我们的组件的复杂性:<?phpclass SomeComponent protected $_di; public functi

16、on _construct($di) $this->_di = $di; public function someDbTask() / 获得数据库连接实例 / 总是返回一个新的连接 $connection = $this->_di->get('db'); public function someOtherDbTask() / 获得共享连接实例 / 每次请求都返回相同的连接实例 $connection = $this->_di->getShared('db'); / 这个方法也需要一个输入过滤的依赖服务 $filter = $this

17、->_di->get('filter'); $di = new PhalconDI();/在容器中注册一个db服务$di->set('db', function() return new Connection(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" =>

18、"invo" ););/在容器中注册一个filter服务$di->set('filter', function() return new Filter(););/在容器中注册一个session服务$di->set('session', function() return new Session(););/把传递服务的容器作为唯一参数传递给组件$some = new SomeComponent($di);$some->someTask();这个组件现在可以很简单的获取到它所需要的服务,服务采用延迟加载的方式,只有在需要使用的时

19、候才初始化,这也节省了服务器资源。这个组件现在是高度解耦。例如,我们可以替换掉创建连接的方式,它们的行为或它们的任何其他方面,也不会影响该组件。实现方法(Our approach)¶PhalconDI 是一个实现依赖注入和定位服务的组件,而且它本身就是一个装载它们的容器。因为Phalcon是高度解构的,整合框架的不同组件,使用PhalconDI是必不可少的。开发者也可以使用这个组件去注入依赖和管理的应用程序中来自不同类的全局实例。基本上,这个组件实现了 控制反转(/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD

20、%AC) 的模式。使用这种模式,组件的对象不用再使用setter或者构造函数去接受依赖实例,而是使用请求服务的依赖注入。这减少了总的复杂性,因为在组件内,只有一个方法去获取所需的依赖实例。另外,该模式增加了代码的可测试性,从而使其不易出错。使用容器注册服务(Registering services in the Container)¶框架本身或者开发者都可以注册服务。当一个组件A需要组件B(或者它的类的实例) 去操作,它可以通过容器去请求组件B,而不是创建一个新的组件B实例。这个工作方法给我们提供了许多优势:· 我们可以很容易的使用一个我们自己建立的或者是第三方的组件去替换

21、原有的组件。· 我们完全控制对象的初始化,这让我们在传递它们的实例到组件之前,根据需要设置这些对象。· 我们可以在一个结构化的和统一组件内获取全局实例。服务可以使用不同方式去定义:<?php/ 创建一个依赖注入容器$di = new PhalconDI();/ 通过类名称设置服务$di->set("request", 'PhalconHttpRequest');/ 使用匿名函数去设置服务,这个实例将被延迟加载$di->set("request", function() return new Phalc

22、onHttpRequest(););/ 直接注册一个实例$di->set("request", new PhalconHttpRequest();/ 使用数组方式定义服务$di->set("request", array( "className" => 'PhalconHttpRequest');使用数组的方式去注册服务也是可以的:<?php/ 创建一个依赖注入容器$di = new PhalconDI();/ 通过类名称设置服务$di"request" = 'Pha

23、lconHttpRequest'/ 使用匿名函数去设置服务,这个实例将被延迟加载$di"request" = function() return new PhalconHttpRequest();/ 直接注册一个实例$di"request" = new PhalconHttpRequest();/ 使用数组方式定义服务$di"request" = array( "className" => 'PhalconHttpRequest');在上面的例子中,当框架需要访问request服务的内

24、容,它会在容器里面查找名为request的服务。 在容器中将返回所需要的服务的实例。当有需要时,开发者可能最终需要替换这个组件。每个方法(在上面的例子证明)用于设置/注册服务方面具都具有优势和劣势。这是由开发者和特别的要求决定具体使用哪个。通过字符串设置一个服务是很简单,但是缺乏灵活性。通过数组设置服务提供了更加灵活的方式,但是使代码更复杂。匿名函数是上述两者之间的一个很好的平衡,但是会导致比预期的更多维护。PhalconDI 对每个储存的服务提供了延迟加载。除非开发者选择直接实例化一个对象并将其存储在容器中,任何储存在里面的对象(通过数组,字符串等等设置的)都将延迟加载,即只要当使用到时才实

25、例化。简单的注册(Simple Registration)¶就像你之前看到的那样,这里有几种方法去注册服务。下面是简单调用的例子:字符串(String)¶使用字符串注册服务需要一个有效的类名称,它将返回指定的类对象,如果类还没有加载的话,将使用自动加载器实例化对象。这种类型不允许向构造函数指定参数:<?php/ 返回 new PhalconHttpRequest(); 对象$di->set('request', 'PhalconHttpRequest');对象(Object)¶这种类型注册服务需要一个对象。实际上,这个服

26、务不再需要初始化,因为它已经是一个对象,可以说,这是不是一个真正的依赖注入,但是如果你想强制总是返回相同的对象/值,使用这种方式还是有用的:<?php/ 返回 PhalconHttpRequest(); 对象$di->set('request', new PhalconHttpRequest();闭包与匿名函数(Closures/Anonymous functions)¶这个方法提供了更加自由的方式去注册依赖,但是如果你想从外部改变实例化的参数而不用改变注册服务的代码,这是很困难的:<?php$di->set("db", f

27、unction() return new PhalconDbAdapterPdoMysql(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "blog" ););这些限制是可以克服的,通过传递额外的变量到闭包函数里面:<?php/ 把当前域的$config变量传递给匿名函数使用$d

28、i->set("db", function() use ($config) return new PhalconDbAdapterPdoMysql(array( "host" => $config->host, "username" => $config->username, "password" => $config->password, "dbname" => $config->name ););复杂的注册(Complex Registr

29、ation)¶如果要求不用实例化/解析服务,就可以改变定义服务的话,我们需要使用数组的方式去定义服务。使用数组去定义服务可以更加详细:<?php/ 通过类名和参数,注册logger服务$di->set('logger', array( 'className' => 'PhalconLoggerAdapterFile', 'arguments' => array( array( 'type' => 'parameter', 'value' =&g

30、t; './apps/logs/error.log' ) );/ 使用匿名函数的方式$di->set('logger', function() return new PhalconLoggerAdapterFile('./apps/logs/error.log'););上面两种注册服务的方式的结果是一样的。然而,使用数组定义的话,在需要的时候可以变更注册服务的参数:<?php/ 改变logger服务的类名$di->getService('logger')->setClassName('MyCusto

31、mLogger');/ 不用实例化就可以改变第一个参数值$di->getService('logger')->setParameter(0, array( 'type' => 'parameter', 'value' => './apps/logs/error.log');除了使用数组的语法注册服务,你还可以使用以下三种类型的依赖注入:构造函数注入(Constructor Injection)¶这个注入方式是通过传递依赖/参数到类的构造函数。让我们假设我们有下面的组件:<?phpnamespace SomeApp;use PhalconHttpResponse;class Some

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论