设计模式详解--原型模式
是指原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。而调用者不需要知道任何创建细节,不调用构造函数
其实就是如何快速构建对象的方法总结,比如简单工厂将getter setter封装到某个方法中
JDK提供实现Cloneable接口,实现快速复制。当初在spring配置scope="prototype",、scope="singotn"就是在设置是否原型模式创建对象
属于创建型模式
适用场景:
1)类初始化消耗资源较多
2)new产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
3)构造函数比较复杂
4)循环体中生产大量对象时
克隆分为深克隆和浅克隆
⑴浅复制(浅克隆)
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。 Object类提供的方法clone只是拷贝本对象,就是在实体类中实现cloneable接口然后重写clone()方法即可,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址
我们这里手写一个接口模拟jdk原生的拷贝
先写一个代替clonable的接口Prototype
1.
public interface Prototype{
Prototype clone();
}
2.然后再写一个实体类来实现这个接口并重写clone方法
public class ConcretePrototypeA implements Prototype { private int age; private String name; private List hobbies; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getHobbies() { return hobbies; } public void setHobbies(List hobbies) { this.hobbies = hobbies; } @Override public ConcretePrototypeA clone() { ConcretePrototypeA concretePrototype = new ConcretePrototypeA(); concretePrototype.setAge(this.age); concretePrototype.setName(this.name); concretePrototype.setHobbies(this.hobbies); return concretePrototype; } }
可以看到我们这里clone方法就是new一个对象然后把已有的值赋给它。
3.写一个客户端来代理原型模式(也可以不写直接在测试类中调用)
public class Client {
private Prototype prototype;
public Client(Prototype prototype){
this.prototype = prototype;
}
public Prototype startClone(Prototype concretePrototype){
return (Prototype)concretePrototype.clone();
}
}
4.写一个测试类看看两个对象中的某个元素的地址是否一致
public class PrototypeTest {
public static void main(String[] args) {
// 创建一个具体的需要克隆的对象
ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
// 填充属性,方便测试
concretePrototype.setAge(18);
concretePrototype.setName("prototype");
List hobbies = new ArrayList<String>();
concretePrototype.setHobbies(hobbies);
System.out.println(concretePrototype);
// 创建Client对象,准备开始克隆
Client client = new Client(concretePrototype);
ConcretePrototypeA concretePrototypeClone = (ConcretePrototypeA) client.startClone(concretePrototype);
System.out.println(concretePrototypeClone);
System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeClone.getHobbies());
System.out.println("原对象中的引用类型地址值:" + concretePrototype.getHobbies());
System.out.println("对象地址比较:"+(concretePrototypeClone.getName() == concretePrototype.getName()));
}
}
6.输出结果截图
可以看到name的属性复制是浅克隆,地址是一致的
⑵深复制(深克隆)
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
我们以孙悟空拔下三根救命毫毛可以变出多个猴子猴孙为例
1.首先写一个猴子的实体父类
public class Monkey {
public int height;
public int weight;
public Date birthday;
}
2.然后再写一个金箍棒的实体,因为千变万化的孙猴子不可能用一个金箍棒吧,所以这里要实现深克隆,实现serializable接口
public class JinGuBang implements Serializable {
public float h = 100;
public float d = 10;
public void big(){
this.d *= 2;
this.h *= 2;
}
public void small(){
this.d /= 2;
this.h /= 2;
}
}
3.写一个齐天大圣的实体
public class QiTianDaSheng extends Monkey implements Cloneable,Serializable {
public JinGuBang jinGuBang;
public QiTianDaSheng(){
//只是初始化
this.birthday = new Date();
this.jinGuBang = new JinGuBang();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return this.deepClone();
}
public Object deepClone(){
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();
copy.birthday = new Date();
return copy;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
public QiTianDaSheng shallowClone(QiTianDaSheng target){
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
qiTianDaSheng.height = target.height;
qiTianDaSheng.weight = target.height;
qiTianDaSheng.jinGuBang = target.jinGuBang;
qiTianDaSheng.birthday = new Date();
return qiTianDaSheng;
}
}
这个类要同时实现clonable和serializable接口,我在这个类中写了两个方法一个是深克隆一个是浅克隆。
4.编写测试类
public class DeepCloneTest {
public static void main(String[] args) {
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
try {
QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone();
System.out.println("深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang));
} catch (Exception e) {
e.printStackTrace();
}
QiTianDaSheng q = new QiTianDaSheng();
QiTianDaSheng n = q.shallowClone(q);
System.out.println("浅克隆:" + (q.jinGuBang == n.jinGuBang));
}
}
5.运行结果
可以发现深克隆的结果是产生了不同的对象,深克隆成功
(3)总结
深克隆可能会产生单例被破坏的情况,与单例唱反调。所以如果不想让单例被破坏可以不实现cloneable
缺点:
必须配备克隆(或者可拷贝)方法
对克隆复杂对象或对克隆出的对象进行复杂改造时,容易带来风险
深拷贝、浅拷贝要运用得当
原型模式的介绍就到这里啦,如有不当之处,烦请流言指正。下一篇,设计模式详解--代理模式