当A类须要依赖于B类,也就是说须要在A类中实例化B类的对象来使用时候,若是B类中的功能发生改变,也会致使A类中使用B类的地方也要跟着修改,致使A类与B类高耦合。这个时候解决方式是,A类应该去依赖B类的接口,把具体的类的实例化交给外部。
就拿咱们业务中经常使用的通知模块来讲。php
<?php /** * 定义了一个消息类 * Class Message */ class Message{ public function seed() { return 'seed email'; } } /* * 订单产生的时候 须要发送消息 */ class Order{ protected $messager = ''; function __construct() { $this->messager = new Message(); } public function seed_msg() { return $this->messager->seed(); } } $Order = new Order(); $Order->seed_msg();
上面的代码是咱们传统的写法。首先由个消息发送的类。而后在咱们须要发送消息的地方,调用发送消息的接口。有一天你须要添加一个发送短信的接口以知足不一样的需求。那么你会发现你要再Message
类里面作修改。一样也要再Order
类里面作修改。这样就显得很麻烦。这个时候就有了依赖注入的思路。下面把代码作一个调整html
<?php /** * 为了约束咱们先定义一个消息接口 * Interface Message */ interface Message{ public function seed(); } /** * 有一个发送邮件的类 * Class SeedEmail */ class SeedEmail implements Message { public function seed() { return 'seed email'; // TODO: Implement seed() method. } } /** *新增一个发送短信的类 * Class SeedSMS */ class SeedSMS implements Message { public function seed() { return 'seed sms'; // TODO: Implement seed() method. } } /* * 订单产生的时候 须要发送消息 */ class Order{ protected $messager = ''; function __construct(Message $message) { $this->messager = $message; } public function seed_msg() { return $this->messager->seed(); } } //咱们须要发送邮件的时候 $message = new SeedEmail(); //将邮件发送对象做为参数传递给Order $Order = new Order($message); $Order->seed_msg(); //咱们须要发送短信的时候 $message = new SeedSMS(); $Order = new Order($message); $Order->seed_msg();
这样咱们就实现了依赖注入的思路,是否是很方便扩展了。闭包
我理解的服务容器就是一个自动产生类的工厂。
<?php /** * 为了约束咱们先定义一个消息接口 * Interface Message */ interface Message{ public function seed(); } /** * 有一个发送邮件的类 * Class SeedEmail */ class SeedEmail implements Message { public function seed() { return 'seed email'; // TODO: Implement seed() method. } } /** *新增一个发送短信的类 * Class SeedSMS */ class SeedSMS implements Message { public function seed() { return 'seed sms'; // TODO: Implement seed() method. } } /** * 这是一个简单的服务容器 * Class Container */ class Container { protected $binds; protected $instances; public function bind($abstract, $concrete) { if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } public function make($abstract, $parameters = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters); } } //建立一个消息工厂 $message = new Container(); //将发送短信注册绑定到工厂里面 $message->bind('SMS',function (){ return new SeedSMS(); }); //将发送邮件注册绑定到工厂 $message->bind('EMAIL',function (){ return new SeedEmail(); }); //须要发送短信的时候 $SMS = $message->make('SMS'); $SMS->seed();
container
是一个简单的服务容器里面有bind
,make
两个方法bind
是向容器中绑定服务对象。make
则是从容器中取出对象。函数
在bind
方法中须要传入一个 concrete
咱们能够传入一个实例对象或者是一个闭包函数。
能够看到我这全使用的是闭包函数,其实也能够这样写this
$sms = new SeedSMS(); $message->bind('SMS',$sms);
后面这种写法与闭包相比的区别就是咱们须要先实例化对象才能往容易中绑定服务。而闭包则是咱们使用这个服务的时候才去实例化对象。能够看出闭包是有不少的优点的。.net
make
方法就从容器中出去方法。里面首先判断了instances
变量中是否有当前以及存在的服务对象,若是有直接返回。若是没有那么会经过 call_user_func_array
返回一个对象。call_user_func_array
的使用能够查看
PHP 中 call_user_func 的使用 code
原文地址htm