<?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修饰,保证不能被克隆