什么是单例模式
顾名思义,就是只有一个实例。作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
为什么使用单例模式
- PHP的应用主要在于数据库应用,一个应用中会存在大量的数据库操作,在使用面向对象的方式开发时,如果使用单例模式,
则可以避免大量的 new 操作消耗的资源,还可以减少数据库连接这样就不容易出现 too many connections 情况。 - 如果系统中需要有一个类来全局控制某些配置信息,那么使用单例模式可以很方便的实现。
- 在一次页面请求中,便于进行调试,因为所有的代码(例如数据库操作类db)都集中在一个类中,我们可以在类中设置钩子,输出日志,从而避免到处 var_dump,echo。
单例模式结构图
PHP设计模式(1):单例模式(Singleton)单例模式的实现
- 私有化一个属性用于存放唯一的一个实例
- 私有化构造方法,私有化克隆方法,用来创建并只允许创建一个实例
- 公有化静态方法,用于向系统提供这个实例
代码实现
class Singleton
{
// 存放实例
private static $_instance = null;
// 私有化构造方法
private function __construct()
{
echo '单例模式的实例被构造了';
}
// 私有化克隆方法
private function __clone() {}
// 公有化获取实例方法
public static function getInstance()
{
if (!(self::$_instance instanceof Singleton)) {
self::$_instance = new Singleton();
}
return self::$_instance;
}
}
$singleton = Singleton::getInstance();
优点:
- 因为静态方法可以在全局范围内被访问,当我们需要一个单例模式的对象时,只需调用getInstance方法,获取先前实例化的对象,无需重新实例化。
- 由于单例模式在内存中只有一个实例,减少内存开支,特别是一个对象需要频繁地创建销毁时,而且创建或销毁时性能又无法优化,单例模式就非常明显了
- 由于单例模式只生成一个实例,所以,减少系统的性能开销,当一个对象产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
- 单例模式可以避免对资源的多重占用,例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。
缺点:
- 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
- 单例对象如果持有Context,那么很容易引发内存泄漏,此时需要注意传递给单例对象的Context最好是Application Context。
使用 Trait 关键字实现类似于继承单例类的功能
Trait Singleton
{
//存放实例
private static $_instance = null;
//私有化克隆方法
private function __clone() {}
//公有化获取实例方法
public static function getInstance()
{
$class = __CLASS__;
if (!(self::$_instance instanceof $class)) {
self::$_instance = new $class();
}
return self::$_instance;
}
}
class DB
{
private function __construct()
{
echo __CLASS__ . PHP_EOL;
}
}
class DBHandle extends DB
{
use Singleton;
private function __construct()
{
echo '单例模式的实例被构造了';
}
}
$handle = DBHandle::getInstance();
// 注意若父类方法为public,则子类只能为pubic,若父类为private,子类为public,protected,private都可以。
单例模式特点(三私一公)
- 私有的构造方法(防止类外实例化)
- 私有的克隆方法(防止通过克隆生成对象)
- 私有的静态属性(保存类的实例)
- 公有的静态方法(调取这个类相当一个接口)
补充,大多数书籍介绍单例模式,都会讲三私一公,公有化静态方法作为提供对象的接口,私有属性用于存放唯一一个单例对象。私有化构造方法,私有化克隆方法保证只存在一个单例。
但实际上,虽然我们无法通过 new 关键字和 clone 出一个新的对象,但我们若想得到一个新对象。还是有办法的,那就是通过序列化和反序列化得到一个对象。私有化 sleep() 和 wakeup() 方法依然无法阻止通过这种方法得到一个新对象。或许真得要阻止,你只能去 __wakeup 添加删除一个实例的代码,保证反序列化增加一个对象,你就删除一个。不过这样貌似有点怪异。
网友评论