IOC :Inversion of Control,中文含义就是控制反转,IOC不是一种技术,而是一种思想,通过这种思想,我们可以设计出低耦合,灵活性高,扩展性高的程序。
既然提到控制反转,那么就有控制正转。所谓的控制正转,就是我们传统的程序设计的方式:在对象内部通过new的方式创建对象,即我们主动去创建程序的依赖对象。下面我们通过简单的示例代码表现。
<?php interface Pay { function doPay(); } #支付宝支付类 class AliPay implements Pay { function doPay() { echo "我是支付宝支付"; } } #微信支付类 class WeChatPay implements Pay { function doPay() { echo "我是微信支付"; } } #订单类 class Order { public $pay; public function __construct() { $this->pay = New AliPay(); } public function pay() { return $this->pay->doPay(); } } $order = new Order(); $order->pay(); #运行结果: #我是支付宝支付上面的示例代码就是我们的传统的程序开发方式。这是个简单的例子:我们的Order类中需要依赖支付宝支付类,那么我们就要在构造方法中手动把我们所需要的类给new出来,才可以实现支付宝提供的一些功能。
但是如果支付宝服务故障或其他一些原因需要将支付服务切换为微信支付,那么我们将要修改Order类的代码。示例中的一处修改可以接受。但是想象一下,如果系统非常庞大,支付宝支付被无数个对象或类依赖,那么我们需要修改的数量是灾难级的。由此可见传统的设计方式的解耦,灵活性及扩展性是非常差的。
上面我们通关简单的示例描述了传统的设计方式,高耦合给我们的开发工作带来了很多不便。那么我们有没有比较好的方式来解决这个问题呢?答案就是控制反转。理解控制反转关键要明确“谁控制谁?控制了什么?为何是反转?”
上述传统的开发模式中,我们在类中的依赖是通过new的方式实现的,就是程序去主动创建依赖对象;而IOC则抽象出一个容器来管理依赖的创建而不是显式的使用new的方式;谁控制谁?当然是IOC容器控制了对象;控制了什么?主要是控制了外部依赖的资源。
上述创通模式中描述了程序主动创建依赖的示例,而反转则是由IOC容器来创建和注入依赖对象。相对比主动创建依赖,被动地由容器创建和注入,所以称为“反转”。
介绍了这么多,下面我们就仿照Laravel框架,实现一个超级简易的IOC容器:
<?php interface Pay{ public function doPay(); } class AliPay implements Pay{ public function doPay() { echo '这是支付宝支付'; } } class Wechat implements Pay{ public function doPay() { echo '这是微信支付'; } } class Order{ protected $pay; public function __construct(Pay $pay) { $this->pay = $pay; } public function pay() { return $this->pay->doPay(); } } class Container{ protected $bindings = []; public function bind($abstract,$concrete) { if(!$concrete instanceof Closure){ $concrete = function($container)use($concrete){ return $container->build($concrete); }; } $this->bindings[$abstract]['concrete'] = $concrete; } public function make($abstract) { $concrete = $this->bindings[$abstract]['concrete']; return $concrete($this); } public function build($concrete) { $reflector = new ReflectionClass($concrete); $constructor = $reflector->getConstructor(); if(is_null($constructor)){ return $reflector->newInstance(); }else{ $parameters = $constructor->getParameters(); $instatces = []; foreach($parameters as $parameter){ $instatces[] = $this->make($parameter->getClass()->name); } return $reflector->newInstanceArgs($instatces); } } } $container = new Container(); $container->bind('Pay', Wechat::class); $container->bind('Order',Order::class); $container->make('Order')->pay(); #运行结果 #这是微信支付上面是我仿照Laravel写的非常简易的一个IOC容器的类,与传统的方式对比,这种方式对资源的耦合度大大降低。比如切换一个支付类,只要在绑定时更换类名,我们就可以在容器中实现依赖的注入,不需要关心内部如何实现的,因此代码的灵活性和可扩展性非常高。
由上面的示例把传统方式和IOC容器做了对比,IOC容器耦合度低,具有很高的灵活性和扩展性。 但是IOC容器就没有任何缺点吗?答案是否定的。
1.容器创建对象都是在容器内部执行,不直观,而且创建过程复杂。很多人不适应这种方式。 2.容器通过反射实例化对象,所以在性能上稍有损耗。但是对于程序的灵活性和扩展性来说,这点损耗是可接受的。
以上是通过简单的代码来介绍一下IOC容器的思想,希望通过简单的总结来让自己理解深刻 。市面上支持IOC的常见的框架有Laravel以及TP6,尤其是Laravel框架,组件式的开发模式很新颖,功能多样并且优雅。希望有兴趣的同学可去阅读一下源码,痛苦而富有乐趣。