前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >原型模式浅析

原型模式浅析

作者头像
孟君
发布2020-05-11 10:36:20
3740
发布2020-05-11 10:36:20
举报

原型模式是一种创建型模式。通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。今天我们就简单来说一说这个模式。

一. 原型模式的基本介绍

意图

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

结构

原型模式的基本结构如下:

这里涉及的参与者有如下几种:

  • Prototype(抽象原型)
    • 通常由一个接口或者抽象类实现。此角色给出所有的具体原型所需的接口
  • Concrete Prototype(具体原型)
    • 实现一个克隆自身的操作
  • Client(客户)
    • 提出创建对象的请求,让一个原型克隆自身从而创建一个新的对象。

二. 原型模式的示例

植物大战僵尸是曾经比较流行的一个游戏,本文以僵尸作为原型模式的示例:

  • Zombie(抽象原型)
代码语言:javascript
复制
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示例如下:

代码语言:javascript
复制
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() + "]";  }

}
  • DanceZombie和PlatsicZombie(具体原型
代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.prototype;
public class DanceZombie extends Zombie {
}
代码语言:javascript
复制
package com.wangmengjun.tutorial.designpattern.prototype;
public class PlasticZombie extends Zombie {
}
  • Client(客戶端)
代码语言:javascript
复制
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());  }}

输出结果:

代码语言:javascript
复制
原始对象==>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()方法采用的是浅拷贝。

接下来,我们来实现深度拷贝,这样才能使克隆出来的对象和原始对象是独立的。

三. 深度拷贝

  • 方法一:修改其它信息类和僵尸类的clone方法

OtherInfo增加clone方法

代码语言:javascript
复制
  public Object clone() {    Object cloneObj = null;    try {      cloneObj =super.clone();    }catch(CloneNotSupportedException e) {      e.printStackTrace();    }    return cloneObj;
  }

然后修改僵尸类的clone方法:

代码语言:javascript
复制
  public Zombie clone() {    Zombie cloneObj = null;    try {      cloneObj =(Zombie)super.clone();      cloneObj.otherInfo =(OtherInfo)this.otherInfo.clone();    }catch(CloneNotSupportedException e) {      e.printStackTrace();    }    return cloneObj;
  }

增加了

代码语言:javascript
复制
cloneObj.otherInfo =(OtherInfo)this.otherInfo.clone();

然后重新执行Client类

代码语言:javascript
复制
原始对象==>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

从打印以看到原始对象和克隆对象的值都是独立的,实现了深度拷贝。

方法二:实现序列化反序列化来做

其它信息类和僵尸类实现序列化接口

代码语言:javascript
复制
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方法

代码语言:javascript
复制
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方法来实现深拷贝,如:

代码语言:javascript
复制
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());  }}

运行结果如下,可见已经满足了深拷贝。

代码语言:javascript
复制
原始对象==>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):在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重签到引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。

原型模式的适用环境:

  • 创建新对象成本较大(例如初始化时间长,占用CPU多或占太多网络资源),新对象可以通过复制已有对象来获得,如果相似对象,则可以对其成员变量稍作修改。
  • 系统要保存对象的状态,而对象的状态很小。
  • 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的组合状态,通过复制原型对象得到新实例可以比使用构造函数创建一个新实例更加方便。

参考

[1]. 阎宏. Java与模式.电子工业出版社

[2]. Erich Gamma. 设计模式-可复用面向对象软件的基础. 机械工业出版社.

[3]. https://www.jianshu.com/p/6e51ba4dc992

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 孟君的编程札记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 原型模式的基本介绍
  • 二. 原型模式的示例
  • 三. 深度拷贝
  • 四. 小结
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档