PHP实现单例模式最安全的做法


Posted in PHP onJune 13, 2014

作为一种常用的设计模式,单例模式被广泛的使用。那么如何设计一个单例才是最好的呢?

通常我们会这么写,网上能搜到的例子也大部分是这样:

class A
{
    protected static $_instance = null;
    protected function __construct()
    {
        //disallow new instance
    }
    protected function __clone(){
        //disallow clone
    }
    public function getInstance()
    {
        if (self::$_instance === null) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }
}
class B extends A
{
    protected static $_instance = null;
}
$a = A::getInstance();
$b = B::getInstance();
var_dump($a === $b);

将__construct方法设为私有,可以保证这个类不被其他人实例化。但这种写法一个显而易见的问题是:代码不能复用。比如我们在一个一个类继承A:
class B extends A
{
    protected static $_instance = null;
}
$a = A::getInstance();
$b = B::getInstance();
var_dump($a === $b);

上面的代码会输出:
bool(true)

问题出在self上,self的引用是在类被定义时就决定的,也就是说,继承了B的A,他的self引用仍然指向A。为了解决这个问题,在PHP 5.3中引入了后期静态绑定的特性。简单说是通过static关键字来访问静态的方法或者变量,与self不同,static的引用是由运行时决定。于是简单改写一下我们的代码,让单例模式可以复用。
class C
{
    protected static $_instance = null;
    protected function __construct()
    {
    }
    protected function __clone()
    {
        //disallow clone
    }
    public function getInstance()
    {
        if (static::$_instance === null) {
            static::$_instance = new static;
        }
        return static::$_instance;
    } 
}
class D extends C
{
    protected static $_instance = null;
}
$c = C::getInstance();
$d = D::getInstance();
var_dump($c === $d);

以上代码输出:
bool(false)

这样,简单的继承并重新初始化$_instance变量就能实现单例模式。注意上面的方法只有在PHP 5.3中才能使用,对于之前版本的PHP,还是老老实实为每个单例类写一个getInstance()方法吧。

需要提醒的是,PHP中单例模式虽然没有像Java一样的线程安全问题,但是对于有状态的类,还是要小心的使用单例模式。单例模式的类会伴随PHP运行的整个生命周期,对于内存也是一种开销。

PHP 相关文章推荐
聊天室php&mysql(五)
Oct 09 PHP
修改php.ini实现Mysql导入数据库文件最大限制的修改方法
Dec 11 PHP
php实现网站插件机制的方法
Nov 10 PHP
PHP数据类型的总结分析
Jun 13 PHP
PHP学习笔记(二) 了解PHP的基本语法以及目录结构
Aug 04 PHP
destoon实现调用自增数字从1开始的方法
Aug 21 PHP
基于OpenCart 开发支付宝,财付通,微信支付参数错误问题
Oct 01 PHP
PHP入门教程之操作符与控制结构流程详解
Sep 09 PHP
PHP 记录访客的浏览信息方法
Jan 29 PHP
php打开本地exe程序,js打开本地exe应用程序,并传递相关参数方法
Feb 06 PHP
php成功操作redis cluster集群的实例教程
Jan 13 PHP
关于Laravel参数验证的一些疑与惑
Nov 19 PHP
PHP5.5和之前的版本empty函数的不同之处
Jun 13 #PHP
PHP输出英文时间日期的安全方法(RFC 1123格式)
Jun 13 #PHP
PHP中多维数组的foreach遍历示例
Jun 13 #PHP
PHP根据传来的16进制颜色代码自动改变背景颜色
Jun 13 #PHP
php smarty truncate UTF8乱码问题解决办法
Jun 13 #PHP
PHP根据传入参数合并多个JS和CSS文件的简单实现
Jun 13 #PHP
Codeigniter上传图片出现“You did not select a file to upload”错误解决办法
Jun 12 #PHP
You might like
ecshop后台编辑器替换成ueditor编辑器
2015/03/03 PHP
Yii框架引用插件和ckeditor中body与P标签去除的方法
2017/01/19 PHP
ThinkPHP实现分页功能
2017/04/28 PHP
php实现姓名根据首字母排序的类与方法(实例代码)
2018/05/16 PHP
PHP中rename()函数的妙用讲解
2019/02/28 PHP
PHP使用Redis实现Session共享的实现示例
2019/05/12 PHP
PHP ob缓存以及ob函数原理实例解析
2020/11/13 PHP
常用参考资料(手册)下载或者链接
2006/07/22 Javascript
Prototype1.5 rc2版指南最后一篇之Position
2007/01/10 Javascript
javascript 嵌套的函数(作用域链)
2010/03/15 Javascript
利用NodeJS和PhantomJS抓取网站页面信息以及网站截图
2013/11/18 NodeJs
js使用for循环查询数组中是否存在某个值
2014/08/12 Javascript
图片旋转、鼠标滚轮缩放、镜像、切换图片js代码
2020/12/13 Javascript
JS+CSS实现的漂亮渐变背景特效代码(6个渐变效果)
2016/03/25 Javascript
Bootstrap每天必学之警告框插件
2016/04/26 Javascript
微信小程序  生命周期详解
2016/10/27 Javascript
jQuery ajax 当async为false时解决同步操作失败的问题
2016/11/18 Javascript
JS 实现可停顿的垂直滚动实例代码
2016/11/23 Javascript
详解angular用$sce服务来过滤HTML标签
2017/04/11 Javascript
vue音乐播放器插件vue-aplayer的配置及其使用实例详解
2017/07/10 Javascript
vue+egg+jwt实现登录验证的示例代码
2019/05/18 Javascript
vue2.0 获取从http接口中获取数据,组件开发,路由配置方式
2019/11/04 Javascript
深入解析Python中的urllib2模块
2015/11/13 Python
Python实现SSH远程登陆,并执行命令的方法(分享)
2017/05/08 Python
python爬虫获取小区经纬度以及结构化地址
2018/12/30 Python
淘宝秒杀python脚本 扫码登录版
2019/09/19 Python
numpy数组做图片拼接的实现(concatenate、vstack、hstack)
2019/11/08 Python
flask 实现上传图片并缩放作为头像的例子
2020/01/09 Python
印度尼西亚最大和最全面的网络商城:Blibli.com
2017/10/04 全球购物
证婚人经典证婚词
2014/01/09 职场文书
三八妇女节超市活动方案
2014/08/18 职场文书
班级班风口号大全
2015/12/25 职场文书
2016年小学推普宣传周活动总结
2016/04/06 职场文书
诚信高考倡议书
2019/06/24 职场文书
创业计划书之餐饮馄饨店
2019/07/18 职场文书
ant design charts 获取后端接口数据展示
2022/05/25 Javascript