原型模式
原型模式总概述
在有些系统中,存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂且耗时耗资源,用原型模式生成对象就很高效,就像孙悟空拔下猴毛轻轻一吹就变出很多孙悟空一样简单。在设计模式中也存在一个类似的模式,可以通过一个原型对象克隆出多个一模一样的对象,该对象称之为原型模式。
原型模式的意图,解决的问题,什么时候使用
原型模式的意图就是解决当创建对象麻烦时,并且此对象需要参照某个对象某些属性的模板来创建的时候,直接通过克隆这个参照对象即可。原型模式提出了如何管理以及如何克隆这个模板对象的概念。如果程序中,经常需要(多种)对象(引入原型管理器,即原型对象的抽象类集合)的克隆我们可以考虑原型模式。
1.原型模式
原型(Prototype)模式,用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多。在生活中复制的例子非常多,这里不一一列举了。
2.单例模式中的角色
组成(角色) | 作用 |
---|---|
抽象原型类 | 规定了具体原型对象必须实现的接口。 |
具体原型类 | 实现抽象原型类的 clone() 方法,它是可被复制的对象。 |
访问类 | 使用具体原型类中的 clone() 方法来复制新的对象。 |
//具体原型类
class Realizetype implements Cloneable {
Realizetype() {
System.out.println("具体原型创建成功!");
}
public Object clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功!");
return (Realizetype) super.clone();
}
}
//原型模式的测试类
public class PrototypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Realizetype obj1 = new Realizetype();
Realizetype obj2 = (Realizetype) obj1.clone();
System.out.println("obj1==obj2?" + (obj1 == obj2));
}
}
使用Object类中的clone方法需要实现Cloneable 接口。
3.原型模式之浅克隆
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
数据一般分为两种,值类型和引用类型,在浅克隆中,值类型会直接拷贝,而引用类型会拷贝内存地址,也就是说克隆之后的两个对象中的某个引用类型还是指向的同一地址。Object类中的clone()就是浅克隆,需要注意Object中的clone()生成对象不会调用构造函数,并且与构造函数访问权限无关了。
4.原型模式之深克隆
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
我们通常会通过序列化的方式进行深克隆,这里需要注意的是,通过序列化方式实现深克隆会破坏单例模式(可以用过单元素的枚举类型强化Singleton属性,后面博文说话如何防止对单例模式的攻击,可以持续关注作者),通过说单例模式和原型模式是对立的。
5.原型模式优缺点、使用场景
优点:
- 1.简化对象创建过程
- 2.扩展性好,可抽象原型类,相比于工厂模式简化了结构
- 3.可以利用深克隆保存对象状态。
缺点:
- 1.需要克隆的类都需要实现克隆方法,且在类的内部不易动态扩展,违背开闭原则;
- 2.如果要深刻克隆,那么该对象的引用类型成员都需要支持深克隆才行
适用场景:
- 创建对象代码太大时,通过克隆来简化对象创建。
- 需要保存对象状态时,采取深克隆,例如文件对象存储,或者备份对象数据等。
- 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。
- 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例要优于构造函数。