设计模式-单例模式
所谓单例模式,就是确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例,即在应用程序中只会有这个类的一个实例存在。
通常单例模式用在仅允许数据库访问对象的实例中,从而防止打开多个数据库连接,单例模式是一种常见的设计模式,在计算机系统中,线程池、缓存、日志对象、对话框、打印机、数据库操作、显卡的驱动程序常被设计成单例。
一个单例类应包括以下几点:
1、和普通类不同,单例类不能被直接实例化,只能是由自身来实例化。因此,要获得这样的限制效果,构造函数必须标记为private。
2、要让单例类不被直接实例化而能起到作用,就必须为其提供这样的一个实例。因此,就必须要让单例类拥有一个能保存类的实例的私有静态成员变量和对应的一个能访问到实例的公共静态方法。
3、在PHP中,为防止对单例类对象的克隆来打破单例类的上述实现形式,通常还为基提供一个空的私有__clone()方法。
单例模式有以下3个特点:
1、只能有一个实例,必须拥有一个构造函数,并且必须被标记为private。
2、必须自行创建这个实例,拥有一个保存类的实例的静态成员变量。
3、必须给其他对象提供这一实例,拥有一个访问这个实例的公共的静态方法。
单例类不能再其它类中直接实例化,只能被其自身实例化。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。
那么为什么要使用PHP单例模式?
PHP一个主要应用场合就是应用程序与数据库打交道的场景,在一个应用中会存在大量的数据库操作,针对数据库句柄连接数据库的行为,使用单例模式可以避免大量的new操作。因为每一次new操作都会消耗系统和内存的资源。
在以往的项目开发中,没使用单例模式前的情况如下:
//初始化一个数据库句柄 $db = new DB(...); //比如有个应用场景是添加一条评论信息 $db->addComment(); ...... //如果我们要在另一地方使用这个评论信息,这时要用到数据库句柄资源,可能会这么做 ...... function comment() { $db = new DB(...); $db->getCommentInfo(); ...... //可能有些朋友也许会说,可以直接使用global关键字! global $db; ......
的确global可以解决问题,也起到单例模式的作用,但在OOP中,我们建议拒绝这种编码。因为global存在安全隐患(全局变量不受保护的本质)。
全局变量是面向对象程序员遇到的引发BUG的主要原因之一。这是因为全局变量将类捆绑于特定的环境,破坏了封装。如果新的应用程序无法保证一开始就定义了相同的全局变量,那么一个依赖于全局变量的类就无法从一个应用程序中提取出来并应用到新应用程序中。
确切的讲,单例模式恰恰是对全局变量的一种改进,避免那些存储唯一实例的全局变量污染命名空间。你无法用错误类型的数据覆写一个单例。这种保护在不支持命名空间的PHP版本里尤其重要。因为在PHP中命名冲突会在编译时被捕获,并使脚本停止运行。
使用单例模式改进下示例:
class Single { private $name;//声明一个私有的实例变量 private function __construct(){//声明私有构造方法为了防止外部代码使用new来创建对象。 } static public $instance;//声明一个静态变量(保存在类中唯一的一个实例) static public function getinstance(){//声明一个getinstance()静态方法,用于检测是否有实例对象 if(!self::$instance) self::$instance = new self(); return self::$instance; } public function setname($n){ $this->name = $n; } public function getname(){ return $this->name; } } $oa = Single::getinstance(); $ob = Single::getinstance(); $oa->setname('hello php world'); $ob->setname('good morning php'); echo $oa->getname();//good morning php echo $ob->getname();//good morning php
单例模式的优缺点:
优点:
1、改进系统的设计
2、是对全局变量的一种改进
缺点:
1、难于调试
2、隐藏的依赖关系
3、无法用错误类型的数据覆写一个单例