定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象
类型
创建型模式
结构
原型模式由以下几部分组成:
- 原型(
Prototype
): 声明一个克隆自身的接口; - 具体原型(
Concrete Prototype
): 实现一个克隆自身的操作; - 客户端(
Client
): 让一个原型克隆自身从而创建一个新的对象。
优点
- 当创建新的对象比较复杂时,可以简化对象的创建过程,同时也能够提高效率;
- 可以使用深克隆保持对象的状态,同时也能逃避构造函数约束;
缺点
- 实现深克隆可能相对比较复杂。
- 需要为每一个类配备一个克隆方法。
使用场景
- 对于创建新对象成本较大的情况,可以利用已有的对象进行复制来获得。
- 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占内存不大,可以使用原型模式配合备忘录模式来应用;相反,如果对象的状态变化很大,或者对象占用的内存很大,那么采用状态模式会比原型模式更好。
注意事项
- Java 中,使用原型模式复制对象不会调用类的构造方法,因为对象的复制是通过调用 Object 类的
clone()
方法来完成的。 - 不但构造方法中的代码不会执行,访问权限对原型模式也无效。单例模式中,只要将构造方法的访问权限设置为 private 就可以实现单例。但是
clone()
方法无视构造方法的权限,所以某种意义上单例模式与原型模式是冲突的。 - 深拷贝与浅拷贝。Object 类的
clone()
方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。(Java 中,会发生深拷贝的有 8 种基本类型以及他们的封装类型,另外还有 String 类型,其余的都是浅拷贝)
代码实现
具体原型
public class User implements Cloneable {
private String userName;
private String password;
public User() {
System.out.println("user constructor.");
}
@Override
public Object clone() {
User user = null;
try {
user = (User) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return user;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setPassword(String password) {
this.password = password;
}
}
客户端
public static void main(String... args) {
User user1 = new User();
user1.setUserName("user1");
user1.setPassword("123456");
User user2 = (User) user1.clone();
System.out.println("user1 == user2 " + (user1 == user2));
System.out.println("user1.getClass() == user2.getClass() " + (user1.getClass() == user2.getClass()));
System.out.println("user1.equals(user2) " + (user1.equals(user2)));
}
参考代码
https://github.com/xueyufish/design-pattern/tree/master/prototype