小议大型开发中php的设计模式之单件模式
时间:2011-04-15 来源:李惟
以往在php环境下谈到的设计模式往往都是理论一大堆,加之php较少在实现大型应用方面,所以很多时候设计模式成了一种摆设,本文从较简单的设计模型中的单件模式说起,谈谈应用,阐述设计模型对于复杂环境中的大内涵.
从一个简单的例子着手,在一些应用中,我们希望有些对象是全局作用的,如下:
include('example.class.php'); $obj = new example(); class a1 { function ex() { global $obj; $obj->v = 1; //注意这里,需要调用外部对象 } } class a2 { function ex() { global $obj; $obj->v ++; //注意这里,再次调用外部对象 } } $a1 = new a1; $a1->ex(); $a2 = new a2; $a2->ex();
从上面简单的例子看到,$obj对象被其它多个对象所使用,这时通常需要的几个步骤:
1. include类文件
2. 实例化
3. 在使用时global声明全局性
倘若有种情况需要在不同的对象中调用多个外部对象,那么情况有可能就是如下:
include('example.class.php'); $obj = new example(); include('example2.class.php'); $obj2 = new example2(); ... $obj3 $obj4 $obj_n ... class a1 { function ex() { global $obj; $obj->v = 1; global $obj2; $obj2->v = 1; ... global $obj_n; $obj_n->v = 1; } } class a2 { ...
局面开始不好收拾了,于是我们使用一种方法封装这个过程,首先定义一个类,这个类专门负责将另一个指定的类自动实例化成对象:
class Singleton { private $objs; public function instance($obj) { if($this->objs[$obj] == null) //判断是否已经实例化 { include($obj.'.class.php'); $this->objs[$obj] = new $obj; } return $this->objs[$obj]; } } $Singleton = new Singleton();
那么之前的局面就演变成如下:
... class a1 { function ex() { global $Singleton; //只需一次global声明 $Singleton->reg('example')->v = 1; $Singleton->reg('example2')->v = 1; ... $Singleton->reg('example_n')->v = 1; ... } } class a2 { ...
那么其中通过$Singleton对象访问类就是经典的单件模式了,这边仅仅是说明了单件模式概念,以下说明使用单件模式的好处.
一.文件包含与对象实例化的透明化
我们发现上边在使用$Singleton仍然需要global声明,这时可以使用函数来"包装"
function instance($class_name) { static $instance; if($instance == false) $instance = new Singleton(); return $instance->reg($class_name); //调用Singleton对象,实例化类并返回对象 }
那么最终的调用就是这样的
... class a1 { function ex() { //这里我们无需使用global,直接使用example类生成的对象 instance('example')->v = 1; instance('example2')->v = 1; ... } } class a2 { ...
这时我们看到使用一个外部对象就变得简单而清晰了,我们无需关注example类所在文件的包含和实例化就能直接使用,
这就是使用单件模式对对象透明化的作用.
二.全局(作用域)应用
通过之前的代码得知,我们可以在任何时候使用Singleton得到我们希望使用的对象:
class a1 { function ex() { instance('example')->v = 1; //使用单件模式针对example类生成对象并进行操作 ... } } ... class a2 { function ex() { instance('example')->v ++; //在不同作用域对同一个对象进行操作 ... } }
在a1和a2的类中调用example实现的对象时,无需考虑作用域及global声明,实现跨全局对象的应用.
三.事务性/一致性的应用
在一些多线程的语言中,有时希望在同一时间只能有一个线程对对象进行操作,这是线程安全问题,而在php中,类似的情况是资源竞争的解决,如对共享数据的读写,而使用单件模式则很容易做到,在原有单件模式的基础上,增加了事务性
class Singleton_safe { private $objs; private $mutex; public function reg($obj) { if($objs[$obj] == null) //判断是否已经实例化 { include($obj.'.class.php'); $this->mutex[$obj] = sem_get(fileinode($obj.'.class.php'),1,0666,true); sem_acquire($this->mutex[$obj]); //这边使用信号量做同步 $this->objs[$obj] = new $obj; } return $this->objs[$obj]; } function __destruct() { foreach($this->mutex as $sem_id) { sem_release($sem_id); //单件模式下析构时删除所有对象的信号量锁 //sem_remove($sem_id); } } }
当然,和其它语言不一样,这个简单的例子实现的事务周期是对象在整个php进程的运行期间,这样保证了在调用Singleton_safe实现单例模式下,派生对象的所有操作是系统唯一的.
*四.其它扩展
有时在使用或调试对象时,需要记录对象的调用次数,可增加一些记数功能,
class Singleton { private $objs; private $record = 0; public function reg($obj) { if($this->objs[$obj] == null) //判断是否已经实例化 { include($obj.'.class.php'); $this->objs[$obj] = new $obj; } $this->record[$obj]++; //累计对象的使用次数 return $this->objs[$obj]; } public get_record($obj_name) { return $this->record[$obj_name]; //获取对象的使用次数 } } .... function instance($class_name == null) { static $instance; if($instance == false) $instance = new Singleton(); if($class_name == null) return $instance; return $instance->reg($class_name); }
使用时
instance('example')->v = 1; instance('example')->v++; echo 'example被使用次数为:',instance()->get_record('example');//2
此外在一些其它语言上,如Cpp上也可结合对象的创建与销毁等内存管理工作.
单件模式是设计模式中较简单同时也是使用最为频繁的模式之一,仅仅这样一个简单的实现便可以帮我们做很多事,也让我们更多更有必要地去了解设计模式的价值所在.
附:C语言扩展编写的单件库
http://lajabs.net/2010/08/15/zend-api%E6%89%A9%E5%B1%95%E7%9A%84php%E5%AF%B9%E8%B1%A1%E7%9A%84autoload%E5%B7%A5%E5%85%B7/