设计模式-原型模式
2016-03-14 21:30
357 查看
1.原型模式的介绍
原型模式是一个创建型的模式。原型二字表明了该模式应该有一个样板的实例,用户从这个样板对象中复制出一个内部属性一致的对象,这个过程也就是我们俗称的“克隆”。被复制的实例就是我们所称的“原型”,这个原型是可以定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下 ,复制一个已经存在的实例可使程序运行的更高效。2.原型模式的定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。3.原型模式的使用场景
3.1 类初始化需要消耗非常多的资源,这资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。3.2 通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可以使用原型模式。
3.3 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
4.UML类图
5.深拷贝和浅拷贝
5.1 深拷贝(深克隆)深拷贝把要复制的对象所引用的对象复制了一遍5.2 浅拷贝(浅克隆)浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
5.3 Java中对象的克隆,为了获取对象的一份拷贝,可以利用Object类的clone()方法。必须遵循下面三点:
A.在派生类中覆盖基类的clone()方法,并声明为public[Object类中的clone()放为
protected]
B.在派生类中的clone()方法中,调用super.clone()。
C.在派生类中实现Cloneable接口
6.new 和 clone 的区别
new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。而clone在第一步是和new相似的, 都是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。
7.实现
1.创建一个商场进货单对象A,含有商场名称和水果。另外一家商场B想按照商场A进货,现在只需将进货单的商场名称修改即可。1.1.原型类
public class Mark implements Cloneable{ String name; ArrayList<String> list = new ArrayList<>(); public Mark() { } protected Mark clone() { try { Mark mark = (Mark) super.clone(); mark.name = this.name; mark.list = this.list; return mark; } catch (Exception e) { } return null; } public String getName() { return name; } public void setName(String name) { this.name = name; } public ArrayList<String> getList() { return list; } public void setList(ArrayList<String> list) { this.list = list; } public void addFruit(String fruit) { this.list.add(fruit); } public void showMark() { System.out.println("/***********" + name + " Start***********/"); System.out.println("Nmae : " + name); for (String fruit: list) { System.out.println("fruit: " + fruit); } System.out.println("/************" + name + " End************/"); } }
1.2.测试类
public class TestPrototype { public static void main(String[] args) { Mark mark_0 = new Mark(); mark_0.setName("家乐福"); mark_0.addFruit("苹果"); mark_0.addFruit<span style="font-family: Arial, Helvetica, sans-serif;">("梨");</span> mark_0.addFruit("橙汁"); mark_0.showMark(); Mark mark_1 = mark_0.clone(); mark_1.showMark(); mark_1.setName("华润万家"); mark_1.showMark(); mark_0.showMark(); } }
1.3.输出结果
/***********家乐福 Start***********/ Nmae : 家乐福 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************家乐福 End************/ /***********家乐福 Start***********/ Nmae : 家乐福 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************家乐福 End************/ /***********华润万家 Start***********/ Nmae : 华润万家 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************华润万家 End************/ /***********家乐福 Start***********/ Nmae : 家乐福 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************家乐福 End************/
mark_1是通过mark_0.clone()创建的。根据输出结果,可以明显看出mark_0和mark_1的
输出结果是一致的,即mark_1是mark_0的一份拷贝。在mark_1中修改了商场的名称,并不影响
mark_0的内容。
需要注意的是通过clone()拷贝对象并不会执行构造函数。
2.商场B想在进货单中添加西瓜和火龙果两种水果,是否对商场A有影响呢?
2.1 测试类
public class TestPrototype { public static void main(String[] args) { Mark mark_0 = new Mark(); mark_0.setName("家乐福"); mark_0.addFruit("苹果"); mark_0.addFruit("梨"); mark_0.addFruit("橙汁"); mark_0.showMark(); Mark mark_1 = mark_0.clone(); mark_1.showMark(); mark_1.setName("华润万家"); mark_1.showMark(); mark_1.addFruit("西瓜"); mark_1.addFruit("火龙果"); mark_1.showMark(); mark_0.showMark(); } }
2.2 输出结果
/***********家乐福 Start***********/ Nmae : 家乐福 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************家乐福 End************/ /***********家乐福 Start***********/ Nmae : 家乐福 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************家乐福 End************/ /***********华润万家 Start***********/ Nmae : 华润万家 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************华润万家 End************/ /***********华润万家 Start***********/ Nmae : 华润万家 fruit : 苹果 fruit : 梨 fruit : 橙汁 fruit : 西瓜 fruit : 火龙果 /************华润万家 End************/ /***********家乐福 Start***********/ Nmae : 家乐福 fruit : 苹果 fruit : 梨 fruit : 橙汁 fruit : 西瓜 fruit : 火龙果 /************家乐福 End************/
通过输出结果,可以明显看到商场A的进货单在商场B修改的同时也修改了。这是怎么回事呢?在前面我们提过深拷贝和浅拷贝的区别,在上文中Mark的clone()方法只是简单地进行了浅拷贝,mark_1中的引用类型指向了mark_0的引用list并没有重新构造一个对象,然后再mark_1将西瓜和火龙果添加至引用list时,实际上是将其添加到mark_0中的引用对象中,意味着mark_0和mark_1的引用对象是同一个。现在发现了这个问题,该如何解决?之前引用的是浅拷贝,如果采用的深拷贝是什么效果,不妨试一下。
3
3.1 修改clone()方法,将引用对象也进行拷贝
protected Mark clone() { try { Mark mark = (Mark) super.clone(); mark.name = this.name; mark.list = (ArrayList<String>) this.list.clone(); return mark; } catch (Exception e) { } return null; }3.2 输出结果
/***********家乐福 Start***********/ Nmae : 家乐福 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************家乐福 End************/ /***********家乐福 Start***********/ Nmae : 家乐福 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************家乐福 End************/ /***********华润万家 Start***********/ Nmae : 华润万家 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************华润万家 End************/ /***********华润万家 Start***********/ Nmae : 华润万家 fruit : 苹果 fruit : 梨 fruit : 橙汁 fruit : 西瓜 fruit : 火龙果 /************华润万家 End************/ /***********家乐福 Start***********/ Nmae : 家乐福 fruit : 苹果 fruit : 梨 fruit : 橙汁 /************家乐福 End************/输出结果显示,在修改商场B的进货列表时,商场的进货列表并未受到影响。如上述代码所示,将mark.list指向this.list的一份拷贝,而不是mark.list本身。
8.总结
原型模式是一个非常简单的模式,它的核心问题是对原始对象进行拷贝,在这个模式中需要注意的是深拷贝和浅拷贝的问题。在实际运用中,为了避免不必要的错误,建议尽量使用深拷贝,避免对原始对象的影响.相关文章推荐
- Activity周期、加载模式理解
- PS——书籍装帧
- Python自然语言处理 - 系列四
- JS基础回顾,小练习(DOM元素)
- 2016/3/14 开始自己的编程之路
- 把一元硬币换成1分,2分5分的硬币有几种不同的方法
- 网页
- bash shell的编程
- OpenMP中的常用函数
- 【1】-x86架构内核的配置和编译
- Specified key was too long; max key length is 767 bytes
- CodeForces 219D-H - Choosing Capital for Treeland-树DP
- Windows7 C盘 不能新建文件 修改权限 更改用户
- 网页分块设计
- Jquery validate配置项
- 机器学习实践中的 7 种常见错误
- Collection,List,Set和Map用法和区别
- The difference between Python __init__ and __new__
- Yii2.0 数据库查询方法
- Emgu在VS2012中的安装和配置