在面向对象系统中,使用原型模式来复制一个对象自身,从而克隆出多个与原型对象一模一样的对象。因为在软件系统中,有些对象的创建过程较为复杂,而且有时候需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在。
一、原型模式动机
我们来看一个例子——邮件。由于邮件对象包含的内容较多(如发送者、接收者、标题、内容、日期、附件等),某系统中现需要提供一个邮件复制功能,对于已经创建好的邮件对象,可以通过复制的方式创建一个新的邮件对象,如果需要改变某部分内容,无须修改原始的邮件对象,只需要修改复制后得到的邮件对象即可。
那么根据上述描述,我们需要先构建一个新的邮件对象,然后把原来邮件对象中所有的字段(如发送者、接收者、标题、内容、日期、附件等)都重新赋值到新的邮件对象中,如果在每次复制的时候重新写一遍这个代码,可想而知是多么的复杂,所以这个时候需要封装一个基类,把这些赋值的过程全部封装好,由此可见,原型模式利用的无非就是面向对象编程的封装、继承特点。
二、原型模式定义
原型模式(Prototype Pattern):原型模式是一种对象创建型模式,用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。原型模式允许一个对象再创建另外一个可定制的对象,无须知道任何创建的细节。
原型模式的基本工作原理是通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝原型自己来实现创建过程。
三、原型模式结构
原型模式包含如下角色:
Prototype:抽象原型类
ConcretePrototype:具体原型类
Client:客户类
四、模式分析
1.在原型模式结构中定义了一个抽象原型类,所有的Java类都继承自java.lang.Object,而Object类提供一个clone()方法,可以将一个Java对象复制一份。因此在Java中,可以直接使用Object提供的clone()方法来实现对象的克隆,Java语言中的原型模式实现很简单。
2.能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常。因此在Java中,Object类和Cloneable接口共同充当着抽象原型类,无需再次定义,不过由于调用clone方法需要捕获异常,每次调用的时候做处理比较麻烦,所以可以抽象出一个基类,来捕获,那么在具体子类中就不需要再次捕获(仅限于子类不需要处理异常的时候)。
3.这一点很重要。通常情况下,一个类包含一些成员对象,在使用原型模式克隆对象时,根据其成员对象是否也克隆,原型模式可以分为两种形式:深克隆和浅克隆。Java中的clone方法默认是浅克隆。
浅克隆
深克隆
4.Java语言提供的clone()方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足:
(1) 对任何的对象x,都有x.clone() !=x,即克隆对象与原对象不是同一个对象。
(2) 对任何的对象x,都有x.clone().getClass()==x.getClass(),即克隆对象与原对象的类型一样。
(3) 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。
五、实例分析
拿上面的邮件例子来说,由于展示,我们封装邮件中的标题、内容和附件三个字段,其中附件是一个类,包含了名字、类型和文件大小。首先采用默认的clone方式(浅克隆),即复制后的邮件中的附件与原邮件中的附件是同一对象;然后实现深克隆,即复制后的邮件中的附件与原邮件中的附件不是同一对象。
1.基类——为了方便子类调用clone不需要捕获异常
public class Prototype implements Cloneable { public Prototype cloneMe() { Prototype prototype = null; try { prototype = (Prototype) this.clone(); } catch (CloneNotSupportedException exception) { } return prototype; } }
2.附件子类
public class Attachment extends Prototype { // 附件名字 private String name; // 附件文档类型 private String type; // 附件大小 private long length; public Attachment(String name, String type, long length) { super(); this.name = name; this.type = type; this.length = length; } public void download() { System.out.println("下载了附件:" + name); } public String display() { return "Attachment [name=" + name + ", type=" + type + ", length=" + length + "]"; } @Override public boolean equals(Object obj) { Attachment a = (Attachment) obj; if(a.name.equals(name) && a.type.equals(type) && a.length == length) { return true; } return false; } }
3.邮件子类
public class Email extends Prototype { // 标题 private String title; // 内容 private String content; // 附件 private Attachment attachment; public Email(String title, String content, Attachment attachment) { super(); this.title = title; this.content = content; this.attachment = attachment; } public String display() { return "Email [title=" + title + ", content=" + content + ", attachment=" + attachment.display(); } public Attachment getAttachment() { return attachment; } @Override public boolean equals(Object obj) { Email e = (Email) obj; if(e.title.equals(title) && e.content.equals(content) && e.attachment.equals(attachment)) { return true; } return false; } }
4.客户端类
public class Test { public static void main(String[] args) { Email email = new Email("邮件标题", "邮件内容,哈哈哈..", new Attachment("附件标题", "文档", 45987)); System.out.println(email.display()); Email copyEmail = (Email) email.cloneMe(); System.out.println("邮件复制" + (email != copyEmail && email.equals(copyEmail) ? "成功" : "失败") ); Attachment attachment = email.getAttachment(); Attachment copyAttachment = copyEmail.getAttachment(); if(attachment.equals(copyAttachment)) { System.out.println("邮件附件内容一致"); } if(attachment == copyAttachment) { System.out.println("邮件附件未复制"); } else { System.out.println("邮件附件已复制"); } } }
可以看到,由于重写了子类的equals方法,所以当每个字段的内容相等时,认为该类的内容相同,但是进行复制后,地址是不同的,最后一个判断是用于判断邮件类调用cloneMe方法后,当中的附件对象是否也进行了复制,若地址相同,所以未进行复制,此时是浅克隆。
运行结果如下:
Email [title=邮件标题, content=邮件内容,哈哈哈.., attachment=Attachment [name=附件标题, type=文档, length=45987]
邮件复制成功
邮件附件内容一致
邮件附件未复制
5.深克隆修改
那么如何进行深克隆呢?很简单,只需要重写邮件对象的cloneMe方法,先调用父类的克隆方法获得自身拷贝的对象,然后自身的附件对象也调用cloneMe方法,将得到的对象再赋值给拷贝后的邮件对象。
public class Email extends Prototype {
// 标题
private String title;
// 内容
private String content;
// 附件
private Attachment attachment;
public Email(String title, String content, Attachment attachment) {
super();
this.title = title;
this.content = content;
this.attachment = attachment;
}
public String display() {
return "Email [title=" + title + ", content=" + content + ", attachment=" + attachment.display();
}
public Attachment getAttachment() {
return attachment;
}
@Override
public boolean equals(Object obj) {
Email e = (Email) obj;
if(e.title.equals(title) && e.content.equals(content) && e.attachment.equals(attachment)) {
return true;
}
return false;
}
@Override
public Email cloneMe() {
Email e = (Email) super.cloneMe();
e.attachment = (Attachment) attachment.cloneMe();
return e;
}
}
运行结果:
Email [title=邮件标题, content=邮件内容,哈哈哈.., attachment=Attachment [name=附件标题, type=文档, length=45987]
邮件复制成功
邮件附件内容一致
邮件附件已复制
六、模式优缺点
(一)优点
1.当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过一个已有实例可以提高新实例的创建效率。
2.可以动态增加或减少产品类。
3.原型模式提供了简化的创建结构。
4.可以使用深克隆的方式保存对象的状态。
(二)缺点
1.需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事,必须修改其源代码,违背了“开闭原则”。
2.在实现深克隆时需要编写较为复杂的代码。
(三)使用场景
在以下情况下可以使用原型模式:
1.创建新对象成本较大,新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其属性稍作修改。
2.如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占内存不大的时候,也可以使用原型模式配合备忘录模式来应用。相反,如果对象的状态变化很大,或者对象占用的内存很大,那么采用状态模式会比原型模式更好。
3.需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。
谢谢您的关注和阅读,文章不当之处还请您不吝赐教~~~
相关推荐
Java设计模式——原型模式 原型模式Java设计模式——原型模式概念使用场景Java里的克隆代码理解prototype(原型)问题总结优缺点模型优点模型缺点 概念 原型模式是创建型模式的最后一种,讲到原型模式就不得不提到...
设计模式——原型模式 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 原型模式是用场景:需要大量的基于某个基础原型进行微量修改而得到新原型时使用 """ fro
java23种设计模式 工厂模式 ,抽象工厂,建造者模式,单态模式,原型模式
java 设计模式——原型模式 克隆羊问题介绍原型模式原理,深入浅出易于理解
书名: 设计模式可复用面向对象软件的基础 英文原书名: Design Patterns:Elements of Reusable Object-Oriented software 作者: Erich Gamma 等 译者: 李英军 马晓星 蔡敏 刘建中 书号: 7-111-07575-7 页码: 254 定价...
C#设计模式(23种设计模式) 1. 单件模式(Singleton Pattern) 2. 抽象工厂(Abstract Factory) 3. 建造者模式(Builder) 4. 工厂方法模式(Factory Method) 5. 原型模式(Prototype) 结构型: 6. 适配器...
对象的克隆——原型模式,复杂对象的组装与创建——建造者模式,不兼容结构的协调——适配器模式,处理多维度变化——桥接模式,树形结构的处理——组合模式,扩展系统功能——装饰模式,深入浅出外观模式,实现对象...
3、完成毕业设计,积累项目经验,为找工作打基础。 ###应用场合: 1、夏日炎炎,在外工作一天,准备回家时,通过手机,提前将家里的空调启动,一回到家就能享受惬意的清凉。 2、在公司上班,亲友想用自己车库里的车...
3.5PrototypePattern(原型模式) 70 3.5.1定义 70 3.5.2现实中的拷贝-粘贴 71 3.5.3C#实例——颜色管理器 72 3.5.4Java实例——简单ToolBar 74 3.5.5ShallowCopy与DeepCopy 76 3.5.6优势和缺陷 82 3.5.7应用...
目录: 前 言 第一部分 大旗不挥,谁敢冲锋——热身篇 第1章 单一职责原则 1.1 我是“牛”类,我可以担任多职吗 1.2 绝杀技,打破你的传统思维 1.3 我单纯,所以我快乐 1.4 最佳实践 ...附录:23个设计模式
Pattern)原型模式(Prototype Pattern) 2 结构型模式:这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 适配器模式(Adapter Pattern)桥接模式(Bridge Pattern)...
3.5PrototypePattern(原型模式) 70 3.5.1定义 70 3.5.2现实中的拷贝-粘贴 71 3.5.3C#实例——颜色管理器 72 3.5.4Java实例——简单ToolBar 74 3.5.5ShallowCopy与DeepCopy 76 3.5.6优势和缺陷 82 3.5.7应用...
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:...
第7章 设计模式 单体模式 工厂模式 迭代器模式 装饰者模式 策略模式 外观模式 代理模式 中介者模式 观察者模式 小结 第8章 DOM和浏览器模式 关注分离 DOM脚本 事件 长期运行脚本 ...
1.2 Smalltalk MVC中的设计模式 1.3 描述设计模式 1.4 设计模式的编目 1.5 组织编目 1.6 设计模式怎样解决设计问题 1.7 怎样选择设计模式 1.8 怎样使用设计模式 第二章 实例研究:设计一个文档编辑器 2.1 设计问题...
(二) 确保对象的唯一性——单例模式 (三) 确保对象的唯一性——单例模式 (四) 确保对象的唯一性——单例模式 (五) 原型模式-Prototype Pattern 对象的克隆——原型模式(一) 对象的克隆——原型模式(二) ...
C++设计模式原理与实战视频...2-12 原型模式的实用工程技术——DRY原则与使用模式进行重构 3-1 适配器模式的定义、场景与实用工程技术:对象适配与类的适配 3-2 门面(外观)模式Facade的定义、场景与实用工程技术:LOD
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:...
综合实例——Bug管理系统 (3)设计模式样例(24个讲解样例程序) pattern/src/principle/liskovsubstitution//10.3.2里氏代换原则 pattern/src/creation/factorymethod //11.1工厂方法模式 pattern/src/creation/...