DependencyInjection&IOC&ServiceLocator理解

DependencyInjection中文名叫依赖注入,是一种设计模式。其实接触DI已经很久,总结总结自己的浅显认识,也欢迎资深人士指正。下面看看DI怎么配合框架使用。

首先 我们有个somecomponent类,它需要依赖数据库连接。

class SomeComponent 
{
	public function someDbTask()
	{
		$connection = new Connection(array(
			"host" => "localhost",
			"username" => "root",
			"password" => "secret",
			"dbname" => "invo"
		));
		// ...
	}
}
$some = new SomeComponent();
$some->someDbTask();

我们看到如上的使用方法,这样的话 ,connection的创建 完全在类内部,我们在外部完全没法修改连接参数,也完全没法复用。

好,那独立注入,来优化这个事情。把这种依赖的,通过set注入进去。

class SomeComponent
{
	protected $_connection;
	/**
	* Sets the connection externally
	*/
	public function setConnection($connection)
	{
		$this->_connection = $connection;
	}
	public function someDbTask()
	{
		$connection = $this->_connection;
		// ...
	}
}
$some = new SomeComponent();
//Create the connection
$connection = new Connection(array(
	"host" => "localhost",
	"username" => "root",
	"password" => "secret",
	"dbname" => "invo"
));
//Inject the connection in the component
$some->setConnection($connection);
$some->someDbTask();

那么接着而来的问题是 每次使用的时候 我都需要生成一个connection 然后注入一下。

那么我们怎么解决呢 来个global registry

class Registry
{
    /**
    * Returns the connection
    */
    public static function getConnection()
    {
        return new Connection(array(
        "host" => "localhost",
        "username" => "root",
        "password" => "secret",
        "dbname" => "invo"
        ));
    }
}
class SomeComponent
{
    protected $_connection;
    /**
    * Sets the connection externally
    */
    public function setConnection($connection)
    {
        $this->_connection = $connection;
    }
    public function someDbTask()
    {
        $connection = $this->_connection;
        // ...
    }
}
$some = new SomeComponent();
//Pass the connection defined in the registry
$some->setConnection(Registry::getConnection());
$some->someDbTask();

进一步呢 我们优化getconnection来,类似单例模式。


class Registry
{
	protected static $_connection;
	/**
	* Creates a connection
	*/
	protected static function _createConnection()
	{
		return new Connection(array(
		"host" => "localhost",
		"username" => "root",
		"password" => "secret",
		"dbname" => "invo"
		));
	}
	/**
	* Creates a connection only once and returns it
	*/
	public static function getSharedConnection()
	{
		if (self::$_connection===null){
			$connection = self::_createConnection();
			self::$_connection = $connection;
		}
		return self::$_connection;
	}
	/**
	* Always returns a new connection
	*/
	public static function getNewConnection()
	{
		return self::_createConnection();
	}
}
$some = new SomeComponent();
//This injects the shared connection
$some->setConnection(Registry::getSharedConnection());
$some->someDbTask();
//Here, we always pass a new connection as parameter
$some->someOtherDbTask(Registry::getNewConnection());

没错 已经使用了DI模式,看起来代码已经解耦,可维护性也不错。

但是呢,进一步的这种依赖有可能很多的时候,就会出现问题。


//Create the dependencies or retrieve them from the registry
$connection = new Connection();
$session = new Session();
$fileSystem = new FileSystem();
$filter = new Filter();
$selector = new Selector();
//Pass them as constructor parameters
$some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);
// ... or using setters
$some->setConnection($connection);
$some->setSession($session);
$some->setFileSystem($fileSystem);
$some->setFilter($filter);
$some->setSelector($selector);

有可能这段代码 在很多运行some类的某个方法的时候用到,那么每个用的地方都有这么一堆注入。

一旦某个依赖不需要了,修改起来也是灾难性的。

也许接着的方案就是 再把这些依赖 移到类的factory方法去。。

class 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 new self($connection, $session, $fileSystem, $filter, $selector);
	}
}

啊哈,你看到 ,似乎我们的代码回到了最初开始的地方。

怎么办呢。。。最终我们需要一个容器来装这些依赖。

class SomeComponent
{
	protected $_di;
	public function __construct($di)
	{
		$this->_di = $di;
	}
	public function someDbTask()
	{
		// Get the connection service
		// Always returns a new connection
		$connection = $this->_di->get('db');
	}
	public function someOtherDbTask()
	{
		// Get a shared connection service,
		// this will return the same connection everytime
		$connection = $this->_di->getShared('db');
		//This method also requires an input filtering service
		$filter = $this->_di->get('filter');
	}
}

DI容器 看起来和global registry 很像,用di这个容器做桥梁来获得依赖,减少了我们实现类的复杂性。

同时DI容器实现了 需要时候才初始化的特性,在不使用的时候不进行初始化,这个和类似zend_registry是个改进。

任何依赖如connection的修改,并不影响somecomponent的代码。

它不仅能注入一些依赖使得代码解耦,而且很好的管理一些全局的共享类实例,也使得测试变得简单,同时DI容器提供了类内部依赖的统一请求接口。

DI从字面上只是注入依赖这个事情,其实深层次的,配合DI容器构成ServiceLocator,可以达到很强大的效果。


那么提提IoC,IoC字面上就是控制反转,Inversion Of Control ,它的思想是对象的控制权进行转移,比如转移到Ioc容器,比如转移到配制文件,等等。而DI是这种思想的一种具体方式,将这种转移在运行期间,动态地将某种依赖关系注入到对象之中。



带来的好处已经提了很多,上面提及的是代码上的一些好处,使用上来说

1)使得我们可以轻松替换一个依赖组件,修改为我们自己写的或者第三方的。

2)我们有了这些依赖组件的初始化的完全控制权。

3)我们获得了全局类的一些统一的访问入口。


认识就这么多,因为我大部分是PHP的开发,猜想有可能在JAVA这种需要编译的语言上,这种模式的运用带来的好处更多。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值