对某一类实现单例模式,为了节省代码,避免每个类都重复书写一样的代码,可以为所有实现单例的类定义一个父类:
abstract class Common{ protected static $_instance; protected function __construct(){} public static function instance(): Common {} } class Auth extends Common{} class Ip extends Common{}
思考下,instance静态方法怎么书写,按这样写:
if(is_null(self::$_instance)) self::$_instance = new self(); return self::$_instance;
肯定是不对的,不管是执行 Auth::instance() 还是 Ip::instance() ,“self”永远指的是Common类,因为Common类是抽象类,不能实例化,会报错的。《类的范围解析操作符》
Cannot instantiate abstract class app\extend\Common
把“self”改成“static”
if(is_null(self::$_instance)) self::$_instance = new static(); return self::$_instance;
当执行 Auth::instance() 或 Ip::instance() 时,貌似是可以了
实现单例了,但如果执行一次脚本,先后执行了 Auth::instance() 、 Ip::instance() ,就会发现新的问题, Ip::instance() 返回的是Auth对象,具体的原因一分析就能知道,写的代码逻辑是有错误的:
self::$_instance 等同于 Common::$_instance ,Auth类、Ip类执行instance静态方法,赋值获取的都是Common父类的$_instance静态属性,所以执行 Ip::instance() , self::$_instance 已被提前执行的 Auth::instance() 赋值为Auth对象实例。
怎么去改呢?如果这样,把 self::$_instance 改成 static::$_instance :
if( is_null(static::$_instance) ) static::$_instance = new static(); return static::$_instance;
其实也不行,虽然在Auth类 static::$_instance 等同于 Auth::$_instance ,在Ip类 static::$_instance 等同于 Ip::$_instance ,可Auth类、Ip类并没有$_instance静态属性,在《第七章:第9节 PHP面向对象——重写》有过总结“子类调用父类的方法访问成员变量,子类没有则访问父类的成员变量;”所以 static::$_instance 最终等同于 Common::$_instance 。
如果所有的子类都有自己的$_instance静态属性,那就不会出现错误了。最终代码:
abstract class Common{ protected static $_instance; protected function __construct(){} public static function instance(): Common { if( is_null(static::$_instance) ) static::$_instance = new static(); return static::$_instance; } } class Auth extends Common{ protected static $_instance; } class Ip extends Common{ protected static $_instance; }
每个子类都需要加$_instance静态属性才可以,这样的代码设计很不好,如果有些类忘记加了,岂不是又会出现错误。代码可以这样优化:
abstract class Common{ protected static $_instance; protected function __construct(){} public static function instance(): Common { if( empty(self::$_instance[static::class]) ) self::$_instance[static::class] = new static(); return self::$_instance[static::class]; } } class Auth extends Common{} class Ip extends Common{}
self::$_instance 也可以改成 static::$_instance 。