概述
- 定义 : 指原型实例指定创建对象的种类, 并且通过拷贝这些原型创建新的对象
- 不需要知道任何创建细节, 不调用构造函数
- 类型 : 创建型
适用场景
- 类初始化消耗较多资源
- new产生一个对象需要非常繁琐的过程(数据准备, 访问权限等)
- 构造函数比较复杂
- 循环体中生产大量对象时
优点
- 原型模式在性能上比直接new一个对象性能高
- 简化创建过程
缺点
- 必须配备克隆方法
- 对克隆复杂对象或克隆出的对象进行复杂改造时, 容易引入风险
- 深克隆, 浅克隆要运用得当
模式角色
- Prototype : 声明一个克隆自身的接口, 需要注意的是, 在Java中Object类中存在了clone方法, 所以这个角色就相当于Object类的角色了, 使用时不需要再声明接口了, 直接覆盖Object的clone方法即可
- ConcretePrototype : 实现一个克隆自身的操作
- Client : 即客户端, 让一个原型克隆自身从而创建一个新的对象
代码实现
- java中要实现clone, 需要实现Cloneable接口, 这是一个标记接口
- 重写Object类的clone方法
/**
* 原型模式
*
* @author 七夜雪
* @create 2018-11-23 10:51
*/
public class Person implements Cloneable {
private String name;
private Integer age;
private String address;
private String company;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
", company='" + company + '\'' +
", hashCode='" + hashCode() + '\'' +
'}';
}
// ..............Setter, Getter方法省略.................
}
测试代码:
public static void main(String[] args) throws Exception {
// 原型
Person prototype = new Person();
prototype.setName("萧忆情");
prototype.setAddress("洛阳");
prototype.setCompany("听雪楼");
prototype.setAge(18);
Person clone = (Person)prototype.clone();
System.out.println("原型:" + prototype);
System.out.println("拷贝:" + clone);
}
输出结果:
原型:Person{name='萧忆情', age=18, address='洛阳', company='听雪楼', hashCode='356573597'}
拷贝:Person{name='萧忆情', age=18, address='洛阳', company='听雪楼', hashCode='1735600054'}
- 对于都是简单属性的类, 这种操作并没有什么问题, 如果属性中存在复杂对象, 就可能会存在问题, 现在我们在Person类中增加一个Date类型的birthday属性, 然后修改一下测试方法试一下:
public static void main(String[] args) throws Exception {
// 原型
Person prototype = new Person();
prototype.setName("萧忆情");
prototype.setAddress("洛阳");
prototype.setCompany("听雪楼");
prototype.setAge(18);
prototype.setBirthday(new Date());
Person clone = (Person)prototype.clone();
System.out.println("原型:" + prototype);
System.out.println("拷贝:" + clone);
// 这里如果直接调用clone.setBirthday()方法是并没有问题的, 因为prototype和clone是两个不同的对象了,直接调用set方法的话, 只是会把clone对象的Birthday属性指向一个新的Date对象
clone.getBirthday().setTime(211111212121L);
System.out.println("--------------更新了clone对象的birthday属性---------------");
System.out.println("原型:" + prototype);
System.out.println("拷贝:" + clone);
}
输出结果:
原型:Person{name='萧忆情', age=18, address='洛阳', company='听雪楼', birthday='Fri Nov 23 11:15:11 CST 2018', hashCode='1836019240'}
拷贝:Person{name='萧忆情', age=18, address='洛阳', company='听雪楼', birthday='Fri Nov 23 11:15:11 CST 2018', hashCode='325040804'}
--------------更新了clone对象的birthday属性---------------
原型:Person{name='萧忆情', age=18, address='洛阳', company='听雪楼', birthday='Thu Sep 09 18:00:12 CST 1976', hashCode='1836019240'}
拷贝:Person{name='萧忆情', age=18, address='洛阳', company='听雪楼', birthday='Thu Sep 09 18:00:12 CST 1976', hashCode='325040804'}
从上面的输出结果可以看出, 这种情况下prototype和clone对象的birthday属性还是同一个对象, 这种就是浅克隆, 又叫浅拷贝, 这种情况就需要改写clone方法, 对birthday属性再进行一次clone操作, 修改后的clone方法如下:
@Override
protected Object clone() throws CloneNotSupportedException {
Person clone = (Person) super.clone();
clone.birthday = (Date) this.birthday.clone();
return clone;
}
修改完之后, 再执行上面的测试用例, 就会发现结果已经正确了:
原型:Person{name='萧忆情', age=18, address='洛阳', company='听雪楼', birthday='Fri Nov 23 11:20:51 CST 2018', hashCode='1836019240'}
拷贝:Person{name='萧忆情', age=18, address='洛阳', company='听雪楼', birthday='Fri Nov 23 11:20:51 CST 2018', hashCode='325040804'}
--------------更新了clone对象的birthday属性---------------
原型:Person{name='萧忆情', age=18, address='洛阳', company='听雪楼', birthday='Fri Nov 23 11:20:51 CST 2018', hashCode='1836019240'}
拷贝:Person{name='萧忆情', age=18, address='洛阳', company='听雪楼', birthday='Thu Sep 09 18:00:12 CST 1976', hashCode='325040804'}