前言
创建型模式(Creational Patterns)提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。创建型模式有多种,本文将简单介绍其中的单例模式(Singleton Pattern)与原型模式(Prototype Pattern),这两种设计模式都是处理对象创建的设计模式。
单例模式 Singleton Pattern
单例模式可以说是最常用的设计模式之一。单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只创建这个类的一个实例。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要外部实例化该类的对象。 简单来说,单例模式具有以下 3 个特点:
- 单例类只能有一个实例。
- 单例类必须能够自我实例化。
- 单例类必须给所有其他对象提供一个全局访问点。
应用
单例模式是编程中应用比较多的一种设计模式。比较常见的应用场景有:
- 有状态的工具类对象,各种存储config类
- 创建对象耗时过多或耗资源过多的对象,比如频繁访问数据库或文件的对象
- 系统只需要一个实例对象,负责统筹全局的对象等等
单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例, 这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则,因此在使用时需要保证其线程安全。此外,由于单例模式使得对象是可以全局访问和修改的,在实现该类时对于其中的一些变量也应当保证线程读写安全。
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
优缺点
优点:
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
- 避免对资源的多重占用
- 可以全局访问
缺点:
- 没有接口,不能继承,难以拓展
- 单例类的职责过重,一定程度上违背了单一职责原则
- 由于是全局变量,在一个地方更改可能会影响其他区域
原型模式 Prototype Pattern
所谓原型模式就是用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。原型模式可以说是所有设计模式中最简单的一种,其核心就是实现一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
应用
原型模式的实现主要涉及 3 个角色- Client:提出创建对象的请求
- Prototype:这是一个抽象角色,定义了具体原型类所需实现的方法。
- Concrete Prototype:此角色需要实现 Prototype 要求的克隆接口。
原型模式十分简单,只需要实现 clone 接口即可。但 clone 时需要注意深拷贝和浅拷贝的区别。
浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式被称为浅拷贝。
深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值。
简单来说,对于对象 A 引用对象 B,对 A 进行浅拷贝获得的是对象 A1 引用对象 B,对 A 进行深拷贝获得的是对象 A1 引用对象 B1。
优缺点
优点:
- 使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
- 使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。
缺点:
- 必须实现克隆接口
- 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用含有循环结构的时候
单例模式与原型模式的对比
原型模式是在已指定对象的基础上,然后通过拷贝这些原型对象创建新的对象。而单例模式模式的核心是将类的构造方法私有化,之后在类的内部产生实例化对象,并通过静态方法返回实例化对象的应用。