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中MVC的开发经验分享
May 17 PHP
php中CI操作多个数据库的代码
Jul 05 PHP
用php来改写404错误页让你的页面更友好
Jan 24 PHP
PHP获取栏目的所有子级和孙级栏目的ID号示例
Apr 01 PHP
php实现转换ubb代码的方法
Jun 18 PHP
php获取本机真实IP地址实例代码
Mar 31 PHP
深入浅析Yii admin的权限控制
Aug 31 PHP
php实现文件预览功能
May 23 PHP
php读取本地json文件的实例
Mar 07 PHP
PHP count_chars()函数讲解
Feb 14 PHP
yii2 开发api接口时优雅的处理全局异常的方法
May 14 PHP
php封装的page分页类完整实例代码
Feb 01 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
十天学会php之第四天
2006/10/09 PHP
PHP 开发环境配置(Zend Server安装)
2010/04/28 PHP
gd库图片下载类实现下载网页所有图片的php代码
2012/08/20 PHP
php去除HTML标签实例
2013/11/06 PHP
php、mysql查询当天,查询本周,查询本月的数据实例(字段是时间戳)
2017/02/04 PHP
PDO::errorCode讲解
2019/01/28 PHP
javascript 得到变量类型的函数
2010/05/19 Javascript
jQuery函数的等价原生函数代码示例
2013/05/27 Javascript
js导出格式化的excel 实例方法
2013/07/17 Javascript
JavaScript中操作字符串小结
2015/05/04 Javascript
javascript实现网页屏蔽Backspace事件,输入框不屏蔽
2015/07/21 Javascript
jQuery实现淡入淡出二级下拉导航菜单的方法
2015/08/28 Javascript
WordPress 单页面上一页下一页的实现方法【附代码】
2016/03/10 Javascript
完美JQuery图片切换效果的简单实现
2016/07/21 Javascript
jQuery子元素过滤选择器用法示例
2016/09/09 Javascript
从零开始做一个pagination分页组件
2017/03/15 Javascript
详解Nodejs之npm&package.json
2017/06/15 NodeJs
如何理解Vue的render函数的具体用法
2017/08/30 Javascript
Vue 组件传值几种常用方法【总结】
2018/05/28 Javascript
JavaScript插入排序算法原理与实现方法示例
2018/08/06 Javascript
用VueJS写一个Chrome浏览器插件的实现方法
2019/02/27 Javascript
微信小程序HTTP接口请求封装代码实例
2019/09/05 Javascript
d3.js实现图形缩放平移
2019/12/19 Javascript
解决ant-design-vue中menu菜单无法默认展开的问题
2020/10/31 Javascript
JavaScript十大取整方法实例教程
2020/12/03 Javascript
python使用pil库实现图片合成实例代码
2018/01/20 Python
使用Python实现将list中的每一项的首字母大写
2019/06/11 Python
python从zip中删除指定后缀文件(推荐)
2019/12/05 Python
wxpython多线程防假死与线程间传递消息实例详解
2019/12/13 Python
python 列表、字典和集合的添加和删除操作
2019/12/16 Python
如何基于python测量代码运行时间
2019/12/25 Python
基于html5实现的图片墙效果
2014/10/16 HTML / CSS
Sephora丝芙兰澳洲官方网站:国际知名化妆品购物
2016/10/27 全球购物
高一新生军训方案
2014/05/12 职场文书
党在我心中演讲稿
2014/09/02 职场文书
Oracle用户管理及赋权
2022/04/24 Oracle