深入理解设计模式-原型模式

Posted 、Dong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解设计模式-原型模式相关的知识,希望对你有一定的参考价值。

文章目录


一、定义

原型模式使用原型实例指定创建对象的种类,并且通过拷贝原型对象创建新的对象。原型模式本质是一种克隆对象的方法,其核心是重写Object中的clone方法,调用该方法可以在内存中进行对象拷贝。

Java提供了一个标记接口——Cloneable,实现该接口完成标记,在JVM中具有这个标记的对象才有可能被拷贝。如果不实现该接口,克隆对象会抛出CloneNotSupportedException异常。


二、使用场景

  • 当一个系统应该独立于它的产品创建、构成和表示时,要使用原型模式
  • 当要实例化的类是在运行时刻指定时,如通过动态装载
  • 为了避免创建一个与产品类层次平行的工厂类层次时
  • 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆原型可能比每次用合适的状态手工实例化原型类更方便一些。

三、代码样例

1.类图

通过原型模式,实现简历复制

  • 抽象原型(Prototype)角色:规定了具体原型对象必须实现的接口(如果要提供深拷贝,则必须具有实现clone的规定)
  • 具体原型(ConcretePrototype):从抽象原型派生而来,是客户程序使用的对象,即被复制的对象,需要实现抽象原型角色所要求的接口。
  • 客户(Client)角色:使用原型对象的客户程序

2.简历实体类

/**
 * 简历类
 */
public class Resume implements Cloneable, Serializable 
    private static final long serialVersionUID = -4410449301166191440L;

    private String name;
    private String gender;
    private int age;

    /**
     * 引用”工作经历”对象,为演示深度拷贝和浅度拷贝而用
     */
    private WorkExperience workExperience;

    public Resume() 
        // 在“简历”类实例化时,同时实例化“工作经历”类
        workExperience = new WorkExperience();
    

    public void display() 
        System.out.println(this.getName() + " " + this.getGender() + " "
                + this.getAge() + "\\n工作经历: "
                + this.getWorkExperience().getWorkDate() + " "
                + this.getWorkExperience().getWorkCompany());
    

    @Override
    protected Object clone() throws CloneNotSupportedException 
        return super.clone();
    

    // 通过对象序列化,实现深度拷贝
    public Object deepClone() throws IOException, ClassNotFoundException 
        // 将对象写入流内
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        // 从流内读出对象
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
                bos.toByteArray()));
        return ois.readObject();

    

    public String getName() 
        return name;
    

    public Resume setName(String name) 
        this.name = name;

        return this;
    

    public String getGender() 
        return gender;
    

    public Resume setGender(String gender) 
        this.gender = gender;

        return this;
    

    public int getAge() 
        return age;
    

    public Resume setAge(int age) 
        this.age = age;

        return this;
    

    public WorkExperience getWorkExperience() 
        return workExperience;
    

    public void setWorkExperience(String workDate, String workCompany) 
        workExperience.setWorkDate(workDate);
        workExperience.setWorkCompany(workCompany);
    

3.工作经历实体类

/**
 * 工作经历类,为演示深度拷贝和浅度拷贝而用
 */
public class WorkExperience implements Serializable 
    private static final long serialVersionUID = 1L;

    private String workDate;
    private String workCompany;

    public String getWorkDate() 
        return workDate;
    

    public WorkExperience setWorkDate(String workDate) 
        this.workDate = workDate;
        return this;
    

    public String getWorkCompany() 
        return workCompany;
    

    public WorkExperience setWorkCompany(String workCompany) 
        this.workCompany = workCompany;

        return this;
    



4.客户端类

/**
 * 原型模式客户端 演示深度拷贝和浅度拷贝
 */
public class PrototypeClient 
    public static void shallowCopy() throws CloneNotSupportedException 
        Resume aResume = new Resume();
        aResume.setName("大鸟 ").setGender("男 ").setAge(25);
        aResume.setWorkExperience("1999-2002, ", "XX公司");

        Resume bResume = (Resume) aResume.clone();
        bResume.setWorkExperience("1999-2002, ", "YY公司");

        Resume cResume = (Resume) aResume.clone();
        cResume.setWorkExperience("1999-2002, ", "ZZ公司");

        System.out.println(">>>>>>浅度拷贝:");
        aResume.display();
        bResume.display();
        cResume.display();
    

    public static void deepCopy() throws CloneNotSupportedException,
            ClassNotFoundException, IOException 
        Resume aResume = new Resume();
        aResume.setName("大鸟 ").setGender("男 ").setAge(25);
        aResume.setWorkExperience("1999-2002, ", "XX公司");

        Resume bResume = (Resume) aResume.deepClone();
        bResume.setWorkExperience("1999-2002, ", "YY公司");

        Resume cResume = (Resume) aResume.deepClone();
        cResume.setWorkExperience("1999-2002, ", "ZZ公司");

        System.out.println(">>>>>>深度拷贝:");
        aResume.display();
        bResume.display();
        cResume.display();

    

    public static void main(String[] args) throws CloneNotSupportedException,
            ClassNotFoundException, IOException 

        // 浅度拷贝
        shallowCopy();

        System.out.println("==================================");

        // 深度拷贝
        deepCopy();

    


4.输出


四、优缺点

优点:

  • Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

缺点:

  • 需要为每一个类都配置一个 clone 方法
  • clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
  • 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。

五、两种实现方式:浅拷贝与深拷贝

1.浅拷贝

被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。

2.深拷贝

被拷贝对象的所有的变量都含有与原来对象相同的值,除了引用其他对象的变量。引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有被引用对象。即深拷贝把要拷贝的对象所引用的对象也都拷贝了一次。

深拷贝要深入到多少层,是一个不确定的问题。在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。


结尾

  • 感谢大家的耐心阅读,如有建议请私信或评论留言。
  • 如有收获,劳烦支持,关注、点赞、评论、收藏均可,博主会经常更新,与大家共同进步
创作打卡挑战赛 赢取流量/现金/CSDN周边激励大奖

以上是关于深入理解设计模式-原型模式的主要内容,如果未能解决你的问题,请参考以下文章

深入理解设计模式-抽象工厂模式

深入理解设计模式-抽象工厂模式

深入理解设计模式---系列目录

深入理解设计模式:简单工厂模式

深入理解JavaScript中创建对象模式的演变(原型)

深入理解JavaScript中创建对象模式的演变(原型)