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的FTP学习(二)
Oct 09 PHP
PHP控制网页过期时间的代码
Sep 28 PHP
PHP开发中常用的字符串操作函数
Feb 08 PHP
用穿越火线快速入门php面向对象
Feb 22 PHP
PHP中fwrite与file_put_contents性能测试代码
Aug 02 PHP
理解PHP中的stdClass类
Apr 18 PHP
PHP中数据库单例模式的实现代码分享
Aug 21 PHP
php实现的CSS更新类实例
Sep 22 PHP
PHP中round()函数对浮点数进行四舍五入的方法
Nov 19 PHP
基于php实现的php代码加密解密类完整实例
Oct 12 PHP
THINKPHP在添加数据的时候获取主键id的值方法
Apr 03 PHP
PHP时间相关常用函数用法示例
Jun 03 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
frename PHP 灵活文件命名函数 frename
2009/09/09 PHP
如何使用“PHP” 彩蛋进行敏感信息获取
2013/08/07 PHP
PHP CURL 多线程操作代码实例
2015/05/13 PHP
php获取本机真实IP地址实例代码
2016/03/31 PHP
Laravel实现autoload方法详解
2017/05/07 PHP
Yii框架视图、视图布局、视图数据块操作示例
2019/10/14 PHP
确保Laravel网站不会被嵌入到其他站点中的方法
2019/10/18 PHP
jquery 最简单易用的表单验证插件
2010/02/27 Javascript
javascript 日期时间 转换的方法
2013/02/21 Javascript
jQuery实现行文字链接提示效果的方法
2015/03/10 Javascript
Bootstrap每天必学之弹出框(Popover)插件
2016/04/25 Javascript
D3.js封装文本实现自动换行和旋转平移等功能
2016/10/14 Javascript
Angular.js实现多个checkbox只能选择一个的方法示例
2017/02/24 Javascript
基于vue的fullpage.js单页滚动插件
2017/03/20 Javascript
微信小程序 循环及嵌套循环的使用总结
2017/09/26 Javascript
小程序开发基础之view视图容器
2018/08/21 Javascript
一文读懂ES7中的javascript修饰器
2019/05/06 Javascript
在vue中使用eslint,配合vscode的操作
2020/11/09 Javascript
[43:48]Ti4正赛第一天 VG vs NEWBEE 2
2014/07/19 DOTA
python返回昨天日期的方法
2015/05/13 Python
Python运行提示缺少模块问题解决方案
2020/04/02 Python
Python3爬虫关于代理池的维护详解
2020/07/30 Python
css3实现3D文本悬停改变效果的示例代码
2019/01/16 HTML / CSS
CSS3中媒体查询结合rem布局适配手机屏幕
2019/06/10 HTML / CSS
深入浅析HTML5中的article和section的区别
2018/05/15 HTML / CSS
柯基袜:Corgi Socks
2017/01/26 全球购物
能否解释一下XSS cookie盗窃是什么意思
2012/06/02 面试题
调查研究项目计划书
2014/04/29 职场文书
2014单位领导班子四风对照检查材料思想汇报
2014/09/25 职场文书
教师查摆问题及整改措施
2014/10/11 职场文书
基层党支部承诺书
2015/04/30 职场文书
2016中秋节广告语
2016/01/28 职场文书
解决golang在import自己的包报错的问题
2021/04/29 Golang
tensorflow中的梯度求解及梯度裁剪操作
2021/05/26 Python
Python可变与不可变数据和深拷贝与浅拷贝
2022/04/06 Python
GoFrame gredis缓存DoVar Conn连接对象 自动序列化GoFrame gredisDo/DoVar方法Conn连接对象自动序列化/反序列化总结
2022/06/14 Golang