原型模式
原型模式属于设计模式中的创建型
什么是原型模式
使用原型实例指定要创建对象的类型,并通过复制这个原型来创建新对象。也就是指派一个类的实例来作为未来所有实例的生成器。
原型模式的原理与应用
当对象初始化开销很大,并且初始化参数的变化不大时,原型非常有用。在这种情况下,原型可以避免昂贵的“从头创建”,并支持廉价地克隆预初始化的原型。原型模式在其他创建模式中是独一无二的,因为它不需要类而只需要对象。
为什么对象的初始化开销很大。
实际上,在创建对象的过程中包含的申请内存、给成员变量赋值这一过程,本身并不会花费太多时间,或者说对于大部分业务系统来说,这点时间完全是可以忽略的。应用一个复杂的模式,只得到一点点的性能提升,这就是所谓的过度设计,得不偿失。
但是,如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算哈希值),或者需要从RPC、网络、数据库、文件系统等非常慢速的IO中读取,这种情况下,我们就可以利用原型模式,从其他已有对象中直接拷贝得到,而不用每次在创建新对象的时候,都重复执行这些耗时的操作。
深拷贝和浅拷贝
在使用原型模式克隆对象时,根据其成员对象是否也克隆,原型模式可以分为两种形式:深拷贝 和 浅拷贝 。
深拷贝和浅拷贝的区别
- 浅拷贝只会复制对象中基本数据类型数据和引用对象的内存地址,不会递归地复制引用对象,以及引用对象的引用对象
- 而深拷贝得到的是一份完完全全独立的对象。所以,深拷贝比起浅拷贝来说,更加耗时,更加耗内存空间。
如果要拷贝的对象是不可变对象,浅拷贝共享不可变对象是没问题的,但对于可变对象来说,浅拷贝得到的对象和原始对象会共享部分数据,就有可能出现数据被修改的风险,也就变得复杂多了。如果没有充分的理由,不要为了一点点的性能提升而使用浅拷贝。
原型模式案例 1
用抽象工厂存储一组原型,从中克隆返回产品对象
interface Person {
Person clone();
}
class Tom implements Person {
private final String NAME = "Tom";
@Override
public Person clone() {
return new Tom();
}
@Override
public String toString() {
return NAME;
}
}
class Dick implements Person {
private final String NAME = "Dick";
@Override
public Person clone() {
return new Dick();
}
@Override
public String toString() {
return NAME;
}
}
class Harry implements Person {
private final String NAME = "Harry";
@Override
public Person clone() {
return new Harry();
}
@Override
public String toString() {
return NAME;
}
}
class Factory {
private static final Map<String, Person> prototypes = new HashMap<>();
static {
prototypes.put("tom", new Tom());
prototypes.put("dick", new Dick());
prototypes.put("harry", new Harry());
}
public static Person getPrototype(String type) {
try {
return prototypes.get(type).clone();
} catch (NullPointerException ex) {
System.out.println("Prototype with name: " + type + ", doesn't exist");
return null;
}
}
}
public class PrototypeFactory {
public static void main(String[] args) {
if (args.length > 0) {
for (String type : args) {
Person prototype = Factory.getPrototype(type);
if (prototype != null) {
System.out.println(prototype);
}
}
} else {
System.out.println("Run again with arguments of command string ");
}
}
}
Output
>java PrototypeFactory tom dick jack
Tom
Dick
Prototype with name: jack, doesn't exist
原型模式案例 2
- 约定一个具有克隆属性的实体
- 设计一个具有注册功能的类来缓存原型对象
- 用一个方法来填充注册数据
- 注册类有一个方法能够根据传入的类型找到对应的对象并调用克隆方法
- 所有的类都实现克隆方法。
- 客户端通过克隆而不通过new来创建对象
// 1.约定一个具有克隆属性的实体
interface Prototype {
Prototype clone();
String getName();
void execute();
}
class PrototypeModule {
// 2.设计一个具有注册功能的类来缓存原型对象
private static List<Prototype> prototypes = new ArrayList<>();
public static void addPrototype(Prototype p) {
prototypes.add(p);
}
public static Prototype createPrototype(String name) {
// 4.注册类有一个方法能够根据传入的类型找到对应的对象并调用克隆方法
for (Prototype p : prototypes) {
if (p.getName().equals(name)) {
return p.clone();
}
}
System.out.println(name + ": doesn't exist");
return null;
}
}
// 5.所有的类都实现克隆方法。
class PrototypeAlpha implements Prototype {
private String name = "AlphaVersion";
@Override
public Prototype clone() {
return new PrototypeAlpha();
}
@Override
public String getName() {
return name;
}
@Override
public void execute() {
System.out.println(name + ": does something");
}
}
class PrototypeBeta implements Prototype {
private String name = "BetaVersion";
@Override
public Prototype clone() {
return new PrototypeBeta();
}
@Override
public String getName() {
return name;
}
@Override
public void execute() {
System.out.println(name + ": does something");
}
}
class ReleasePrototype implements Prototype {
private String name = "ReleaseCandidate";
@Override
public Prototype clone() {
return new ReleasePrototype();
}
@Override
public String getName() {
return name;
}
@Override
public void execute() {
System.out.println(name + ": does real work");
}
}
public class PrototypeDemo {
public static void main(String[] args) {
if (args.length > 0) {
initializePrototypes();
List<Prototype> prototypes = new ArrayList<>();
// 6.客户端通过克隆而不通过new来创建对象
for (String protoName : args) {
Prototype prototype = PrototypeModule.createPrototype(protoName);
if (prototype != null) {
prototypes.add(prototype);
}
}
for (Prototype p : prototypes) {
p.execute();
}
} else {
System.out.println("Run again with arguments of command string ");
}
}
// 3.用一个方法来填充注册数据
public static void initializePrototypes() {
PrototypeModule.addPrototype(new PrototypeAlpha());
PrototypeModule.addPrototype(new PrototypeBeta());
PrototypeModule.addPrototype(new ReleasePrototype());
}
}
Output
>java PrototypeDemo Garbage AlphaVersion BetaVersion Nothing ReleaseCandidate
Garbage: doesn't exist
Nothing: doesn't exist
AlphaVersion: does something
BetaVersion: does something
ReleaseCandidate: does real work