鸡肋的PHP单例模式应用详解


Posted in PHP onJune 03, 2013

单例模式的要点有三个:
一是某个类只能有一个实例;
二是它必须自行创建这个实例;
三是它必须自行向整个系统提供这个实例。

<?php 
/* 单例模式举例,其要点如下: 
* 
* 1. $_instance 必须声明为静态的私有变量 
* 2. 构造函数和克隆函数必须声明为私有的,这是为了防止外部程序 new 类从而失去单例模式的意义 
* 3. getInstance()方法必须声明为公有的,必须调用此方法以返回唯一实例的一个引用 
* 4. ::操作符只能访问静态变量或静态函数 
* 5. PHP的单例模式是相对而言的,因为PHP的解释运行机制使得每个PHP页面被解释执行后,所有的相关资源都会被回收。 
* 也就是说,PHP在语言级别上没有办法让某个对象常驻内存。在PHP中,所有的变量都是页面级的,无论是全局变量, 
* 还是类的静态成员,都会在页面执行完毕后被清空,结果会重新建立新的对象,这样也就完全失去了Singleton的意义。 
* 不过,在实际应用中同一个页面中可能会存在多个业务逻辑,这时单例模式就起到了很重要的作用,有效的避免了重复 
* new 对象(注: new 对象会消耗内存资源)这么一个行为,所以我们说PHP的单例模式是相对而言的 
* 
*/ 
class People 
{ 
static private $_instance = NULL; 
public $height = ''; 
public $age = ''; 
private function __construct() 
{ 
$this->height = '185'; 
$this->age = 25; 
} 
private function __clone() 
{ 
//do something 
} 
static public function getInstance() 
{ 
if(!self::$_instance instanceof self) 
{ 
//echo 'lgh-big'; 
self::$_instance = new self; 
} 
else 
{ 
//for testing only 
//echo 'gdc-xiaoairener'; 
} 
return self::$_instance; 
} 
public function getHeight() 
{ 
echo $this->height; 
} 
public function getAge() 
{ 
echo $this->age; 
} 
} 
function testInstance() 
{ 
People::getInstance()->getAge(); 
} 
//begin to use the class 
$lgh = People::getInstance(); 
$lgh->getHeight(); 
echo '<br />'; 
testInstance(); 
?>

优点:单例模式可以避免大量的new操作,因为每一次new操作都会消耗内存资源和系统资源
缺点:在PHP中,所有的变量无论是全局变量还是类的静态成员,都是 页面级的,每次页面被执行时,都会重新建立新的对象,都会在页面执行完毕后被清空,这样似乎PHP单例模式就没有什么意义了,所以PHP单例模式我觉得只 是针对单次页面级请求时出现多个应用场景并需要共享同一对象资源时是非常有意义的。

Why?为什么要使用PHP单例模式?
PHP的一个主要应用场合就是应用程序与数据库打交道的应用场景,所以一个应用中会存在大量的数据库操作,比如过数据库句柄来连接数据库这一行为,使用单例模式可以避免大量的new操作,因为每一次new操作都会消耗内存资源和系统资源。
还是有些抽象,给出代码片段。
使用传统方式编码

<?php
......
//初始化一个数据库句柄
$db = new DB(...);
//比如有个应用场景是添加一条用户信息:
$db->addUserInfo();
......
//然而我们在另外一个地方可能要查找用户的信息,这个情景出现在一个函数中,这时要用到数据库句柄资源,我们可能需要这么去做
......
function test(){
 ......
//这时我们不得不重新初始化一个数据库句柄,试想多个应用场景下,这样的代码是多么可怕啊?!
 $db = new DB(...);
 $db->getUserInfo();
 ......
//有些朋友或许会说,我也可以不这样做啊,我直接利用global关键字不就可以了吗?的确,global可以解决问题,也起到了单例模式的作用,但是OOP中,我们拒绝这样来编写代码,因为global存在安全隐患,请参考相关书籍,同时单例模式恰恰是对全局变量的一种改进,避免了那些存储唯一实例的全局变量污染命名空间
 global $db;  //OOP中,我们不提倡这样编写代码
......
}

使用单例模式编码
<?php
......
//所有的应用情景只有一个数据库句柄资源,嘿嘿,效率老高了,
//资源也大大的得到节省,代码简洁明了:)
DB::getInstance()->addUserInfo();
DB::getInstance()->getUserInfo();
......

How?如何来编写PHP单例模式?
在了解了单例模式的应用场景之后,下面我们通过编写单例模式的具体实现代码来掌握PHP单例模式的核心要点,代码如下:
<?php
/**
 *  PHP单例模式演示举例
*  @author   guohua.li
 *  @modify  2010-07-11
*  @website  http://blog.163.com/lgh_2002/
*/
class User{
/**
 *  静态成品变量 保存全局实例
 *  @access private
*/
static private $_instance = NULL;
/**
 *  私有化构造函数,防止外界实例化对象
*/
private function  __construct() {}
/**
 *  私有化克隆函数,防止外界克隆对象
*/
private function  __clone(){}
/**
 *  静态方法, 单例统一访问入口
 *  @return  object  返回对象的唯一实例
*/
static public function getInstance() {
if (is_null(self::$_instance) || !isset(self::$_instance)) {
self::$_instance = new self();
}
return self::$_instance;
} 
   /**
 * 测试方法: 获取用户名字
*/
public function getName() {
echo 'hello liguohua!';
}
}

从以上代码中,我们总结出PHP单例模式实现的核心要点有如下三条:
1.需要一个保存类的唯一实例的静态成员变量(通常为$_instance私有变量)
2.构造函数和克隆函数必须声明为私有的,这是为了防止外部程序new类从而失去单例模式的意义
3.必须提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一实例的一个引用
PHP单例模式的缺点
众所周知,PHP语言是一种解释型的脚本语言,这种运行机制使得每个PHP页面被解释执行后,所有的相关资源都会被回收。也就是说,PHP在语言级别上没有办法让某个对象常驻内存,这和asp.net、Java等编译型是不同的,比如在Java中单例会一直存在于整个应用程序的生命周期里,变量是跨页面级的,真正可以做到这个实例在应用程序生命周期中的唯一性。然而在PHP中,所有的变量无论是全局变量还是类的静态成员,都是页面级的,每次页面被执行时,都会重新建立新的对象,都会在页面执行完毕后被清空,这样似乎PHP单例模式就没有什么意义了,所以PHP单例模式我觉得只是针对单次页面级请求时出现多个应用场景并需要共享同一对象资源时是非常有意义的。
PHP 相关文章推荐
php生成SessionID和图片校验码的思路和实现代码
Mar 10 PHP
PHP 页面跳转到另一个页面的多种方法方法总结
Jul 07 PHP
最新的php 文件上传模型,支持多文件上传
Aug 13 PHP
php实现执行某一操作时弹出确认、取消对话框
Dec 30 PHP
PHP中使用sleep造成mysql读取失败的案例和解决方法
Aug 21 PHP
微信公众平台开发之配置与请求
Aug 26 PHP
深入解析PHP中foreach语句控制数组循环的用法
Nov 30 PHP
浅谈PHP值mysql操作类
Jun 29 PHP
PHP使用Redis实现Session共享的实现示例
May 12 PHP
PHP swoole和redis异步任务实现方法分析
Aug 12 PHP
laravel框架中视图的基本使用方法分析
Nov 23 PHP
Yii 框架使用数据库(databases)的方法示例
May 19 PHP
phpize的深入理解
Jun 03 #PHP
PHP不用第三变量交换2个变量的值的解决方法
Jun 02 #PHP
基于php socket(fsockopen)的应用实例分析
Jun 02 #PHP
深入PHP操作MongoDB的技术总结
Jun 02 #PHP
深入php数据采集的详解
Jun 02 #PHP
基于php下载文件的详解
Jun 02 #PHP
用PHP实现浏览器点击下载TXT文档的方法详解
Jun 02 #PHP
You might like
php中time()和mktime()方法的区别
2013/09/28 PHP
PHPMailer发送HTML内容、带附件的邮件实例
2014/07/01 PHP
YII框架中使用memcache的方法详解
2017/08/02 PHP
jQuery中wrapAll()方法用法实例
2015/01/16 Javascript
删除javascript所创建子节点的方法
2015/05/21 Javascript
javascript倒计时效果实现
2015/11/12 Javascript
javascript发送短信验证码实现代码
2015/11/12 Javascript
AngularJS包括详解及示例代码
2016/08/17 Javascript
浅谈Node.js:理解stream
2016/12/08 Javascript
js实现PC端根据IP定位当前城市地理位置
2017/02/22 Javascript
javascript 中的try catch应用总结
2017/04/01 Javascript
Javascript中类式继承和原型式继承的实现方法和区别之处
2017/04/25 Javascript
nodejs使用redis作为缓存介质实现的封装缓存类示例
2018/02/07 NodeJs
Angular ng-animate和ng-cookies用法详解
2018/04/18 Javascript
Angular4.0动画操作实例详解
2019/05/10 Javascript
微信小程序npm引入vant-weapp的踩坑记录
2019/08/01 Javascript
layui加载表格,绑定新增,编辑删除,查看按钮事件的例子
2019/09/06 Javascript
jQuery 淡入/淡出效果函数用法分析
2020/05/19 jQuery
Vue(定时器)解决mounted不能获取到data中的数据问题
2020/07/30 Javascript
如何搭建一个完整的Vue3.0+ts的项目步骤
2020/10/18 Javascript
[01:06:54]DOTA2-DPC中国联赛 正赛 SAG vs DLG BO3 第二场 2月28日
2021/03/11 DOTA
opencv改变imshow窗口大小,窗口位置的方法
2018/04/02 Python
解决Python pandas df 写入excel 出现的问题
2018/07/04 Python
浅谈python图片处理Image和skimage的区别
2019/08/04 Python
Python 支持向量机分类器的实现
2020/01/15 Python
python GUI库图形界面开发之PyQt5窗口布局控件QStackedWidget详细使用方法
2020/02/27 Python
python实现音乐播放和下载小程序功能
2020/04/26 Python
linux centos 7.x 安装 python3.x 替换 python2.x的过程解析
2020/12/14 Python
.NET面试10题
2014/02/24 面试题
.NET面试题:什么是反射
2016/09/30 面试题
Servlet都有哪些方法?主要作用是什么?
2014/03/04 面试题
《奇妙的国际互联网》 教学反思
2014/02/25 职场文书
大学优秀班主任事迹材料
2014/05/02 职场文书
2014中考励志标语
2014/06/05 职场文书
2014年银行信贷员工作总结
2014/12/08 职场文书
感谢信模板大全
2015/01/23 职场文书