一 . 模式动机
- 在软件系统中,有些对象的创建过程比较复杂,而且需要频繁创建,消耗资源多,原型模式就是通过给出一个原型对象来指明要创建对象的类型,然后复制这个原型对象,创建更多这个类型的对象,就相当于给一个原物品,然后复制很多此物品,这样来降低创建多个同种复杂对象的资源。
二 . 定义
- 原型模式(Prototype Pattern):是一种对象型创建模式,用原型实列指定要克隆的对象的类型,通过克隆原型对象创建新的对象。原型模式允许一个对象再创建另一个可定制的对象,无需知道任何创建的细节。
三 . 模式结构
(图片来源于网络)
Prototype:抽象原型类
ConcretePrototype:具体原型类
四 . 模式分析
- 这个模式的结构非常简单,最主要的是这个clone()方法,怎么去实现克隆一个对象。实现这个方法也很简单,因为java中所有的类都继承自java.lang.Object,而Object类提供了一个clone()方法。
- 要适用clone()方法必须实现一个标识接口Cloneable空接口(接口里面没有方法),表示这个java类支持复制。如果没有实现这个接口就调用了clone(),java编译器就会抛出一个CloneNotSupportedException异常。
- 克隆模式还可以分为浅克隆和深克隆,浅克隆是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深克隆不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。
- 浅克隆
- 深克隆
五 . 实例
import java.io.*;
//原型类必须实现Cloneable接口
public class Sheep implements Cloneable,Serializable {
String name;
int age;
Sheep sheep;
public void setSheep(Sheep sheep) {
this.sheep = sheep;
}
public Sheep getSheep() {
return sheep;
}
public Sheep(String name, int age) {
this.name = name;
this.age = age;
}
//通过调用Object的clone()方法返回一个克隆对象
public Object clone() throws CloneNotSupportedException {
Object object = super.clone();
return object;
}
public String display() {
return (this.name+this.age+" 引用对象属性:"+this.getSheep().name+this.getSheep().age);
}
}
class Client{
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Sheep s = new Sheep("羊",12);
Sheep sheep = new Sheep("多利",8);
sheep.setSheep(s);
//sheep1是浅克隆
//通过调用sheep这个原型对象的clone()方法克隆一个sheep1
Sheep sheep1 =(Sheep) sheep.clone();
//通过序列化反序列化深克隆出sheep2
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(sheep);
ByteArrayInputStream bio = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bio);
Sheep sheep2 = (Sheep) ois.readObject();
System.out.println("判断原型的引用对象和浅克隆的引用对象是否是同一个对象");
System.out.println(sheep1.getSheep()==sheep.getSheep());
System.out.println("判断原型的引用对象和深克隆的引用对象是否是同一个对象");
System.out.println(sheep2.getSheep()==sheep.getSheep());
System.out.println("原型"+sheep.display()+"\n"+"浅克隆"+sheep1.display()+"\n"+"深克隆"+sheep2.display()+"\n");
//这里改变原型对象的引用属性的属性和基本属性,基本属性不会变,深克隆的引用属性的属性不会变
sheep.getSheep().name = "小肥羊";
sheep.age = 19;
System.out.println("原型"+sheep.display()+"\n"+"浅克隆"+sheep1.display()+"\n"+"深克隆"+sheep2.display());
}
}
- 运行结果
E:\Java\jdk1.8.0_171\bin\java.exe "-javaagent:E:\idea\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar=60831:E:\idea\IntelliJ IDEA 2019.1.3\bin" -Dfile.encoding=UTF-8 -classpath "E:\Java\jdk1.8.0_171\jre\lib\charsets.jar;E:\Java\jdk1.8.0_171\jre\lib\deploy.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\access-bridge-64.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\cldrdata.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\dnsns.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\jaccess.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\jfxrt.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\localedata.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\nashorn.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\sunec.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\sunjce_provider.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\sunmscapi.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\sunpkcs11.jar;E:\Java\jdk1.8.0_171\jre\lib\ext\zipfs.jar;E:\Java\jdk1.8.0_171\jre\lib\javaws.jar;E:\Java\jdk1.8.0_171\jre\lib\jce.jar;E:\Java\jdk1.8.0_171\jre\lib\jfr.jar;E:\Java\jdk1.8.0_171\jre\lib\jfxswt.jar;E:\Java\jdk1.8.0_171\jre\lib\jsse.jar;E:\Java\jdk1.8.0_171\jre\lib\management-agent.jar;E:\Java\jdk1.8.0_171\jre\lib\plugin.jar;E:\Java\jdk1.8.0_171\jre\lib\resources.jar;E:\Java\jdk1.8.0_171\jre\lib\rt.jar;E:\Design pattern\out\production\Design pattern" Prototype原型模式.Client
判断原型的引用对象和浅克隆的引用对象是否是同一个对象
true
判断原型的引用对象和深克隆的引用对象是否是同一个对象
false
原型多利8 引用对象属性:羊12
浅克隆多利8 引用对象属性:羊12
深克隆多利8 引用对象属性:羊12
原型多利19 引用对象属性:小肥羊12
浅克隆多利8 引用对象属性:小肥羊12
深克隆多利8 引用对象属性:羊12
Process finished with exit code 0
- 结果分析:可以看到,浅克隆的引用对象与原型的引用对象为同一个对象,而与深克隆则不是同一个对象,深克隆出来的对象的引用对象是一个新的对象,我们改变了原型多利的基础属性age=19但浅克隆和深克隆的age属性都没有改变,但改变引用对象的属性后,浅克隆和原型的引用对象其实是同一个,因此也会改变,而深克隆的类的引用对象则完全是一个新的对象,不会改变
六 . 模式优缺点
- 优点:当创建对象实例比较复杂是,原型模式可以简化对象的创建过程,提高效率。可以保存对象状态。
- 缺点:需要为每个类配备一个克隆方法,而且克隆方法需要对类的功能进行通盘考虑,对全新类不是很难,但对已有类进行改造是,必须修改其源代码,违背了“开闭原则”。
七 . 适用环境
- 创建新对象成本较大时。
个类配备一个克隆方法,而且克隆方法需要对类的功能进行通盘考虑,对全新类不是很难,但对已有类进行改造是,必须修改其源代码,违背了“开闭原则”。