原型模式
文章目录
亦称:克隆、Clone、Prototype
背景
假如有一个对象,希望生成与其完全相同的一个复制品,该如何实现呢?
- 新建一个属于相同类的对象
- 遍历原始对象的所有成员变量,将成员变量值复制到新对象中。
有个问题,并非所有对象都能通过这种方式进行复制。因为有些对象可能拥有私有成员变量,它们在外部是不可见的。
因此,从"外部"复制对象,并非总是可行的。
直接复制还有其它问题:
- 必须知道对象所属的类才能创建复制品,所以代码必须依赖该类
- 有时只知道对象所实现的接口,并不知道具体的类
解决方案
原型模式将克隆过程委派给被克隆的实际对象。
模式为所有支持克隆的对象声明了一个通用接口,该接口提供克隆对象的能力,通常仅包含一个克隆方法
。
所有的类对克隆方法的实现都非常相似,该方法会创建一个当前类的对象,然后将原始对象所有的成员变量值复制到新建的类中。
支持克隆的对象即为原型
。
使用原型实例的Clone方法创建新的对象,新的对象属于同一个类,由原型实例拷贝而来。
原型模式
常用于创建对象成本较大的情况。这里的拷贝根据实际业务,可以是深拷贝,也可以是浅拷贝。
对于Prototype,核心在于对象自身的Clone函数,用于拷贝自己。
是否需要有Prototype接口类,看具体情况。如果需要用到里氏替换,就创建接口类,如果没有用处,就无需创建。
使用场景
- 业务代码需要复制一些对象,同时又希望业务代码独立于这些对象所属的具体类,可以使用原型模式。
- 如代码需要处理第三方代码通过接口传递过来的对象,即使不考虑代码耦合的情况,也不能依赖这些对象所属的具体类,可能并不知道它们的具体信息(通过接口传递的)
- 创建对象成本较大的情况。如需要从DB、硬盘等获取大量数据或者需要经过大量计算等;又或者建立的对象存储的内容是可以被复用,完全重新建一个新的更加耗时。
实现步骤
- 创建原型接口,声明克隆方法。
- 原型类必须另行定义一个以该类对象为参数的构造函数,该构造函数必须复制参数对象中的所有成员变量值到新建实体中。如果需要修改子类,必须调用父类构造函数,让父类复制其私有成员变量值。
- 克隆方法通常只有一行代码:使用new运算符调用原型版本的构造函数。注意,每个类都必须显式重写克隆方法并使用自身类名调用new运算符,否则克隆方法可能会生成父类的对象。
- 还可以创建一个中心化原型注册表,用于存储常用原型。
优缺点
优点
- 可以克隆对象,无需与它们所属的具体类相耦合。
- 可以克隆预生成原型,避免反复运行初始化代码。
- 可以更方便地生成复杂对象。
- 可以用继承以外的方式来处理复杂对象的不同配置。
缺点
- 克隆包含循环引用的复杂对象可能会非常麻烦。
与其它模式的关系
- 在许多设计工作的初期都会使用
工厂方法模式
(较为简单,而且可以更方便地通过子类进行定制),随后演化为使用抽象工厂模式、原型模式或生成器模式(更灵活但更加复杂)。 抽象工厂模式
通常基于一组工厂方法,也可以使用原型模式
来实现这些类的方法。- 原型可用于保存
命令模式
的历史记录。 - 大量使用
组合模式
和装饰模式
的设计通常可从对于原型的使用中获益。可以通过该模式来复制复杂结构,而非从零开始重新构造。 - 原型并不基于继承,因此没有继承的缺点。另一方面,原型需要对被复制对象进行复杂的初始化。
工厂方法
基于继承,但是它不需要初始化步骤。 - 有时候原型可以作为
备忘录模式
的一个简化版本,其条件是你需要在历史记录中存储的对象的状态比较简单,不需要链接其他外部资源,或者链接可以方便地重建。 - 抽象工厂、生成器和原型都可以用单例模式来实现。
示例
|
|
|
|
|
|
|
|
原型模式就是利用对已有对象(原型)进行复制的方式,来创建新对象,以达到节省创建时间的目的。 拷贝可以选择深拷贝或者浅拷贝。