[关闭]
@Chiang 2019-12-11T18:46:09.000000Z 字数 11897 阅读 665

工厂模式、服务容器(IocContainer)

设计模式 Laravel


  • 工厂模式作为一种创建型设计模式,是我们最常用的实例化对象模式,是用工厂方法代替new操作的一种模式
  • 工厂模式的目的是为了最大限度的减少依赖,实现解耦,把类的实例对象通过工厂去实现,实现可扩展性
  • IOC , DI 的实现依据
  • reflection 反射, 类型约束, 服务容器(IocContainer)

写在前面我的理解:

  • 简单工厂模式: 添加新产品(扩展)相当于在工厂里再添加一条生产线(属性)专门生产
  • 工厂方法模式: 添加新产品(扩展)相当于再建一个子工厂(子类)专门生产
  • 抽象工厂模式: 在多个产品中寻找共性,抽象出来在现有工厂中添加生产线实现

简单工厂模式(静态方法工厂模式)

  • 帮助封装
  • 解耦
  • 违反了开发封闭原则(开闭原则, 对修改封闭,对扩展开放,实现热插拔),有新的扩展的时候还是要到工厂类里去修改
  • 被创建的实例对象,可以是接口抽象类也可以是具体的类

应用场景(不确定有多少种操作)

  • 运算操作(+-*/)
  • 支付操作(alipay,wechatpay,paypal)
  • 缓存支持(MySQL,Redis,file)

常规操作

同一种业务类型的功能,独立实现,独立调用

  1. <?php
  2. /**
  3. * 不用设计模式时
  4. *
  5. * @author Luffy (lufei@swoole.com)
  6. * @date 2019/9/19
  7. * @copyright Swoole, Inc.
  8. * @package sy-records/design-patterns
  9. */
  10. class WeChatPay
  11. {
  12. public function pay()
  13. {
  14. }
  15. }
  16. class AliPay
  17. {
  18. public function pay()
  19. {
  20. }
  21. }
  22. $we = new WeChatPay();
  23. $we->pay();
  24. $ali = new AliPay();
  25. $ali->pay();

工厂设计模式

代码结构

简单工厂模式UML

实现原理

1.通过接口定义了支付业务逻辑
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/15
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\SimpleFactory;
  8. /**
  9. * Interface PaySimpleFactory
  10. * @package Luffy\DesignPatterns\Factory\SimpleFactory
  11. */
  12. interface PaySimpleFactory
  13. {
  14. public function pay();
  15. }
2.支付宝支付类,微信支付类去实现支付接口
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/15
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\SimpleFactory;
  8. /**
  9. * Class AliPay
  10. * @package Luffy\DesignPatterns\Factory\SimpleFactory
  11. */
  12. class AliPay implements PaySimpleFactory
  13. {
  14. public function pay()
  15. {
  16. echo "我是AliPay\n";
  17. }
  18. }
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/15
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\SimpleFactory;
  8. /**
  9. * Class WeChatPay
  10. * @package Luffy\DesignPatterns\Factory\SimpleFactory
  11. */
  12. class WeChatPay implements PaySimpleFactory
  13. {
  14. public function pay()
  15. {
  16. echo "我是WeChatPay\n";
  17. }
  18. }
3.简单工厂,静态工厂去实例化支付类
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/15
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\SimpleFactory;
  8. /**
  9. * Class SimpleFactory
  10. * @package Luffy\DesignPatterns\Factory\SimpleFactory
  11. */
  12. class SimpleFactory
  13. {
  14. public function pay($operate)
  15. {
  16. switch ($operate) {
  17. case 'WeChatPay':
  18. $result = new WeChatPay();
  19. break;
  20. case 'AliPay':
  21. $result = new AliPay();
  22. break;
  23. default:
  24. throw new \InvalidArgumentException('暂不支持的支付方式');
  25. }
  26. return $result;
  27. }
  28. }
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/15
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\SimpleFactory;
  8. /**
  9. * Class StaticFactory
  10. * @package Luffy\DesignPatterns\Factory\SimpleFactory
  11. */
  12. class StaticFactory
  13. {
  14. public static function pay($operate)
  15. {
  16. switch ($operate) {
  17. case 'WeChatPay':
  18. $result = new WeChatPay();
  19. break;
  20. case 'AliPay':
  21. $result = new AliPay();
  22. break;
  23. default:
  24. throw new \InvalidArgumentException('暂不支持的支付方式');
  25. }
  26. return $result;
  27. }
  28. }
4.客户端实例化工厂类,传入相应参数,从而获取所需支付对象实例

这样客户端只需要依赖工厂类就可以调用多个支付方法

  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/15
  5. * Email: lufei@swoole.com
  6. */
  7. include __DIR__ . '/../../vendor/autoload.php';
  8. // 调用
  9. use Luffy\DesignPatterns\Factory\SimpleFactory\SimpleFactory;
  10. use Luffy\DesignPatterns\Factory\SimpleFactory\StaticFactory;
  11. $pay1 = StaticFactory::pay("WeChatPay");
  12. $pay1->pay();
  13. $factory = new SimpleFactory();
  14. $pay2 = $factory->pay("AliPay");
  15. $pay2->pay();

接口,抽象类的区别

  • 抽象类要被子类继承,接口要被类实现。
  • 接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。
  • 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
  • 接口是设计的结果,抽象类是重构的结果。
  • 抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别最高。
  • 抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。
  • 抽象类主要用来抽象类别,接口主要用来抽象功能。

工厂方法模式

  • 解决了简单工厂模式中的开闭原则
  • 类的实例化延迟到了子类
  • 存在大量的子类工厂

应用场景

要实例化的对象充满不确定,也有可能改变,不确定有多少种类进行实例化

代码结构

工厂方法模式UML

实现原理

具体的工厂类和具体的支付类实现一一对应

1.通过接口定义了支付业务逻辑

  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/19
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\FactoryMethod;
  8. interface createPay
  9. {
  10. public function pay();
  11. }
支付宝支付类,微信支付类去实现支付接口
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/15
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\FactoryMethod;
  8. /**
  9. * Class AliPay
  10. * @package Luffy\DesignPatterns\Factory\FactoryMethod
  11. */
  12. class AliPay implements createPay
  13. {
  14. public function pay()
  15. {
  16. echo "我是AliPay\n";
  17. }
  18. }
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/15
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\FactoryMethod;
  8. /**
  9. * Class WeChatPay
  10. * @package Luffy\DesignPatterns\Factory\FactoryMethod
  11. */
  12. class WeChatPay implements createPay
  13. {
  14. public function pay()
  15. {
  16. echo "我是WeChatPay\n";
  17. }
  18. }

2.通过接口定义了工厂方法

  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\FactoryMethod;
  8. /**
  9. * Interface CreatePayFactoryMethod
  10. * @package Luffy\DesignPatterns\Factory\FactoryMethod
  11. */
  12. interface CreatePayFactoryMethod
  13. {
  14. public function create();
  15. }
子类实现父类接口,实例化对应的支付对象
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\FactoryMethod;
  8. class FactoryAli implements CreatePayFactoryMethod
  9. {
  10. function create()
  11. {
  12. // TODO: Implement create() method.
  13. return new AliPay();
  14. }
  15. }
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\FactoryMethod;
  8. class FactoryWeChat implements CreatePayFactoryMethod
  9. {
  10. function create()
  11. {
  12. // TODO: Implement create() method.
  13. return new WeChatPay();
  14. }
  15. }

3.客户端通过需求实例化对应的子类工厂,从而获取对应的支付对象实例

  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. include __DIR__ . '/../../vendor/autoload.php';
  8. use Luffy\DesignPatterns\Factory\FactoryMethod\FactoryAli;
  9. use Luffy\DesignPatterns\Factory\FactoryMethod\FactoryWeChat;
  10. /**
  11. * 具体生产对象并执行对象方法测试
  12. */
  13. class Client
  14. {
  15. public function index()
  16. {
  17. $factory = new FactoryAli();
  18. $ali = $factory->create();
  19. $ali->pay();
  20. $factory = new FactoryWeChat();
  21. $wechat = $factory->create();
  22. $wechat->pay();
  23. }
  24. }
  25. // 执行
  26. $demo = new Client;
  27. $demo->index();

抽象工厂模式

  • 产品客户端完全分离
  • 重复工作多
  • 利用简单工厂来优化抽象工厂
  • 利用反射来优化抽象工厂
  • 在现有产品上添加新的产品族是比较方便的(寻找产品共性抽象提取)
  • 是在简单工厂模式上提取产品共性的更加抽象化而已

应用场景

不确定对应的实例化对象的时候

代码结构

抽象工厂UML

实现原理

1.寻找产品共性与异性,抽象化不同产品类的共性,提取方法,实现工厂接口

  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\AbstractFactory;
  8. /**
  9. * 工厂接口
  10. * Interface Factory
  11. * @package Luffy\DesignPatterns\Factory\AbstractFactory
  12. */
  13. interface Factory
  14. {
  15. /**
  16. * @return mixed
  17. */
  18. public function createHuMan();
  19. /**
  20. * @return mixed
  21. */
  22. public function createMonkey();
  23. }
子类实现父类工厂接口
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\AbstractFactory;
  8. /**
  9. * 具体工厂类
  10. * Class ManFactory
  11. * @package Luffy\DesignPatterns\Factory\AbstractFactory
  12. */
  13. class ManFactory implements Factory
  14. {
  15. /**
  16. * @return Man|mixed
  17. */
  18. public function createHuMan()
  19. {
  20. return new Man();
  21. }
  22. public function createMonkey()
  23. {
  24. return new ApeMonkey();
  25. }
  26. }
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\AbstractFactory;
  8. /**
  9. * Class WoManFactory
  10. * @package Luffy\DesignPatterns\Factory\AbstractFactory
  11. */
  12. class WoManFactory implements Factory
  13. {
  14. public function createHuMan()
  15. {
  16. return new WoMan();
  17. }
  18. public function createMonkey()
  19. {
  20. return new FemaleMonkey();
  21. }
  22. }

2.通过接口定义产品1的方法

  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\AbstractFactory;
  8. /**
  9. * HuMan 产品父类
  10. * Interface HuMan
  11. * @package Luffy\DesignPatterns\Factory\AbstractFactory
  12. */
  13. interface HuMan
  14. {
  15. /**
  16. * @return mixed
  17. */
  18. public function say();
  19. }
不同子类实现父类接口
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\AbstractFactory;
  8. /**
  9. * 具体产品类
  10. * Class Man
  11. * @package Luffy\DesignPatterns\Factory\AbstractFactory
  12. */
  13. class Man implements HuMan
  14. {
  15. public function say()
  16. {
  17. echo "我是一个男人 👨 \n";
  18. }
  19. }
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\AbstractFactory;
  8. class WoMan implements HuMan
  9. {
  10. public function say()
  11. {
  12. echo "我是一个女人 👩 \n";
  13. }
  14. }

3.通过接口定义产品2的方法

  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\AbstractFactory;
  8. /**
  9. * Interface Monkey
  10. * @package Luffy\DesignPatterns\Factory\AbstractFactory
  11. */
  12. interface Monkey
  13. {
  14. /**
  15. * @return mixed
  16. */
  17. public function say();
  18. }
不同子类实现父类接口
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\AbstractFactory;
  8. /**
  9. * 具体产品类
  10. * Class ApeMonkey
  11. * @package Luffy\DesignPatterns\Factory\AbstractFactory
  12. */
  13. class ApeMonkey implements Monkey
  14. {
  15. public function say()
  16. {
  17. echo "我是一只猿猴 🐒 \n";
  18. }
  19. }
  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\AbstractFactory;
  8. /**
  9. * Class FemaleMonkey
  10. * @package Luffy\DesignPatterns\Factory\AbstractFactory
  11. */
  12. class FemaleMonkey implements Monkey
  13. {
  14. public function say()
  15. {
  16. echo "我是一只母猴 🐵 \n";
  17. }
  18. }

4.客户端通过需求实例化对应的子类工厂,从而获取对应的产品对象实例

  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. use Luffy\DesignPatterns\Factory\AbstractFactory\ManFactory;
  8. use Luffy\DesignPatterns\Factory\AbstractFactory\WoManFactory;
  9. include __DIR__ . '/../../vendor/autoload.php';
  10. class Client
  11. {
  12. public function index()
  13. {
  14. $factory = new ManFactory();
  15. $man = $factory->createHuMan();
  16. $man->say();
  17. $monkey = $factory->createMonkey();
  18. $monkey->say();
  19. echo "*****************\n";
  20. $factory = new WoManFactory();
  21. $woman = $factory->createHuMan();
  22. $woman->say();
  23. $woman_monkey = $factory->createMonkey();
  24. $woman_monkey->say();
  25. }
  26. }
  27. // 执行
  28. $demo = new Client;
  29. $demo->index();

利用简单工厂来优化抽象工厂

代码结构

利用简单工厂来优化抽象工厂UML

实现原理

不需要工厂子类了,只需要在工厂类中修改就好了

  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/16
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\OptimizeAbstractFactory;
  8. /**
  9. * Class Factory
  10. * @package Luffy\DesignPatterns\Factory\OptimizeAbstractFactory
  11. */
  12. class Factory
  13. {
  14. public $create = "Man";
  15. public function __construct()
  16. {
  17. $config = include 'config.php';
  18. $this->create = $config['create'];
  19. }
  20. /**
  21. * @return mixed
  22. */
  23. public function createHuMan()
  24. {
  25. switch ($this->create) {
  26. case 'Man':
  27. $human = new Man();
  28. break;
  29. case 'WoMan':
  30. $human = new WoMan();
  31. break;
  32. default:
  33. throw new \InvalidArgumentException("你是个什么 👿 人类?\n");
  34. }
  35. return $human;
  36. }
  37. /**
  38. * @return mixed
  39. */
  40. public function createMonkey()
  41. {
  42. switch ($this->create) {
  43. case 'Man':
  44. $monkey = new ManMonkey();
  45. break;
  46. case 'WoMan':
  47. $monkey = new WoManMonkey();
  48. break;
  49. default:
  50. throw new \InvalidArgumentException("你是个什么 👿 猴子?\n");
  51. }
  52. return $monkey;
  53. }
  54. }

利用反射来优化抽象工厂(符合开闭原则)

代码结构

利用反射来优化抽象工厂UML

实现原理

不需要工厂子类了,只需要在工厂类中修改就好了

  1. <?php
  2. /**
  3. * User: lufei
  4. * Date: 2019/9/19
  5. * Email: lufei@swoole.com
  6. */
  7. namespace Luffy\DesignPatterns\Factory\OptimizeAbstractFactory;
  8. use ReflectionClass;
  9. use ReflectionException;
  10. class FactoryWithReflection
  11. {
  12. /**
  13. * @var mixed|string
  14. */
  15. public $create = "Man";
  16. /**
  17. * FactoryWithReflection constructor.
  18. */
  19. public function __construct()
  20. {
  21. $config = include 'config1.php';
  22. $this->create = $config['create'];
  23. }
  24. /**
  25. * @return mixed
  26. */
  27. public function createHuMan()
  28. {
  29. $className = __NAMESPACE__ .'\\'. $this->create;
  30. try {
  31. $class = new ReflectionClass($className);
  32. $human = $class->newInstance();
  33. } catch (ReflectionException $Exception) {
  34. throw new \InvalidArgumentException($Exception->getMessage());
  35. }
  36. return $human;
  37. }
  38. /**
  39. * @return mixed
  40. */
  41. public function createMonkey()
  42. {
  43. $className = __NAMESPACE__ .'\\'. $this->create . "Monkey";
  44. try {
  45. $class = new ReflectionClass($className);
  46. $monkey = $class->newInstance();
  47. } catch (ReflectionException $Exception) {
  48. throw new \InvalidArgumentException($Exception->getMessage());
  49. }
  50. return $monkey;
  51. }
  52. }

反射

类的反射和依赖注入

类型约束

IOC,DI, 服务容器(IocContainer)

服务容器中有两个概念控制反转(IOC)和依赖注入(DI):
依赖注入和控制反转是对同一件事情的不同描述,它们描述的角度不同。依赖注入是从应用程序的角度在描述,应用程序依赖容器创建并注入它所需要的外部资源。而控制反转是从容器的角度在描述,容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。


  • IoC(控制反转)
  • DI(依赖注入)
  1. /**
  2. * 摘自Inspirer网站
  3. * https://www.insp.top/article/learn-laravel-container
  4. */
  5. class Container
  6. {
  7. protected $binds;
  8. protected $instances;
  9. public function bind($abstract, $concrete)
  10. {
  11. if ($concrete instanceof Closure) {
  12. $this->binds[$abstract] = $concrete;
  13. } else {
  14. $this->instances[$abstract] = $concrete;
  15. }
  16. }
  17. public function make($abstract, $parameters = [])
  18. {
  19. if (isset($this->instances[$abstract])) {
  20. return $this->instances[$abstract];
  21. }
  22. array_unshift($parameters, $this);
  23. return call_user_func_array($this->binds[$abstract], $parameters);
  24. }
  25. }
  26. // 创建一个容器(后面称作超级工厂)
  27. $container = new Container;
  28. // 向该 超级工厂 添加 超人 的生产脚本
  29. $container->bind('superman', function($container, $moduleName) {
  30. return new Superman($container->make($moduleName));
  31. });
  32. // 向该 超级工厂 添加 超能力模组 的生产脚本
  33. $container->bind('xpower', function($container) {
  34. return new XPower;
  35. });
  36. // 同上
  37. $container->bind('ultrabomb', function($container) {
  38. return new UltraBomb;
  39. });
  40. // ****************** 华丽丽的分割线 **********************
  41. // 开始启动生产
  42. $superman_1 = $container->make('superman', ['xpower']);
  43. $superman_2 = $container->make('superman', ['ultrabomb']);
  44. $superman_3 = $container->make('superman', ['xpower']);

参考资料:
swoole微课堂
GitHub
类的反射和依赖注入
php.net反射(reflection)
Inspirer: laravel 学习笔记 —— 神奇的服务容器
服务容器(IocContainer)
php.net 类型约束
call_user_func_array

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注