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作的文本留言本的例子(四)
Oct 09 PHP
一个php作的文本留言本的例子(三)
Oct 09 PHP
discuz 首页四格:最新话题+最新回复+热门话题+精华文章插件
Aug 19 PHP
mysql limit查询优化分析
Nov 12 PHP
PHP的异常处理类Exception的使用及说明
Jun 13 PHP
ThinkPHP的URL重写问题
Jun 22 PHP
PHP实现全角字符转为半角方法汇总
Jul 09 PHP
php生成4位数字验证码的实现代码
Nov 23 PHP
php curl中gzip的压缩性能测试实例分析
Nov 08 PHP
php生成复杂验证码(倾斜,正弦干扰线,黏贴,旋转)
Mar 12 PHP
Yii框架响应组件用法实例分析
Sep 04 PHP
yii框架结合charjs统计上一年与当前年数据的方法示例
Apr 04 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
thinkphp四种url访问方式详解
2014/11/28 PHP
LaravelS通过Swoole加速Laravel/Lumen详解
2018/03/02 PHP
jQuery 通过事件委派一次绑定多种事件,以减少事件冗余
2010/06/30 Javascript
Javascript实现DIV滚动自动滚动到底部的代码
2012/03/01 Javascript
JavaScript省市联动实现代码
2014/02/15 Javascript
jQuery实现首页顶部可伸缩广告特效代码
2015/04/15 Javascript
JS的框架Polymer中的dom-if和is属性使用说明
2015/07/29 Javascript
解决JavaScript数字精度丢失问题的方法
2015/12/03 Javascript
详细解读Jquery各Ajax函数($.get(),$.post(),$.ajax(),$.getJSON())
2016/08/15 Javascript
RequireJS简易绘图程序开发
2016/10/28 Javascript
JS简单实现移动端日历功能示例
2016/12/28 Javascript
JavaScript利用闭包实现模块化
2017/01/13 Javascript
详解在Vue中使用TypeScript的一些思考(实践)
2018/07/06 Javascript
layui prompt 设置允许空白提交的方法
2019/09/24 Javascript
微信小程序开发之转发分享功能
2019/10/22 Javascript
Vuex中实现数据状态查询与更改
2019/11/08 Javascript
vue 子组件修改data或调用操作
2020/08/07 Javascript
原生js实现贪吃蛇游戏
2020/10/26 Javascript
jQuery实现本地存储
2020/12/22 jQuery
Ruby元编程基础学习笔记整理
2016/07/02 Python
python 实现一个贴吧图片爬虫的示例
2017/10/12 Python
浅谈python配置与使用OpenCV踩的一些坑
2018/04/02 Python
python实现几种归一化方法(Normalization Method)
2019/07/31 Python
NBA欧洲商店(法国):NBA Europe Store FR
2016/10/19 全球购物
Bergfreunde丹麦:登山装备网上零售商
2017/02/26 全球购物
印度尼西亚最大的电商平台:Tokopedia(印尼版淘宝)
2017/12/02 全球购物
下列程序在32位linux或unix中的结果是什么
2014/03/25 面试题
海飞丝广告词
2014/03/20 职场文书
中国文明网向国旗敬礼寄语大全
2014/09/27 职场文书
求职自我推荐信
2015/03/24 职场文书
违纪开除通知书
2015/04/25 职场文书
Python数据分析之pandas函数详解
2021/04/21 Python
HTML中的表单元素介绍
2022/02/28 HTML / CSS
Mysql事务索引知识汇总
2022/03/17 MySQL
springcloud整合seata
2022/05/20 Java/Android
Java中生成微信小程序太阳码的实现方案
2022/06/01 Java/Android