原型模式(原型设计模式)详解-亚博电竞手机版
设计模式
2021年02月06日 15:32
6
原型模式的定义与特点
原型(prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,windows 操作系统的安装通常较耗时,如果复制就快了很多。在生活中复制的例子非常多,这里不一一列举了。原型模式的优点:
- java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
原型模式的缺点:
- 需要为每一个类都配置一个 clone 方法
- clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
- 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
原型模式的结构与实现
由于 java 提供了对象的 clone() 方法,所以用 java 实现原型模式很简单。1. 模式的结构
原型模式包含以下主要角色。- 抽象原型类:规定了具体原型对象必须实现的接口。
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
2. 模式的实现
原型模式的克隆分为浅克隆和深克隆。- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
- 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
//具体原型类 class realizetype implements cloneable { realizetype() { system.out.println("具体原型创建成功!"); } public object clone() throws clonenotsupportedexception { system.out.println("具体原型复制成功!"); return (realizetype) super.clone(); } } //原型模式的测试类 public class prototypetest { public static void main(string[] args) throws clonenotsupportedexception { realizetype obj1 = new realizetype(); realizetype obj2 = (realizetype) obj1.clone(); system.out.println("obj1==obj2?" (obj1 == obj2)); } }程序的运行结果如下:
具体原型创建成功! 具体原型复制成功! obj1==obj2?false
原型模式的应用实例
【例1】用原型模式模拟“孙悟空”复制自己。 分析:孙悟空拔下猴毛轻轻一吹就变出很多孙悟空,这实际上是用到了原型模式。这里的孙悟空类 sunwukong 是具体原型类,而 java 中的 cloneable 接口是抽象原型类。 同前面介绍的猪八戒实例一样,由于要显示孙悟空的图像(点击此处下载该程序所要显示的孙悟空的图片),所以将孙悟空类定义成面板 jpanel 的子类,里面包含了标签,用于保存孙悟空的图像。 另外,重写了 cloneable 接口的 clone() 方法,用于复制新的孙悟空。访问类可以通过调用孙悟空的 clone() 方法复制多个孙悟空,并在框架窗体 jframe 中显示。图 2 所示是其结构图。import java.awt.*; import javax.swing.*; class sunwukong extends jpanel implements cloneable { private static final long serialversionuid = 5543049531872119328l; public sunwukong() { jlabel l1 = new jlabel(new imageicon("src/wukong.jpg")); this.add(l1); } public object clone() { sunwukong w = null; try { w = (sunwukong) super.clone(); } catch (clonenotsupportedexception e) { system.out.println("拷贝悟空失败!"); } return w; } } public class prototypewukong { public static void main(string[] args) { jframe jf = new jframe("原型模式测试"); jf.setlayout(new gridlayout(1, 2)); container contentpane = jf.getcontentpane(); sunwukong obj1 = new sunwukong(); contentpane.add(obj1); sunwukong obj2 = (sunwukong) obj1.clone(); contentpane.add(obj2); jf.pack(); jf.setvisible(true); jf.setdefaultcloseoperation(jframe.exit_on_close); } }程序的运行结果如图 3 所示。
public class prototypecitation { public static void main(string[] args) throws clonenotsupportedexception { citation obj1 = new citation("张三", "同学:在2016学年第一学期中表现优秀,被评为三好学生。", "韶关学院"); obj1.display(); citation obj2 = (citation) obj1.clone(); obj2.setname("李四"); obj2.display(); } } //奖状类 class citation implements cloneable { string name; string info; string college; citation(string name, string info, string college) { this.name = name; this.info = info; this.college = college; system.out.println("奖状创建成功!"); } void setname(string name) { this.name = name; } string getname() { return (this.name); } void display() { system.out.println(name info college); } public object clone() throws clonenotsupportedexception { system.out.println("奖状拷贝成功!"); return (citation) super.clone(); } }程序运行结果如下:
奖状创建成功! 张三同学:在2016学年第一学期中表现优秀,被评为三好学生。韶关学院 奖状拷贝成功! 李四同学:在2016学年第一学期中表现优秀,被评为三好学生。韶关学院
原型模式的应用场景
原型模式通常适用于以下场景。- 对象之间相同或相似,即只是个别的几个属性不同的时候。
- 创建对象成本较大,例如初始化时间长,占用cpu太多,或者占用网络资源太多等,需要优化资源。
- 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
- 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。
原型模式的扩展
原型模式可扩展为带原型管理器的原型模式,它在原型模式的基础上增加了一个原型管理器 prototypemanager 类。该类用 hashmap 保存多个复制的原型,client 类可以通过管理器的 get(string id) 方法从中获取复制的原型。其结构图如图 5 所示。import java.util.*; interface shape extends cloneable { public object clone(); //拷贝 public void countarea(); //计算面积 } class circle implements shape { public object clone() { circle w = null; try { w = (circle) super.clone(); } catch (clonenotsupportedexception e) { system.out.println("拷贝圆失败!"); } return w; } public void countarea() { int r = 0; system.out.print("这是一个圆,请输入圆的半径:"); scanner input = new scanner(system.in); r = input.nextint(); system.out.println("该圆的面积=" 3.1415 * r * r "\n"); } } class square implements shape { public object clone() { square b = null; try { b = (square) super.clone(); } catch (clonenotsupportedexception e) { system.out.println("拷贝正方形失败!"); } return b; } public void countarea() { int a = 0; system.out.print("这是一个正方形,请输入它的边长:"); scanner input = new scanner(system.in); a = input.nextint(); system.out.println("该正方形的面积=" a * a "\n"); } } class prototypemanager { private hashmap运行结果如下所示:ht = new hashmap (); public prototypemanager() { ht.put("circle", new circle()); ht.put("square", new square()); } public void addshape(string key, shape obj) { ht.put(key, obj); } public shape getshape(string key) { shape temp = ht.get(key); return (shape) temp.clone(); } } public class prototypeshape { public static void main(string[] args) { prototypemanager pm = new prototypemanager(); shape obj1 = (circle) pm.getshape("circle"); obj1.countarea(); shape obj2 = (shape) pm.getshape("square"); obj2.countarea(); } }
这是一个圆,请输入圆的半径:3 该圆的面积=28.2735 这是一个正方形,请输入它的边长:3 该正方形的面积=9
展开全文