php设计模式-单例模式

<?php
//简单的单例
class Singleleton {
    private static $_instance = null;

    public static function getSingleleton()
    {
        if (isset(self::$_instance)) {
            return self::$_instance;
        }

        return new self();
    }
}

$instance1 = new Singleleton();
$instance2 = Singleleton::getSingleleton();
// var_dump($instance1);
// var_dump($instance2);

/*
    结果:object(Singleleton)#1 (0) { } object(Singleleton)#2 (0) { }
    问题:不能防止new实例化
*/

// 改进1:封锁new 操作
class Singleleton_1 {
    protected static $_instance = null; // 把自己的实例放在自己的一个静态属性上

    public static function getSingleleton()
    {
        if (isset(self::$_instance)) {
            return self::$_instance;
        }
        
        return new self();
    }

    protected function __construct(){}
}

// $instance1 = new Singleleton_1();
// $instance2 = Singleleton_1::getSingleleton();
// var_dump($instance1);
// var_dump($instance2);

/* 
    结果:Fatal error: Uncaught Error: Call to protected Singleleton_1::__construct() from invalid context
    结论:将构造方法设置为受保护阻止以new形式格式化
    问题: 一个类继承该类,并且使用public修饰的构造方法覆盖了父方法,则封锁__construct失败
*/

class Child extends Singleleton_1
{
    public function __construct()
    {
        
    }
}

$ins1 = new Child;
$ins2 = new Child;

// 这里用== 会得到true, 所以用===
echo $ins1 === $ins2 ? '同一个实例' : '不同实例';

/* 
    结果:不同实例
*/

// 改进2:使用final禁止覆盖构造方法
class Singleleton_2 {
    protected static $_instance = null; // 把自己的实例放在自己的一个静态属性上

    public static function getSingleleton()
    {
        if (isset(self::$_instance)) {
            return self::$_instance;
        }
        
        return new self();
    }

    // 当方法加final修饰,表示继承不能被覆盖,类加final表示不能被继承
    protected final function __construct(){} 
}

// class Child_2 extends Singleleton_2
// {
//     public function __construct()
//     {
        
//     }
// }

/**
 * 结果:Fatal error: Cannot override final method Singleleton_2::__construct()
 * 结论:能防止方法覆盖,导致单例失效
 * 问题:不能防止克隆,导致不同实例
 */


 // 改进3: 防止克隆
 class Singleleton_3 {
    protected static $_instance = null; // 把自己的实例放在自己的一个静态属性上

    public static function getSingleleton()
    {
        if (isset(self::$_instance)) {
            return self::$_instance;
        }
        
        return new self();
    }

    // 当方法加final修饰,表示继承不能被覆盖,类加final表示不能被继承
    protected final function __construct(){} 
}

$ins1 = Singleleton_3::getSingleleton();
$ins2 = clone $ins1;

echo $ins1 === $ins2 ? '同一个实例' : '不同实例'; // 不同实例

class Singleleton_4 {
    protected static $_instance = null; // 把自己的实例放在自己的一个静态属性上

    public static function getSingleleton()
    {
        if (isset(self::$_instance)) {
            return self::$_instance;
        }
        
        return new self();
    }

    // 当方法加final修饰,表示继承不能被覆盖,类加final表示不能被继承
    protected final function __construct(){} 

    // 封锁克隆
    protected final function __clone(){}
}

$ins1 = Singleleton_4::getSingleleton(); //Fatal error: Uncaught Error: Call to protected Singleleton_4::__clone() from context
$ins2 = clone $ins1;

/**
 * 结论:可以将父克隆方法设置受保护final修饰,则实例不能被克隆
 */

总结:单例模式原理是防止类被外部实例化,通过保护类的构造方法,并在类内部通过静态方式实例化来达到单例效果,一个完善单例模式应该满足一下几点

  • 类构造方法受保护,且final修饰,保证不能被外部实例化或子类覆写构造方法进行外部实例化
  • 类克隆方法受保护,且finals修饰,保证不能被克隆

 

posted @ 2020-07-19 15:36  coder_xds  阅读(133)  评论(0编辑  收藏  举报