上一篇文章我们介绍了设计模式的七种面向对象设计原则,本篇文章我们将介绍设计模式中创建型模式的单例与多例模式。在了解单例模式之前我们先来了解一下设计模式的分类有哪些呢?
设计模式的分类有哪些?
设计模式是在软件开发中,经过验证的,用于解决在特定环境下、重复出现的或者特定问题的解决方案。而这些都是前辈们经过大量的实践总结出来的宝贵经验,学习和领会其中的设计思想,能让我们在面对相同问题时可以直接使用现有的解决方案,从而避免重复创造轮子。典型的设计模式根据目的可以分为以下三大类:
-
创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
-
结构型模式:它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
-
行为型模式:描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。GoF 中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。
单例模式
单例模式(Singleton Pattern)是一个比较简单的模式,其定义如下:Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)
在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
单例模式的优点:
-
在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例
-
单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
-
提供了对唯一实例的受控访问。
-
由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
-
允许可变数目的实例。
-
避免对共享资源的多重占用。
单例模式的缺点:
-
不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
-
由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
-
单例类的职责过重,在一定程度上违背了“单一职责原则”。
-
滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
使用单例模式的注意事项:
-
使用时不能用反射模式创建单例,否则会实例化一个新的对象
-
使用懒单例模式时注意线程安全问题
-
.饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式)
单例模式的适用场景:
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
-
需要频繁实例化然后销毁的对象。
-
创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
-
有状态的工具类对象。
-
频繁访问数据库或文件的对象。
单例模式的使用场景:
-
网站的计数器,一般也是采用单例模式实现,否则难以同步。
-
应用程序的日志应用,一般都可以用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
-
Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
-
数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为可以用单例模式来维护,就可以大大降低这种损耗。
-
多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
注意:单例模式的意义在于生成一个对象,重复对其使用,从而节省了new操作的资源,但是对于PHP来说,页面跳转之后,会释放掉内存中的对象,这样似乎就没什么意义了??只能说在单个页面中,重复使用的一个对象资源时,这个单例还是很有用的。
PHP的单例模式
{ /**
* @var Singleton
*/
private static $instance;
public static function getInstance() {
if(!self::$instance instanceof self){
self::$instance = new self();
return self::$instance;
}else{
return self::$instance;
}
}
/**
* 不允许从外部调用以防止创建多个实例
* 要使用单例,必须通过 Singleton::getInstance() 方法获取实例
*/
private function __construct() {
}
/**
* 防止实例被克隆(这会创建实例的副本)
*/
private function __clone() {
}
/**
* 防止反序列化(这将创建它的副本)
*/
private function __wakeup() {
}
}
PHP的多例模式
多例模式 是指存在一个类有多个相同实例,而且该实例都是该类本身。这个类叫做多例类。(实际上是单例模式的推广)其特点是:
-
多例类可以有多个实例。
-
多例类必须自己创建、管理自己的实例,并向外界提供自己的实例。
多例模式的应用
-
2 个数据库连接器,比如一个是 MySQL ,另一个是 SQLite
-
多个记录器(一个用于记录调试消息,一个用于记录错误)
final class Multiton
{
const INSTANCE_1 = '1';
const INSTANCE_2 = '2';
/**
* @var 实例数组
*/
private static $instances = [];
/**
* 这里私有方法阻止用户随意的创建该对象实例
*/
private function __construct() {
}
public static function getInstance(string $instanceName): Multiton {
if (!isset(self::$instances[$instanceName])) {
self::$instances[$instanceName] = new self();
}
return self::$instances[$instanceName];
}
/**
* 该私有对象阻止实例被克隆
*/
private function __clone() {
}
/**
* 该私有方法阻止实例被序列化
*/
private function __wakeup() {
}
}
以上我们介绍了创建型模式中的单例与多例模式,下一篇将会介绍创建型模式中的原型模式。
感谢阅读!