单例

单例模式

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

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

<?php
/**
 * Class Singleton
 * @datetime 2020/7/12 10:08 PM
 * @author roach
 * @email jhq0113@163.com
 */
class Singleton
{
    /**
     * @var Singleton
     * @datetime 2020/7/12 10:09 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    private static $_instance;

    /**
     * Singleton constructor.
     */
    private function __construct(){}

    /**
     * @datetime 2020/7/12 10:08 PM
     * @author roach
     * @email jhq0113@163.com
     */
    private function __clone(){}

    /**
     * @return Singleton
     * @datetime 2020/7/12 10:08 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public static function getInstance()
    {
        if(is_null(self::$_instance)) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }
}

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

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

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

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

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

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

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

<?php
/**
 * Class Di
 * @datetime 2020/7/12 10:29 PM
 * @author roach
 * @email jhq0113@163.com
 */
class Di
{
    /**
     * @var array
     * @datetime 2020/7/12 10:23 PM
     * @author roach
     * @email jhq0113@163.com
     */
    private static $_pool = [];

    /**
     * @param string $key
     * @param array  $config
     * @datetime 2020/7/12 10:24 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public static function set($key, $config = [])
    {
        self::$_pool[ $key ] = $config;
    }

    /**
     * @param string $key
     * @return mixed|null
     * @throws Exception
     * @datetime 2020/7/12 10:29 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public static function get($key)
    {
        if(!isset(self::$_pool[ $key ])) {
            return null;
        }

        $config = self::$_pool[ $key ];
        if(isset($config['class'])) {
            $class = $config['class'];
            unset($config['class']);
            if(!class_exists($class)) {
                throw new \Exception($class.'不存在');
            }

            $object = new $class();
            foreach ($config as $property => $value) {
                $object->$property = $value;
            }

            self::$_pool[ $key ] = $object;
        }

        return self::$_pool[ $key ];
    }
}

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

<?php
/**
 * Class User
 * @datetime 2020/7/12 10:30 PM
 * @author roach
 * @email jhq0113@163.com
 */
class User
{
    /**
     * @var string
     * @datetime 2020/7/12 10:30 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public $nickname;
}

Di::set('user', [
    'class'    => User::class,
    'nickname' => uniqid()
]);

echo Di::get('user')->nickname.PHP_EOL;

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

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

https://github.com/jhq0113/roach

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

composer require jhq0113/roach

学习更多内容: https://404.360tryst.com

我的视频课程: https://edu.csdn.net/course/detail/9933

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苍穹0113

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值