设计模式4---原型模式(Prototype Pattern)

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


原型模式结构图

通俗来说:原型模式就是深拷贝和浅拷贝的实现。

浅拷贝

只实现了值拷贝,对于引用对象还是指向原来的对象。

  • 父类实现clone方法,子类没有实现clone方法,其效果是浅拷贝。
  • 父类实现clone方法,子类也实现clone方法,本来我想应该是深拷贝了,没想到也是浅拷贝
package com.prototype;
import java.io.Serializable;

public class Work implements Serializable,Cloneable{
    private static final long serialVersionUID = 207835812839542204L;
    private String job;
    private double salary;
    public Work(String job,double salary) {
        this.job = job;
        this.salary = salary;
    }
    @Override
    public String toString() {
        return "Work [job=" + job + ", salary=" + salary + "]";
    }
    public String getJob() {
        return job;
    }
    public void setJob(String job) {
        this.job = job;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
}
package com.prototype;

import java.io.Serializable;

public class User implements Serializable,Cloneable{
    private static final long serialVersionUID = -2260332138558500447L;

    private String name = "";
    private Work work = null;

    public User(String name,String job,double salary) {
        this.name=name;
        work = new Work(job, salary);
    }

    public void changeJob(String job){
        this.work.setJob(job);
    }
    /*只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,
    因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB。
    重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的。
    */
    //浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "User [name=" + name + ", work=" + work + "]";
    }

}
package com.prototype;

public class Main {

    public static void main(String[] args) {
        try {
            User user1 = new User("zhangsan","ceo",100000);
            User user2 = (User) user1.clone();
            System.out.println(user1);
            System.out.println(user2);
            System.out.println("修改job");
            user2.changeJob("cfo");
            System.out.println(user1);
            System.out.println(user2);


        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}
//结果
User [name=zhangsan, work=Work [job=ceo, salary=100000.0]]
User [name=zhangsan, work=Work [job=ceo, salary=100000.0]]
修改job
User [name=zhangsan, work=Work [job=cfo, salary=100000.0]]
User [name=zhangsan, work=Work [job=cfo, salary=100000.0]]

深拷贝

即实现了值拷贝,也实现了对引用对象的拷贝。

  • 法一
    //深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。
    //实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。
    public Object deepClone() throws IOException, ClassNotFoundException{
        //写入当前对象的二进制流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        //读入二进制流产生的新对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
  • 法二
    //将User的拷贝方法修改为下面的方法。
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Work w = (Work) work.clone();//对其引用变量进行拷贝
        User u = (User)super.clone();//自身拷贝
        u.work = w;//引用变量重新赋值。
        return u;
    }



===================================================
  • 定义

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

  • 使用场景

    原型模式被用在频繁调用且极其相似的对象上,它会克隆对象并设置改变后的属性,而且消耗的资源较少。

  • 代码举例实现

ProtoTypeImpl.java

 package com.design.prototype;

public class ProtoTypeImpl implements Cloneable{

    private int shallowClone;

    private DeepClone deepClone = new DeepClone();

    public ProtoTypeImpl() {
        System.out.println("construct is called");
    }

    public void print() {
        // TODO Auto-generated method stub
        System.out.println(shallowClone);
        System.out.println(deepClone.getS());
    }


    @Override
    protected ProtoTypeImpl clone(){
        // TODO Auto-generated method stub
        try{
            ProtoTypeImpl protoTypeImp =  (ProtoTypeImpl) super.clone();
            //protoTypeImp.shallowClone = this.shallowClone;
            //protoTypeImp.deepClone = this.deepClone.clone();
            return protoTypeImp;
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }

    }


    public void setShallowClone(int shallowClone) {
        this.shallowClone = shallowClone;
    }

    public void setS(String s){
        deepClone.setS(s);
    }
}

DeepClone.java

package com.design.prototype;

public class DeepClone implements Cloneable{
    private String s;

    public String getS() {
        return s;
    }

    public void setS(String s) {
        this.s = s;
    }

    @Override
    protected DeepClone clone(){
        // TODO Auto-generated method stub
        try {
            return (DeepClone)super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

App.java

package com.design.prototype;

public class App {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ProtoTypeImpl protoTypeImp = new ProtoTypeImpl();
        protoTypeImp.setShallowClone(1);
        protoTypeImp.setS("deep clone");
        protoTypeImp.print();
        System.out.println("-------------");
        ProtoTypeImpl protoTypeImp2 = protoTypeImp.clone();
        protoTypeImp2.setShallowClone(2);
        protoTypeImp2.setS("deep clone 2");

        protoTypeImp2.print();
        System.out.println("-------------");
        protoTypeImp.print();
    }

}
  • 结果分析

运行结果1.png
运行结果1.png
  1. 这个现象主要是由于深浅复制引起的,普通类型的数据没有问题,而对象类型则有问题。同时我们应该注意到clone的时候构造函数是不会被调用的。

  2. 去掉ProtoTypeImpl.clone的两行注释(第一行没什么所谓,但是还是加上,有个对比)

    运行结果2.png
    运行结果2.png
  3. 总结优缺点

    • 优点
      原型模式是在内存二进制流的拷贝,要比直接 new 一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其点。
    • 缺点
      使用过程中要切记构造函数不会被调用,所以在构造函数完成的操作应该多加处理,还有深浅复制的问题



原文地址:https://www.cnblogs.com/linghu-java/p/5692563.html