原型模式是一种创建型模式。通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。今天我们就简单来说一说这个模式。
意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
结构
原型模式的基本结构如下:
这里涉及的参与者有如下几种:
植物大战僵尸是曾经比较流行的一个游戏,本文以僵尸作为原型模式的示例:
package com.wangmengjun.tutorial.designpattern.prototype;
public abstract class Zombie implements Cloneable {
//速度 private int speed;
//血量 private int blood;
//其它特点 private OtherInfo otherInfo;
public Zombie clone() { Zombie cloneObj = null; try { cloneObj =(Zombie)super.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return cloneObj;
}
/** * @return the speed */ public int getSpeed() { return speed; }
/** * @param speed the speed to set */ public void setSpeed(int speed) { this.speed = speed; }
/** * @return the blood */ public int getBlood() { return blood; }
/** * @param blood the blood to set */ public void setBlood(int blood) { this.blood = blood; }
/** * @return the otherInfo */ public OtherInfo getOtherInfo() { return otherInfo; }
/** * @param otherInfo the otherInfo to set */ public void setOtherInfo(OtherInfo otherInfo) { this.otherInfo = otherInfo; }
@Override public String toString() { return "Zombie [speed=" + speed + ", blood=" + blood + ", otherInfo=" + otherInfo + "]"; }
}
其中,其它属性包含攻击力,描述等,OtherInfo示例如下:
package com.wangmengjun.tutorial.designpattern.prototype;
import java.io.Serializable;
public class OtherInfo implements Cloneable, Serializable {
private static final long serialVersionUID = 7472104309423905449L;
private String desc;
private int attact;
/** * @return the desc */ public String getDesc() { return desc; }
/** * @param desc the desc to set */ public void setDesc(String desc) { this.desc = desc; }
/** * @return the attact */ public int getAttact() { return attact; }
/** * @param attact the attact to set */ public void setAttact(int attact) { this.attact = attact; }
/** * @return the serialversionuid */ public static long getSerialversionuid() { return serialVersionUID; }
@Override public String toString() { return "OtherInfo [desc=" + desc + ", attact=" + attact + ", getDesc()=" + getDesc() + ", getAttact()=" + getAttact() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; }
}
package com.wangmengjun.tutorial.designpattern.prototype;
public class DanceZombie extends Zombie {
}
package com.wangmengjun.tutorial.designpattern.prototype;
public class PlasticZombie extends Zombie {
}
package com.wangmengjun.tutorial.designpattern.prototype;
public class Client {
public static void main(String[] args) { Zombie danceZombie = new DanceZombie(); danceZombie.setBlood(100); danceZombie.setSpeed(5); OtherInfo otherInfo = new OtherInfo(); otherInfo.setAttact(50);; otherInfo.setDesc("会跳舞"); danceZombie.setOtherInfo(otherInfo); System.out.println("原始对象==>" + danceZombie);
//克隆一个跳舞僵尸 Zombie cloneDanceZombie = (DanceZombie)danceZombie.clone(); System.out.println("克隆对象==>" + cloneDanceZombie);
//修改一下其它信息对象 cloneDanceZombie.getOtherInfo().setAttact(80); cloneDanceZombie.getOtherInfo().setDesc("新的描述");
//输出 System.out.println(); System.out.println("原始对象的OtherInfo==>" +danceZombie.getOtherInfo()); System.out.println("克隆对象的OtherInfo==>" +cloneDanceZombie.getOtherInfo()); System.out.println(danceZombie.getOtherInfo() == cloneDanceZombie.getOtherInfo()); }}
输出结果:
原始对象==>Zombie [speed=5, blood=100, otherInfo=OtherInfo [desc=会跳舞, attact=50, getDesc()=会跳舞, getAttact()=50, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=1555009629, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@5caf905d]]克隆对象==>Zombie [speed=5, blood=100, otherInfo=OtherInfo [desc=会跳舞, attact=50, getDesc()=会跳舞, getAttact()=50, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=1555009629, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@5caf905d]]
原始对象的OtherInfo==>OtherInfo [desc=新的描述, attact=80, getDesc()=新的描述, getAttact()=80, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=1555009629, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@5caf905d]克隆对象的OtherInfo==>OtherInfo [desc=新的描述, attact=80, getDesc()=新的描述, getAttact()=80, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=1555009629, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@5caf905d]true
浅拷贝
从上述示例中可以看出,其它信息OtherInfo,原型对象和克隆对象是同一个。如下图所示:这是因为继承自java.lang.Object#clone()方法采用的是浅拷贝。
接下来,我们来实现深度拷贝,这样才能使克隆出来的对象和原始对象是独立的。
OtherInfo增加clone方法
public Object clone() { Object cloneObj = null; try { cloneObj =super.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return cloneObj;
}
然后修改僵尸类的clone方法:
public Zombie clone() { Zombie cloneObj = null; try { cloneObj =(Zombie)super.clone(); cloneObj.otherInfo =(OtherInfo)this.otherInfo.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return cloneObj;
}
增加了
cloneObj.otherInfo =(OtherInfo)this.otherInfo.clone();
然后重新执行Client类
原始对象==>Zombie [speed=5, blood=100, otherInfo=OtherInfo [desc=会跳舞, attact=50, getDesc()=会跳舞, getAttact()=50, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=1555009629, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@5caf905d]]克隆对象==>Zombie [speed=5, blood=100, otherInfo=OtherInfo [desc=会跳舞, attact=50, getDesc()=会跳舞, getAttact()=50, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=41359092, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@27716f4]]
原始对象的OtherInfo==>OtherInfo [desc=会跳舞, attact=50, getDesc()=会跳舞, getAttact()=50, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=1555009629, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@5caf905d]克隆对象的OtherInfo==>OtherInfo [desc=新的描述, attact=80, getDesc()=新的描述, getAttact()=80, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=41359092, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@27716f4]false
从打印以看到原始对象和克隆对象的值都是独立的,实现了深度拷贝。
方法二:实现序列化反序列化来做
其它信息类和僵尸类实现序列化接口
package com.wangmengjun.tutorial.designpattern.prototype;
import java.io.Serializable;
public class OtherInfo implements Cloneable, Serializable {
private static final long serialVersionUID = 7472104309423905449L;
private String desc;
private int attact;
/** * @return the desc */ public String getDesc() { return desc; }
/** * @param desc the desc to set */ public void setDesc(String desc) { this.desc = desc; }
/** * @return the attact */ public int getAttact() { return attact; }
/** * @param attact the attact to set */ public void setAttact(int attact) { this.attact = attact; }
/** * @return the serialversionuid */ public static long getSerialversionuid() { return serialVersionUID; }
public Object clone() { Object cloneObj = null; try { cloneObj =super.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return cloneObj;
}
@Override public String toString() { return "OtherInfo [desc=" + desc + ", attact=" + attact + ", getDesc()=" + getDesc() + ", getAttact()=" + getAttact() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; }
}
Zombie类增加deepCopy方法
package com.wangmengjun.tutorial.designpattern.prototype;
import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;
public abstract class Zombie implements Cloneable, Serializable {
private static final long serialVersionUID = -8807086510310094011L;
//速度 private int speed;
//血量 private int blood;
//其它特点 private OtherInfo otherInfo;
public Object deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject();
}
public Zombie clone() { Zombie cloneObj = null; try { cloneObj =(Zombie)super.clone(); cloneObj.otherInfo =(OtherInfo)this.otherInfo.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return cloneObj;
}
/** * @return the speed */ public int getSpeed() { return speed; }
/** * @param speed the speed to set */ public void setSpeed(int speed) { this.speed = speed; }
/** * @return the blood */ public int getBlood() { return blood; }
/** * @param blood the blood to set */ public void setBlood(int blood) { this.blood = blood; }
/** * @return the otherInfo */ public OtherInfo getOtherInfo() { return otherInfo; }
/** * @param otherInfo the otherInfo to set */ public void setOtherInfo(OtherInfo otherInfo) { this.otherInfo = otherInfo; }
@Override public String toString() { return "Zombie [speed=" + speed + ", blood=" + blood + ", otherInfo=" + otherInfo + "]"; }
}
同样在Client端,调用deepClone方法来实现深拷贝,如:
package com.wangmengjun.tutorial.designpattern.prototype;
import java.io.IOException;
public class Client {
public static void main(String[] args) throws ClassNotFoundException, IOException { Zombie danceZombie = new DanceZombie(); danceZombie.setBlood(100); danceZombie.setSpeed(5); OtherInfo otherInfo = new OtherInfo(); otherInfo.setAttact(50);; otherInfo.setDesc("会跳舞"); danceZombie.setOtherInfo(otherInfo); System.out.println("原始对象==>" + danceZombie);
//克隆一个跳舞僵尸 Zombie cloneDanceZombie = (DanceZombie)danceZombie.deepClone(); System.out.println("克隆对象==>" + cloneDanceZombie);
//修改一下其它信息对象 cloneDanceZombie.getOtherInfo().setAttact(80); cloneDanceZombie.getOtherInfo().setDesc("新的描述");
//输出 System.out.println(); System.out.println("原始对象的OtherInfo==>" +danceZombie.getOtherInfo()); System.out.println("克隆对象的OtherInfo==>" +cloneDanceZombie.getOtherInfo()); System.out.println(danceZombie.getOtherInfo() == cloneDanceZombie.getOtherInfo()); }}
运行结果如下,可见已经满足了深拷贝。
原始对象==>Zombie [speed=5, blood=100, otherInfo=OtherInfo [desc=会跳舞, attact=50, getDesc()=会跳舞, getAttact()=50, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=1555009629, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@5caf905d]]克隆对象==>Zombie [speed=5, blood=100, otherInfo=OtherInfo [desc=会跳舞, attact=50, getDesc()=会跳舞, getAttact()=50, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=2040495657, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@799f7e29]]
原始对象的OtherInfo==>OtherInfo [desc=会跳舞, attact=50, getDesc()=会跳舞, getAttact()=50, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=1555009629, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@5caf905d]克隆对象的OtherInfo==>OtherInfo [desc=新的描述, attact=80, getDesc()=新的描述, getAttact()=80, getClass()=class com.wangmengjun.tutorial.designpattern.prototype.OtherInfo, hashCode()=2040495657, toString()=com.wangmengjun.tutorial.designpattern.prototype.OtherInfo@799f7e29]false
这样,深拷贝的方法就实现了。
一般情况下
一般情况下,我们可以将一些具体的原型对象放入到缓存,然后使用的时候直接拿出来修改某些值即可。如:植物大战僵尸中的场景:一大波僵尸来袭,一下子有很多的僵尸出现在界面上。这个时候,我们只要拿到一个具体的僵尸对象,然后通过复制克隆快速产生对象,同类型的僵尸其区别只要修改一下位置即可。
原型模式的优缺点:
优点:
(1):当创建对象的实例较为复杂的时候,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高实例的创建效率。
(2):扩展性好,由于原型模式提供了抽象原型类,在客户端针对抽象原型类进行编程,而将具体原型类写到配置文件中,增减或减少产品对原有系统都没有影响。
(3):原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式不需要这样,圆形模式中产品的复制是通过封装在类中的克隆方法实现的,无需专门的工厂类来创建产品。
(4):可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点:
(1):需要为每一个类配置一个克隆方法,而且该克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
(2):在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重签到引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。
原型模式的适用环境:
参考
[1]. 阎宏. Java与模式.电子工业出版社
[2]. Erich Gamma. 设计模式-可复用面向对象软件的基础. 机械工业出版社.
[3]. https://www.jianshu.com/p/6e51ba4dc992