作者简介:

       姜海强:闷骚码农,互联网行业摸爬滚打数余载,先后担任中国体育直播TV主程、网信集团先锋支付架构师、奇虎360服务器端资深开发。热爱技术,喜欢分享,热衷领域:PHP/Golang语言、面向对象设计模式、Redis、Yaf、Yii2、微服务等。

视频课程

yaf+yar微服务-腾讯课堂
yaf+yar微服务-51CTO学院
CSDN学院

Github

个人主页
swoole-boot
roach
roach-orm

QQ群:

姜海强的QQ群

公众号:

360tryst公众号

单例模式

每一种设计模式都是为了解决特定的问题,单例模式从名字就可以看出,是软件系统中只需要一个对象时使用。

如果一个类在系统中只能有一个实例,可以通过如下代码实现

  1. <?php
  2. /**
  3. * Class Singleton
  4. * @datetime 2020/7/12 10:08 PM
  5. * @author roach
  6. * @email jhq0113@163.com
  7. */
  8. class Singleton
  9. {
  10. /**
  11. * @var Singleton
  12. * @datetime 2020/7/12 10:09 PM
  13. * @author roach
  14. * @email jhq0113@163.com
  15. */
  16. private static $_instance;
  17. /**
  18. * Singleton constructor.
  19. */
  20. private function __construct(){}
  21. /**
  22. * @datetime 2020/7/12 10:08 PM
  23. * @author roach
  24. * @email jhq0113@163.com
  25. */
  26. private function __clone(){}
  27. /**
  28. * @return Singleton
  29. * @datetime 2020/7/12 10:08 PM
  30. * @author roach
  31. * @email jhq0113@163.com
  32. */
  33. public static function getInstance()
  34. {
  35. if(is_null(self::$_instance)) {
  36. self::$_instance = new self();
  37. }
  38. return self::$_instance;
  39. }
  40. }

以上代码是一个标准单例模式的实现,其中的关键实现有一下三点

  • 静态私有实例属性
  • 私有化构造函数和克隆函数
  • 公有的静态获取实例方法

但是在实际的开发中,我们一般不会用以上代码来实现单例,原因有以下两点

  • 以上代码实现单例,在创建对象时,用的是new self(),如果其他类也想实现单例必须复制以上代码
  • 该单例模式在调用时是通过静态方法调用,属于直接依赖关系

上面第一个问题,我们可以通过静态延时绑定来解决,创建对象时,我们将new self()改为new static(),这样如果有其他类也想实现单例,直接继承该类就好,但是第二个问题还是无法解决

那么第二个问题怎么解决呢?

在实际项目开发中一般使用依赖注入方式实现,单独提供一个DI容器,靠配置注入方式实现,这样就不是直接依赖关系了。下面提供一个简单的DI容器类

  1. <?php
  2. /**
  3. * Class Di
  4. * @datetime 2020/7/12 10:29 PM
  5. * @author roach
  6. * @email jhq0113@163.com
  7. */
  8. class Di
  9. {
  10. /**
  11. * @var array
  12. * @datetime 2020/7/12 10:23 PM
  13. * @author roach
  14. * @email jhq0113@163.com
  15. */
  16. private static $_pool = [];
  17. /**
  18. * @param string $key
  19. * @param array $config
  20. * @datetime 2020/7/12 10:24 PM
  21. * @author roach
  22. * @email jhq0113@163.com
  23. */
  24. public static function set($key, $config = [])
  25. {
  26. self::$_pool[ $key ] = $config;
  27. }
  28. /**
  29. * @param string $key
  30. * @return mixed|null
  31. * @throws Exception
  32. * @datetime 2020/7/12 10:29 PM
  33. * @author roach
  34. * @email jhq0113@163.com
  35. */
  36. public static function get($key)
  37. {
  38. if(!isset(self::$_pool[ $key ])) {
  39. return null;
  40. }
  41. $config = self::$_pool[ $key ];
  42. if(isset($config['class'])) {
  43. $class = $config['class'];
  44. unset($config['class']);
  45. if(!class_exists($class)) {
  46. throw new \Exception($class.'不存在');
  47. }
  48. $object = new $class();
  49. foreach ($config as $property => $value) {
  50. $object->$property = $value;
  51. }
  52. self::$_pool[ $key ] = $object;
  53. }
  54. return self::$_pool[ $key ];
  55. }
  56. }

下面是使用容器创建一个User类,并且注入了nickname属性

  1. <?php
  2. /**
  3. * Class User
  4. * @datetime 2020/7/12 10:30 PM
  5. * @author roach
  6. * @email jhq0113@163.com
  7. */
  8. class User
  9. {
  10. /**
  11. * @var string
  12. * @datetime 2020/7/12 10:30 PM
  13. * @author roach
  14. * @email jhq0113@163.com
  15. */
  16. public $nickname;
  17. }
  18. Di::set('user', [
  19. 'class' => User::class,
  20. 'nickname' => uniqid()
  21. ]);
  22. echo Di::get('user')->nickname.PHP_EOL;

通过以上容器实现单例,解决了上面提到的第二个问题,我们每个类如果想在项目中实现单例,不用特殊的实现,也不用继承某个类,我们在调用端通过容器的get方法得到对象,这里的依赖关系属于依赖注入,get方法里的对象是通过配置注入进来的,并且全局是一个单例模式。

作者开源了一个功能更丰富的容器,封装在了jhq0113/roach中,我们可以通过composer方式安装,代码开源地址如下

https://github.com/jhq0113/roach

jhq0113/roach使用简单,代码精简,整个代码库纯代码大小为60K,composer安装方式

  1. composer require jhq0113/roach

QQ群:

姜海强的QQ群

公众号:

360tryst公众号