建立型模式-原型模式(对象的克隆)

1. 定义

原型模式(Prototype Pattern):使用原型实例指定建立对象的种类,而且经过拷贝这些原型建立新的对象。原型模式是一种对象建立型模式。git

2. 结构

原型模式的工做原理很简单:将一个原型对象传给那个要发动建立的对象,这个要发动建立的对象经过请求原型对象拷贝本身来实现建立过程。因为在软件系统中咱们常常会遇到须要建立多个相同或者类似对象的状况,所以原型模式在真实开发中的使用频率仍是很是高的。原型模式是一种“另类”的建立型模式,建立克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。编程

须要注意的是经过克隆方法所建立的对象是全新的对象,它们在内存中拥有新的地址,一般对克隆所产生的对象进行修改对原型对象不会形成任何影响,每个克隆对象都是相互独立的。经过不一样的方式修改能够获得一系列类似但不彻底相同的对象。结构如图:设计模式

原型模式

在原型模式结构图中包含以下几个角色:ide

  • Prototype(抽象原型类):它是声明克隆方法的接口,是全部具体原型类的公共父类,能够是抽象类也能够是接口,甚至还能够是具体实现类。
  • ConcretePrototype(具体原型类):它实如今抽象原型类中声明的克隆方法,在克隆方法中返回本身的一个克隆对象。
  • Client(客户类):让一个原型对象克隆自身从而建立一个新的对象,在客户类中只须要直接实例化或经过工厂方法等方式建立一个原型对象,再经过调用该对象的克隆方法便可获得多个相同的对象。因为客户类针对抽象原型类Prototype编程,所以用户能够根据须要选择具体原型类,系统具备较好的可扩展性,增长或更换具体原型类都很方便。

3. 代码实现

1 对象的克隆方法函数

  • 通用实现方法

通用的克隆实现方法是在具体原型类的克隆方法中实例化一个与自身类型相同的对象并将其返回,并将相关的参数传入新建立的对象中,保证它们的成员属性相同。代码以下:ui

public class ConcretePrototype implements Prototype {

    private String attr;

    public String getAttr() {
        return attr;
    }

    public void setAttr(String attr) {
        this.attr = attr;
    }

    @Override
    public Prototype clone() {
        ConcretePrototype concretePrototype = new ConcretePrototype();
        concretePrototype.setAttr(this.attr);
        return concretePrototype;
    }
}
  • Java语言的clone()方法

代码以下:this

class ConcretePrototype2 implements Cloneable {

    @Override
    public Prototype clone() {
        Object object = null;
        try {
            object = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return (Prototype) object;
    }

}

2 浅克隆与深克隆的实现,代码以下:设计

public class WeeklyLog implements Cloneable,Serializable {

    private static final long serialVersionUID = 8532521622598526741L;

    private String name;

    private String content;

    private Attachment attachment;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Attachment getAttachment() {
        return attachment;
    }

    public void setAttachment(Attachment attachment) {
        this.attachment = attachment;
    }

    @Override
    //浅克隆,克隆出来的对象中的Attachment指向同一个对象地址
    public WeeklyLog clone(){
        Object obj;
        try
        {
            obj = super.clone();
            return (WeeklyLog)obj;
        }
        catch(CloneNotSupportedException  e)
        {
            System.out.println("不支持复制!");
            return null;
        }
    }

    //深克隆,克隆出来的对象中的Attachment为不一样的对象
    public WeeklyLog deepClone() throws IOException, ClassNotFoundException {
        //将对象写入流中
        ByteArrayOutputStream bao=new  ByteArrayOutputStream();
        ObjectOutputStream oos=new  ObjectOutputStream(bao);
        oos.writeObject(this);

        //将对象从流中取出
        ByteArrayInputStream bis=new  ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois=new  ObjectInputStream(bis);
        return  (WeeklyLog)ois.readObject();
    }
}



class Attachment {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Java语言提供的Cloneable接口和Serializable接口的代码很是简单,它们都是空接口,做为一种标识,本质上是调用的Object类的方法code

3 利用原型模式可实现一个原型管理器,专门负责克隆相应的对应,代码以下:

public class PrototypeManager {

    private static PrototypeManager instance = new PrototypeManager();

    private Map<String, OfficialDocument> hm = new ConcurrentHashMap<>();

    private PrototypeManager() {
        hm.put("far", new FAR());
        hm.put("srs", new SRS());
    }

    public PrototypeManager getInstance() {
        return instance;
    }

    //增长新的公文对象
    public void addOfficialDocument(String key, OfficialDocument doc) {
        hm.put(key, doc);
    }

    //经过浅克隆获取新的公文对象
    public OfficialDocument getOfficialDocument(String key) {
        return ((OfficialDocument) hm.get(key)).clone();
    }

}

//抽象公文接口,也可定义为抽象类,提供clone()方法的实现,将业务方法声明为抽象方法
interface OfficialDocument extends Cloneable {

    OfficialDocument clone();

    void display();
}

//可行性分析报告(Feasibility Analysis Report)类
class FAR implements OfficialDocument {

    @Override
    public OfficialDocument clone() {
        OfficialDocument far = null;
        try {
            far = (OfficialDocument) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("不支持复制!");
        }
        return far;
    }

    @Override
    public void display() {
        System.out.println("《可行性分析报告》");
    }
}

//软件需求规格说明书(Software Requirements Specification)类
class SRS implements OfficialDocument {

    @Override
    public OfficialDocument clone() {
        OfficialDocument srs = null;
        try {
            srs = (OfficialDocument) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("不支持复制!");
        }
        return srs;
    }

    @Override
    public void display() {
        System.out.println("《软件需求规格说明书》");
    }
}

4. 优缺点

  • 优势
  1. 若是建立新的对象比较复杂时,可利用原型模式简化对象的建立过程,经过复制也可提升效率。
  2. 原型模式提供了简化的建立结构,无须专门的工厂类来建立产品
  3. 可使用深克隆保持对象的状态。
  • 缺点
  1. 须要为每个类配备一个克隆方法,并且该克隆方法位于一个类的内部,当对已有的类进行改造时,须要修改源代码,违背了“开闭原则”。
  2. 实现深克隆时须要编写较为复杂的代码,多重嵌套引用时实现起来比较麻烦。

5. 适用场景

  1. 建立新对象成本较大(初始化时间较长或占用资源较长),可经过原型模式对已有对象进行复制获得。
  2. 若是系统要保存对象的状态,而对象的状态变化很小,或者对象自己占内存不大的时候,也可使用原型模式配合备忘录模式来应用。相反,若是对象的状态变化很大,或者对象占用的内存很大,那么采用状态模式会比原型模式更好。
  3. 须要避免使用分层次的工厂类来建立分层次的对象,而且类的实例对象只有一个或不多的几个组合状态,经过复制原型对象获得新实例可能比使用构造函数建立一个新实例更加方便。

6. 我的理解

用来复制一个对象,无需知道具体建立过程,可进行对象的浅克隆或深克隆。

参考

  1. Java设计模式-刘伟
相关文章
相关标签/搜索