单例模式(Singleton Pattern 单件模式或单元素模式)
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式是一种常见的设计模式,在计算机系统中,线程池、缓存、日志对象、对话框、打印机、数据库操作、显卡的驱动程序常被设计成单例。
单例模式分3种:懒汉式单例、饿汉式单例、登记式单例。
单例模式有以下3个特点:
1.只能有一个实例。
2.必须自行创建这个实例。
3.必须给其他对象提供这一实例。
那么为什么要使用PHP单例模式?
PHP一个主要应用场合就是应用程序与数据库打交道的场景,在一个应用中会存在大量的数据库操作,针对数据库句柄连接数据库的行为,使用单例模式可以避免大量的new操作。因为每一次new操作都会消耗系统和内存的资源。
不使用单例模式的情况下,代码是这样的。。。
1 //初始化一个数据库句柄 2 $db = new DB(...); 3 //比如有个应用场景是添加一条用户信息 4 $db->addUserInfo(); 5 ...... 6 //然而我们要在另一地方使用这个用户信息,这时要用到数据库句柄资源,可能会这么做 7 ...... 8 function test() { 9 $db = new DB(...); 10 $db->getUserInfo(); 11 ...... 12 有些朋友也许会说,可以直接使用global关键字! 13 global $db; 14 ......
单例模式修改DB层
1 <?php 2 class Mysql{ 3 //该属性用来保存实例 4 private static $conn; 5 6 //构造函数为private,防止创建对象 7 private function __construct(){ 8 $this->conn = mysql_connect('localhost','root',''); 9 } 10 11 //创建一个用来实例化对象的方法 12 public static function getInstance(){ 13 if(!(self::$conn instanceof self)){ 14 self::$conn = new self; 15 } 16 return self::$conn; 17 } 18 19 //防止对象被复制 20 public function __clone(){ 21 trigger_error('Clone is not allowed !'); 22 } 23 24 } 25 26 //只能这样取得实例,不能new 和 clone 27 $mysql = Mysql::getInstance(); 28 ?>
的确global可以解决问题,也起到单例模式的作用,但在OOP中,我们拒绝这种编码。因为global存在安全隐患(全局变量不受保护的本质)。
全局变量是面向对象程序员遇到的引发BUG的主要原因之一。这是因为全局变量将类捆绑于特定的环境,破坏了封装。如果新的应用程序无法保证一开始就定义了相同的全局变量,那么一个依赖于全局变量的类就无法从一个应用程序中提取出来并应用到新应用程序中。
确切的讲,单例模式恰恰是对全局变量的一种改进,避免那些存储唯一实例的全局变量污染命名空间。你无法用错误类型的数据覆写一个单例。这种保护在不支持命名空间的PHP版本里尤其重要。因为在PHP中命名冲突会在编译时被捕获,并使脚本停止运行。
单例模式的优缺点:
优点:
1. 改进系统的设计
2. 是对全局变量的一种改进
3. 避免不必要的多次实例化,照成的空间浪费
缺点:
1. 难于调试
2. 隐藏的依赖关系
3. 无法用错误类型的数据覆写一个单例
应用场景(来自http://blog.csdn.net/starrykey/article/details/52049639)
1. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
3. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
4. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
5. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
6. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
7. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
8. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
9. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.
总结以上,不难看出:
单例模式应用的场景一般发现在以下条件下:
(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
(2)控制资源的情况下,方便资源之间的互相通信。如线程池等。