设计模式之原型模式

来源:互联网 发布:box2d js教程 编辑:程序博客网 时间:2024/06/17 04:40
§模式名称:原型模式(Prototype)

§模式动机:有些对象的创建过程较为复杂,而且有时候需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。这就是原型模式的动机。

§原型模式(Prototype Pattern):它是一种对象创建型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式允许一个对象再创建另外一个可定制的对象,无需知道任何创建的细节。

§工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝原型自己来实现创建过程。

下面在一个例子中使用原型模式,Book类是一个标准的Java bean,代码如下:
package com.prototype;public class Book {private int id;private String name;public Book(int id, String name) {super();this.id = id;this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}}
Person类也是一个Java bean,其成员变量中包含对Book类的引用,且Person类实现了Cloneable接口,代码如下:
package com.prototype;import java.util.ArrayList;import java.util.List;public class Person implements Cloneable {private int id;private String name;private int age;private List<Book> books;public Person() {}public Person(int id, String name, int age, List<Book> books) {this.id = id;this.name = name;this.age = age;this.books = books;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public List<Book> getBooks() {return books;}public void setBooks(List<Book> books) {this.books = books;}@Overrideprotected Object clone() throws CloneNotSupportedException {List<Book> list = new ArrayList<Book>();if(this.books != null){list.addAll(this.books);}Person p = new Person(id, name, age, list);return p;}}
由于Person类的成员变量books并非基本的数据类型,而是引用类型,所以在实现clone方法时,不能直接调用父类的clone方法,这里涉及到一个深复制和浅复制的问题:

§深克隆与浅克隆
§在浅克隆中,被复制对象的所有变量都具有与原来的对象相同的值,而所有对其它对象的引用仍然指向原来的对象。换言之,浅克隆仅仅复制所考虑的对象,而不复制它所引用的对象,也就是说其中的成员对象并不复制。在浅克隆中,当对象被复制时它所包含的成员对象却没有被复制。
§在深克隆中,对象在复制的同时其成员对象也将复制。

所以在上面的Person类的clone方法中,我们返回的Person类的对象p,其构造方法里传入了一个新的list,该list将需要复制的Person类的对象的books都加入进来了,如果不这么做,那么在调用clone方法将一个Person类的对象p1复制为p2时,会导致p1和p2中的books成员变量,引用到同一个对象上了。

下面是测试代码:
package com.prototype;import java.util.ArrayList;public class Test {public static void main(String[] args) throws CloneNotSupportedException {Book book1 = new Book(1, "Chinese");Person p1 = new Person(1, "zhangsan", 20, new ArrayList<Book>());p1.getBooks().add(book1);Person p2 = (Person) p1.clone();p2.getBooks().add(new Book(2, "Math"));System.out.println("p1: " + p1.toString());System.out.println("-------------------");System.out.println("p2: " + p2.toString());}}
测试代码执行的结果如下:

可以看到p2在增加了一个Book后,p1中的books并没有发生改变,证明p1和p2中的books并不是引用的同一个对象,而是分开的

原型模式的优点如下
§当创建新的对象实例较为复杂时,使用原型模式可以简化的对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
§原型模式允许动态增加或减少产品类。
§原型模式具有给一个应用软件动态加载新功能的能力。
§产品类不需要非得有任何事先确定的等级结构 。

原型模式的缺点如下
§原型模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。
§原型模式的另一个缺点是在实现深克隆时需要编写较为复杂的代码。

模式使用
§在下面的情况下可以使用原型模式:
§创建新对象成本较大(CPU,初始化)
§系统要保存对象的状态,对象状态变化很小。
§当一个类的实例只有几个不同状态组合时,建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化更为方便。

0 0