导读
这一系列的文章讲述了依赖注入概念和一个轻量级的使用PHP实现。本篇文章为Part 2。
目录
- Part 1: 什么是依赖注入
- Part 2: 你是否需要依赖注入容器
- Part 3: Symfony服务容器介绍
- Part 4: Symfony服务容器: 使用Builder构建服务
- Part 5: Symfony服务容器: 使用XML或yayML描述服务
- Part 6: 速度的需要
正文
这一系列文章的第一期主要讲依赖注入的概念,我已经尽量给出具体的系统实例来做演示。今天,我将讲述依赖注入容器。
首先,让我们从一句重要的声明开始:
大多数时候,你并不需要通过依赖注入容器来获得依赖注入带来的益处。
但是当你需要管理大量不同的对象实例,同时这些对象实例又有很多的依赖关系时,一个依赖注入容器便真的很有用处(试想一下框架为例)。
如果你记得第一篇文章中的例子,创建一个User对象需要首先创建一个SessionStorage对象。小事一桩,但是你在创建这个对象之前必须得知晓它所有的依赖:
$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);
在接下来的文章中,我们将要讲述Symfony2的依赖注入容器PHP版实现。为了说明这个实现没有局限于Symfony,在文章中我将会用Zend Framework为例来说明。
与普遍的认识相反,PHP框架之争并不存在。我真的对Zend Framework组件充满感激,事实上,它的很多库对Symfony项目非常有用。
Zend Framework的Mail库简化了email管理,但是却默认使用了不是特别灵活的PHP的mail()函数。幸好,通过提供transport对象可以很容易地改变这个行为。接下来这个代码片段演示了如何创建使用Gmail账号发送email的Zend_Mail对象实例:
$transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
'auth' => 'login',
'username' => 'foo',
'password' => 'bar',
'ssl' => 'ssl',
'port' => 465,
));
$mailer = new Zend_Mail();
$mailer->setDefaultTransport($transport);
为了使这篇文章足够短,我使用了简单的示例。当然,针对这些简单的例子使用容器,几乎没有意义。只要将实例当成容器要管理的对象集合的很小的一部分就可以了。
依赖注入容器是一个知道如何初始化和配置其他实例对象的实例对象。为了完成自己的工作,依赖注入容器需要知道实例对象的构造函数的参数以及对象之间的关系。
这里是一段通过简单硬编码而为上文的Zend_Mail示例所写的容器实现:
class Container
{
public function getMailTransport()
{
return new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
'auth' => 'login',
'username' => 'foo',
'password' => 'bar',
'ssl' => 'ssl',
'port' => 465,
));
}
public function getMailer()
{
$mailer = new Zend_Mail();
$mailer->setDefaultTransport($this->getMailTransport());
return $mailer;
}
}
使用这个容器非常简单:
$container = new Container();
$mailer = $container->getMailer();
当使用这个容器时,我们仅仅需要一个mailer对象,并且我们不再需要知道如何创建它;所有有关如何创建mailer实例的信息都已经嵌入到了这个容器中。这个容器将会通过getMailTransport()函数调用自动注入mail transport依赖。这个容器的所有威力都体现在这个简单的函数调用。
但是,精明的读者可能已经发现这里的一个问题。这个容器都是硬编码的!因此,我们要进一步为其添加参数来使它更加有用:
class Container
{
protected $parameters = array();
public function __construct(array $parameters = array())
{
$this->parameters = $parameters;
}
public function getMailTransport()
{
return new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
'auth' => 'login',
'username' => $this->parameters['mailer.username'],
'password' => $this->parameters['mailer.password'],
'ssl' => 'ssl',
'port' => 465,
));
}
public function getMailer()
{
$mailer = new Zend_Mail();
$mailer->setDefaultTransport($this->getMailTransport());
return $mailer;
}
}
现在通过向容器的构造函数传入一些参数很容易地来改变Google的用户名和密码:
$container = new Container(array(
'mailer.username' => 'foo',
'mailer.password' => 'bar',
));
$mailer = $container->getMailer();
如果你需要为测试改变mailer类,类名也可以作为参数传入容器:
class Container
{
// ...
public function getMailer()
{
$class = $this->parameters['mailer.class'];
$mailer = new $class();
$mailer->setDefaultTransport($this->getMailTransport());
return $mailer;
}
}
$container = new Container(array(
'mailer.username' => 'foo',
'mailer.password' => 'bar',
'mailer.class' => 'Zend_Mail',
));
$mailer = $container->getMailer();
最后但不是不重要,每次我需要一个mailer实例时,我不都需要一个新的实例。因此,这个容器可以改为总是返回相同的实例对象:
class Container
{
static protected $shared = array();
// ...
public function getMailer()
{
if (isset(self::$shared['mailer']))
{
return self::$shared['mailer'];
}
$class = $this->parameters['mailer.class'];
$mailer = new $class();
$mailer->setDefaultTransport($this->getMailTransport());
return self::$shared['mailer'] = $mailer;
}
}
通过引入静态属性$shared,每次你调用getMailer()函数时,返回的是第一次调用这个函数时所创建的对象实例。
一个依然注入容器需要实现这些基本的功能。一个依赖注入容器管理对象实例:从它们的实例化到配置。这些对象无法感知到管理它们的容器的存在,对容器也是毫无所知。那就是为什么容器可以管理PHP对象。对象使用依赖注入管理其依赖是更好的,而且不需要前置条件。
当然,手动创建和维护容器很快会变成梦魇。但是一个可用的容器只有很小的要求,很容易实现。这一系列的下一篇文章将讨论Symfony2依赖注入容器的实现。
转载必须注明出处: http://zengfeng.info/2016/04/你是否需要依赖注入容器
网友评论