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文件的实现方法
Mar 19 PHP
php面向对象全攻略 (十四) php5接口技术
Sep 30 PHP
phpadmin如何导入导出大数据文件及php.ini参数修改
Feb 18 PHP
PHP迭代器实现斐波纳契数列的函数
Nov 12 PHP
PHP_SELF,SCRIPT_NAME,REQUEST_URI区别
Dec 24 PHP
php实现Session存储到Redis
Nov 11 PHP
WordPress用户登录框密码的隐藏与部分显示技巧
Dec 31 PHP
PHP编写登录验证码功能 附调用方法
May 19 PHP
PHP高精确度运算BC函数库实例详解
Aug 15 PHP
php注册系统和使用Xajax即时验证用户名是否被占用
Aug 31 PHP
PHP实现的杨辉三角求解算法分析
Mar 11 PHP
解决thinkphp6(tp6)在状态码500下不报错,或者显示错误“Malformed UTF-8 characters”的问题
Apr 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
漫威DC即将合作联动,而双方早已经秘密开始
2020/04/09 欧美动漫
dede3.1分页文字采集过滤规则详说(图文教程)
2007/04/03 PHP
PHP写入WRITE编码为UTF8的文件的实现代码
2008/07/07 PHP
php中session过期时间设置及session回收机制介绍
2014/05/05 PHP
php列出mysql表所有行和列的方法
2015/03/13 PHP
php 截取utf-8格式的字符串实例代码
2016/10/30 PHP
js左侧多级菜单动态的解决方案
2010/02/01 Javascript
javascript中onmouse事件在div中失效问题的解决方法
2012/01/09 Javascript
JavaScript获取和设置CheckBox状态的简单方法
2013/07/05 Javascript
只需一行代码,轻松实现一个在线编辑器
2013/11/12 Javascript
js登录弹出层特效
2014/03/07 Javascript
Javascript限制网页只能在微信内置浏览器中访问
2014/11/09 Javascript
关于在Servelet中如何获取当前时间的操作方法
2016/06/28 Javascript
React实现双向绑定示例代码
2016/09/19 Javascript
微信小程序 向左滑动删除功能的实现
2017/03/10 Javascript
推荐VSCode 上特别好用的 Vue 插件之vetur
2017/09/14 Javascript
vue组件jsx语法的具体使用
2018/05/21 Javascript
解决vue无法设置滚动位置的问题
2018/10/07 Javascript
详解如何在Node.js的httpServer中接收前端发送的arraybuffer数据
2018/11/11 Javascript
iview tabs 顶部导航栏和模块切换栏的示例代码
2019/03/04 Javascript
[00:36]DOTA2勇士令状莱恩声望物品——冥晶之厄展示
2018/05/25 DOTA
Java分治归并排序算法实例详解
2017/12/12 Python
简单实现python画圆功能
2018/01/25 Python
用Pycharm实现鼠标滚轮控制字体大小的方法
2019/01/15 Python
分享一个pycharm专业版安装的永久使用方法
2019/09/24 Python
Pandas 缺失数据处理的实现
2019/11/04 Python
python的json中方法及jsonpath模块用法分析
2019/12/06 Python
python实现随机加减法生成器
2020/02/24 Python
python GUI库图形界面开发之pyinstaller打包python程序为exe安装文件
2020/02/26 Python
在django中form的label和verbose name的区别说明
2020/05/20 Python
pytorch快速搭建神经网络_Sequential操作
2020/06/17 Python
CSS3 简写animation
2012/05/10 HTML / CSS
村党支部群众路线教育实践活动对照检查材料
2014/09/26 职场文书
企业管理制度设计时要注意的几种“常见病”!
2019/04/19 职场文书
生鲜超市—未来中国最具有潜力零售业态
2019/08/02 职场文书
Win11安全功能升级:内置防网络钓鱼功能
2022/04/08 数码科技