转:http://www.impeng.org/prototype.html
原型模式(Prototype)属于创建型模式,实现了对象的创建;具体而言,它实现了对象的复制(拷贝)。
关于对象的复制,我们需要考虑以下两个问题:
1.对象实时状态的复制;
2.对象引用成员变量的复制。
如果通过new方式来实例化对象,只能得到对象的初始状态,这显然不行。在Java中,所有类都继承于Object类,而Object有一个clone()方法。通过查看JDK文档,该clone()方法虽然能够实现实时状态的复制(解决了问题1),但是只能实现“浅拷贝”,即只能实现基本数据类型(包含String)的拷贝,不能实现引用数据类型的拷贝,并不能满足原型模式的要求(问题2不能得到解决)。
怎么办?重写类的clone()方法,通过对象的写入写出来实现“深拷贝”和实时状态复制。重写(Override)需要遵循的条件是:方法名,参数列表和父类一致;返回值是父类返回值的子类。重写clone方法完全满足了条件。(无参数,方法名一致,返回的所有类都是Object的子类;当然可以不重写,方法名改用其它,但是不建议。)
可以想象,若一个类含有若干个引用数据类型(类),而该引用数据类型(类)中又含有若干个引用数据类型…这样嵌套下去,需要复制的就不是一个类,而是一个“类树”(树结构)。Java通过将对象序列化到文件或内存,底层封装了对类树的遍历和复制过程。这个机制已经封装,我们无需了解。
考虑这样一个应用场景:
一个RPG游戏,英雄类含有两个成员变量:一个是英雄名字,为普通成员变量;一个是英雄属性,为引用成员变量(封装了英雄的状态,如生命值、魔法值)。为了实现对象实时拷贝的高效率,我们选择将对象序列化到内存(不是文件(硬盘))。通过输入输出流实现对象的“深拷贝”。
具体的实现代码如下:
- package com.csufox.Prototype;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- class Property implements java.io.Serializable{
- int blood = 100; //生命值
- int magic = 100; //魔法值
- }
- class Hero implements java.io.Serializable{ //需要实现序列化接口
- String name = "英雄";
- Property property = new Property(); //含有引用成员,需要“深”拷贝
- public Hero clone(){
- Hero copy = null;
- try{
- //实例化ObjectOutputStream,写入字节数组输出流
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baos);
- oos.writeObject(this);
- byte[] data = baos .toByteArray(); //data是字节数组,存储着对象的拷贝
- //实例化ObjectInputStream,从字节数组输入流中读取对象
- ByteArrayInputStream bais = new ByteArrayInputStream(data);
- ObjectInputStream ois = new ObjectInputStream(bais);
- copy = (Hero)ois.readObject();
- }catch(Exception ex){ex.printStackTrace();}
- return copy;
- }
- }
- public class Prototype {
- public static void main(String[] args) throws Exception {
- Hero hero = new Hero();
- hero.property.blood -= 20; //原英雄状态发生改变,生命值减少20
- Hero copy = hero.clone();
- System.out.println(copy.property.blood); //输出“80”,说明实现了实时拷贝和深拷贝
- }
- }
由上述代码可知,原型模式具有以下特点:
- 实现Serializable序列化接口;
- 在类中重写clone()方法(由Object继承而来);
- 通过Java I/0,将对象写入内存,然后再从内存读取(反序列化)。
-EOF-