升级工厂前的准备工作

无规矩不成方圆,随着越来越多的行为出现,我们需要需要定下一些规范。

为了约束每一个行为的规范,需要定义一个行为接口:

  1. interface BehaviorInterface
  2. {
  3. /**
  4. * 行为激活方法
  5. * @param array $values 激活的参数
  6. */
  7. public function activate(array $values);
  8. }

按照接口规范修改前述的行为类:

  1. class Attack implements BehaviorInterface{
  2. protected $value = 0;
  3. public function __construct($value)
  4. {
  5. $this->value = $value;
  6. }
  7. public function activate(array $values){}
  8. }
  9. class Defend implements BehaviorInterface{
  10. protected $value = 0;
  11. public function __construct($value){}
  12. public function activate(array $values){}
  13. }
  14. class Move implements BehaviorInterface{
  15. protected $speed;
  16. public function __construct($speed){}
  17. public function activate(array $values){}
  18. }
  19. class Skill1 implements BehaviorInterface{
  20. protected $name = '暴击';
  21. public function __construct(){}
  22. public function activate(array $values){}
  23. }
  24. class Skill2 implements BehaviorInterface{
  25. protected $name = '眩晕';
  26. public function __construct(){}
  27. public function activate(array $values){}
  28. }

使用依赖注入升级工厂

  1. 为了解决工厂对行为的依赖,在每一种行为定义好之后就放到工厂中(依赖注入)。
  2. 放到工厂中的行为必须是依照规范定义的。

  1. class BehaviorFactory
  2. {
  3. protected $instances;
  4. public function setBehavior($behaviorName, $behavior)
  5. {
  6. if($behavior instanceof BehaviorInterface) {
  7. $this->instances[$behaviorName] = $behavior;
  8. }
  9. }
  10. public function getBehavior($behaviorName)
  11. {
  12. return $this->instances[$behaviorName];
  13. }
  14. }
  15. $factory = new BehaviorFactory();
  16. $factory->setBehavior('Attack', new Attack(0));
  17. $factory->setBehavior('Defend', new Defend(0));
  18. $factory->setBehavior('Move', new Move(0));
  19. $factory->setBehavior('Skill1', new Skill1());
  20. $factory->setBehavior('Skill2', new Skill2());

对英雄类做一些简单的修改:

  1. class Hero
  2. {
  3. protected $behavior = [];
  4. public function __construct($factory, array $behaviors)
  5. {
  6. // 通过工厂提供的方法制造需要的模块
  7. foreach ($behaviors as $behaviorName => $behaviorOptions) {
  8. $behavior = $factory->getBehavior($behaviorName);
  9. $this->behavior[] = $behavior->activate($behaviorOptions);
  10. }
  11. }
  12. }
  13. $hero = new Hero($factory, [
  14. 'Attack' => [10],
  15. 'Defend' => [5],
  16. 'Move' => [30],
  17. 'Skill1' => [],
  18. ]);

至此,我们通过依赖注入,已经彻底地解决了英雄对行为的依赖、英雄对工厂的依赖、工厂对行为的依赖。

但是还有一些不足之处:

  1. 所有的对象,我们都必须手动去实例化;
  2. 这些行为,无论英雄是否需要,都要提前实例化并放到工厂中。

再谈依赖注入

前面的文章中我们已经讲解了如何通过PHP的反射机制来解决依赖注入的问题。这里我们再来深入一下对依赖注入的理解。

什么是依赖注入:

所谓的依赖注入,意思是只要不是在类内部产生的对象依赖(比如在初始化、构造函数中,通过工厂方法或者手动实例化),而是由外部以参数或其他形式注入(传入)的,都属于依赖注入(DI)。

超级工厂-IOC服务容器

接下来将我们的升级工厂,进一步改造为超级工厂。

定义一个简单的容器类,用来存储对象实例化的方式和创建对象:

  1. class Container
  2. {
  3. // 存放抽象类与对象实例化方式关系的数据
  4. protected $binds;
  5. // 存放抽象类与已有的对象关系的数据
  6. protected $instances;
  7. // 将抽象类与[对象实例化方式|已有的对象]分别存入数组
  8. public function bind($abstract, $concrete)
  9. {
  10. // 如果第二个参数是闭包,则存入 $binds 数组
  11. if ($concrete instanceof Closure) {
  12. $this->binds[$abstract] = $concrete;
  13. }
  14. // 如果第二个参数不是闭包,则为已经实例化的对象,存入 $instances 数组
  15. else {
  16. $this->instances[$abstract] = $concrete;
  17. }
  18. }
  19. // 根据对象实例化的方式创建对象并返回
  20. public function make($abstract, $parameters = [])
  21. {
  22. // 如果是已经实例化的对象则直接返回
  23. if (isset($this->instances[$abstract])) {
  24. return $this->instances[$abstract];
  25. }
  26. // 将$this(容器)放入参数数组的头部
  27. // [$this, $param1, $param2...]
  28. array_unshift($parameters, $this);
  29. // 调用对象实例化的方法(即绑定时传入的闭包函数和当前的参数),得到实例化后的对象并返回
  30. // function($container, $behaviorName) { return new Hero($behavior); }
  31. // function($this, 'skill1') { return new Hero('skill1'); }
  32. return call_user_func_array($this->binds[$abstract], $parameters);
  33. }
  34. }

测试这个容器:

  1. // 创建一个容器
  2. $container = new Container;
  3. // 向该容器添加英雄的生产方式(实例化的方式)
  4. $container->bind('hero', function($container, $behaviorName) {
  5. $behavior = $container->make($behaviorName);
  6. return new Hero($behavior);
  7. });
  8. // 向该容器添加行为的生产方式(实例化的方式)
  9. $container->bind('skill1', function($container) {
  10. return new Skill1;
  11. });
  12. // 同上
  13. $container->bind('skill2', function($container) {
  14. return new Skill2;
  15. });
  16. // 开始启动生产
  17. $hero1 = $container->make('hero', ['skill1']);
  18. $hero2 = $container->make('hero', ['skill2']);

在这个容器中,我们通过绑定操作,可以向超级工厂注册很多各种各样的生产脚本(可以是匿名函数、非匿名函数、类的方法),这些脚本在生产操作触发的时候就会被容器调用执行。

通过依赖注入和容器,我们彻底解决了所有对象之间的依赖关系;最重要的是,容器类也没有和英雄、行为之间有任何的依赖。另外也不需要再去手动实例化对象,还实现了按需实例化。

实际上,真正的 IoC 容器更为高级,会根据类的依赖需求,使用PHP的反射(Reflection)机制,自动在注册、绑定的一堆实例中搜寻符合的相关依赖,并自动注入到构造函数参数中去。

深入 Laravel 内核之IOC容器的更多相关文章

  1. 理解PHP 依赖注入|Laravel IoC容器

    看Laravel的IoC容器文档只是介绍实例,但是没有说原理,之前用MVC框架都没有在意这个概念,无意中在phalcon的文档中看到这个详细的介绍,感觉豁然开朗,复制粘贴过来,主要是好久没有写东西了, ...

  2. Ioc容器与laravel服务容器初探

    一.Ioc容器 某天,小J心血来潮,决定建造一艘星舰,这艘星舰要搭载"与众不同最时尚,开火肯定棒"的电磁炮.于是他写了一个星舰类: class ElectromagneticGun ...

  3. laravel核心Ioc容器

    laravel容器和依赖注入 啥是Ioc容器,方便我们实现依赖注入的一种实现,也就是说依赖注入不一定需要控制反转容器,只不过使用容器可能会方便些. laravel通过向容器中绑定接口的具体实现,可实现 ...

  4. 通过中看不中用的代码分析Ioc容器,依赖注入....

    /** * 通过生产拥有超能力的超人实例 来理解IOC容器 */ //超能力模组接口 interface SuperModuleInterface{ public function activate( ...

  5. 【转】Spring学习---Spring IoC容器的核心原理

    [原文] Spring的两个核心概念:IoC和AOP的雏形,Spring的历史变迁和如今的生态帝国. IoC和DI的基本概念 IoC(控制反转,英文含义:Inverse of Control)是Spr ...

  6. Laravel核心之IOC和Facade 架构分析1

    控制反转(Inversion of Control) 缩写为IoC 最常见的方式叫做依赖注入 简单说来,就是一个类把自己的的控制权交给另外一个对象,类间的依赖由这个对象去解决. Laravel 中的使 ...

  7. PHP 在Swoole中使用双IoC容器实现无污染的依赖注入

    简介: 容器(container)技术(可以理解为全局的工厂方法), 已经是现代项目的标配. 基于容器, 可以进一步实现控制反转, 依赖注入. Laravel 的巨大成功就是构建在它非常强大的IoC容 ...

  8. laravel5.8 IoC 容器

    网上 对容器的解释有很多,这里只是记录,搬运! 1.简单理解: 2019-10-10 11:24:09 解析 lavarel 容器 IoC 容器 作用 就是 “解耦” .“依赖注入(DI) IoC 容 ...

  9. Spring学习记录1——IoC容器

    IoC容器 1.1  IoC概述 Ioc(Inverse of Control,控制反转)是Spring容器的内核.对于软件来说,即某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定,即由 ...

随机推荐

  1. zabbix之监控Nginx连接数

    #;下载Nginx (编译的时候必须加上此选项 --with-http_stub_status_module) 官网地址:http://nginx.org/en/docs/http/ngx_http_ ...

  2. 用Myclipse开发Spring(转)

    原文链接地址是:http://www.cnitblog.com/gavinkin555/articles/35973.html 1 新建一个项目 File----->New ----->P ...

  3. Tomcat(1):安装Tomcat

    一,安装Tomcat服务器 1,下载tomcat网址: http://tomcat.apache.org/ 2,找到Download 3,下载 4:下载完成后,解压到任意目录 5:解压完成后得到目录 ...

  4. 3.2 go WaitGroup代码示例

    sync.WaitGroup提供了一种安全的多协程处理方法,内部使用race.atomic来处理,避免了资源竞争及锁的产生. 主要的方法有Add.Done.Wait,可以等待一组协程全部执行完毕后,主 ...

  5. Vector Bin Packing 华为讲座笔记

    Vector bin packing:first fit / best fit / grasp 成本:性价比 (先验) 设计评价函数: evaluation function:cosine simil ...

  6. Nginx模块之ngx_http_gzip_module

    Module ngx_http_gzip_module 该ngx_http_gzip_module模块是一个使用"gzip"方法压缩响应的过滤器.这通常有助于将传输数据的大小减少一 ...

  7. 使用ANTLR解析CSV和JSON

    再续 ANTLR专题 ,有了前面的基础,下面开始用ANTLR写一些有趣且实用的程序. CSV和JSON这两种数据格式对软件开发人员来说最熟悉不过了,一般读写CSV或JSON格式的数据都会借助现成的.比 ...

  8. java 常用类库:Math:常用min、max;floor;ceil;random;

    Math //7.取整数小于目标的最大整数(Math.floor(1.2));//8.取整数.获取大于目标的最大整数System.out.println(Math.ceil(1.2)); //18.获 ...

  9. Sublime Text3 Package Control Emmet插件安装

    https://www.cnblogs.com/carrie-hong/p/4995735.html https://www.cnblogs.com/tamato-jacob-wealllostcon ...

  10. Linux(debian7)操作基础(四)之CPU频率调整 Linux系统CPU频率调整工具使用

    在Linux中,内核的开发者定义了一套框架模型来完成CPU频率动态调整这一目的,它就是CPU Freq系统.如下为CPU的几种模式(governor参数): ondemand:系统默认的超频模式,按需 ...