设计模式之原型模式(创建型,拔一根毫毛,吹出猴万个)

介绍

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

浅复制

场景:我们都知道齐天大圣吹一根毫毛就可以变出一只猴子来,这只猴子跟齐天大圣长相一模一样。假定齐天大圣吹一根毫毛变出一只猴子A,这种以齐天大圣为原型,产生一只猴子A的过程就是原型模式。但是,出现了一个问题,猴子A和齐天大圣共用一根金箍棒,出现这种现象是因为这只是原型模式的浅拷贝。
代码:
1.先来描述一只猴子:

public class Monkey {
    private int height;
    private int weight;
    private Date birthday;
    public int getHeight() {
        return height;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

2.描述出齐天大圣的金箍棒:

public class JinGuBang implements Serializable{
    private float h=100;
    private float d=10;
    public void big(){
        this.h *= 2;
        this.d *= 2;
    }
    public void small(){
        this.h /= 2;
        this.d /= 2;
    }
}

3.描述出齐天大圣:

public class QiTianDaSheng extends Monkey implements Cloneable,Serializable{
    private JinGuBang jinGuBang=new JinGuBang();
    public QiTianDaSheng() {
        this.setBirthday(new Date());
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    public JinGuBang getJinGuBang() {
        return jinGuBang;
    }
    public void setJinGuBang(JinGuBang jinGuBang) {
        this.jinGuBang = jinGuBang;
    }
}

4.浅拷贝测试结果:

public class SimpleTest {
    public static void main(String[] args) {
        //齐天大圣出来了
        QiTianDaSheng qiTianDaSheng=new QiTianDaSheng();
        try {
            //齐天大圣吹毫毛变出猴子A出来了
            QiTianDaSheng cloneA=(QiTianDaSheng)qiTianDaSheng.clone();
            //判断齐天大圣跟猴子A是不是同一个对象?答案:不是同一个,表明产生了新的对象
            System.out.println(qiTianDaSheng == cloneA);
            //判断齐天大圣的金箍棒跟猴子A的金箍棒是不是同一根?答案:是同一根金箍棒
            //原则上来说,齐天大圣跟猴子A应该各自都有一根金箍棒,而不应该共用同一根
            //说明这样的克隆是不完美的,这属于原型模式中的浅复制
            //如果猴子A让金箍棒变大一倍,那么齐天大圣的金箍棒就会跟着变大一倍
            System.out.println(qiTianDaSheng.getJinGuBang() == cloneA.getJinGuBang());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

再来看一个例子:
代码:
有一个普通对象Target1:

public class Target1 {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

再来一个普通对象Prototype1引用Target1:

public class Prototype1 implements Cloneable{
    private String name;
    private Target1 target1;
    private List list;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List getList() {
        return list;
    }
    public void setList(List list) {
        this.list = list;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    public Target1 getTarget1() {
        return target1;
    }
    public void setTarget1(Target1 target1) {
        this.target1 = target1;
    }
}

观察p1(Prototype1)克隆出p2的过程:

/**
 * 有一个对象p1,它克隆出了对象p2
 * 然后,修改p2的list的值,p1的list的值也会跟着修改,说明他们用的是同一个引用,这是属于浅复制
 * 然后,修改p2的里一个引用Target1,p1里的引用Target1的值也会跟着变,这也是属于浅复制
 */
public class CloneTest1 {

    public static void main(String[] args) {
        //初始化p1
        Prototype1 p1=new Prototype1();
        p1.setName("张三");

        List list=new ArrayList();
        list.add("123");
        p1.setList(list);

        Target1 t1=new Target1();
        p1.setTarget1(t1);


        //根据p1复制一个p2
        try {
            Prototype1 p2=(Prototype1)p1.clone();
            p2.getList().add("234");
            p2.setName("李四");
            p2.getTarget1().setName("王五");

            System.out.println("p1======"+p1.getName());
            System.out.println("p1======"+p1.getList());
            System.out.println("p1======"+p1.getTarget1());
            System.out.println("p1======"+p1.getTarget1().getName());

            System.out.println("p2======"+p2.getName());
            System.out.println("p2======"+p2.getList());
            System.out.println("p2======"+p2.getTarget1());
            System.out.println("p2======"+p2.getTarget1().getName());
        } catch (Exception e) {
            e.printStackTrace();
        }

    //执行结果:
    p1======张三
    p1======[123, 234]
    p1======com.taofut.sjms.prototype.simple1.Target1@1b6d3586
    p1======王五
    p2======李四
    p2======[123, 234]
    p2======com.taofut.sjms.prototype.simple1.Target1@1b6d3586
    p2======王五
    }
}

总结:直接实现java提供的Cloneable接口而不去重写它的话,这样实现的是一种浅复制,它能实现基本类型变量的值全部复制,但是不能实现类似集合,对象引用类型的复制,它仅仅是复制了对象的引用,该引用仍然还是指向原有对象,所以实际上还是只有一个对象,这样的话,就会造成新产生的对象自身做修改,还会影响到原有对象的变化,这是不安全的,所以这个原型模式是不完美的,属于浅复制。

深复制

场景:接着上面齐天大圣的案例,如果齐天大圣吹一根毫毛变出一只猴子A后,猴子A自身也拥有自己的金箍棒,那么这样的原型模式就完美了,它成功的复制出了一个一模一样的个体,但是这两者却又是不同的对象,齐天大圣还是齐天大圣,猴子A还是猴子A,它们只是长得一模一样,这样的复制属于深复制。
代码:
1.一样的,先来一只猴子:

public class Monkey {
    private int height;
    private int weight;
    private Date birthday;
    public int getHeight() {
        return height;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

2.再来一根金箍棒:

public class JinGuBang implements Serializable{
    private float h=100;
    private float d=10;
    public void big(){
        this.h *= 2;
        this.d *= 2;
    }
    public void small(){
        this.h /= 2;
        this.d /= 2;
    }
}

3.齐天大圣也该出来了:

public class QiTianDaSheng extends Monkey implements Cloneable,Serializable{
    private JinGuBang jinGuBang=new JinGuBang();
    public QiTianDaSheng() {
        this.setBirthday(new Date());
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
//        return super.clone();
        return deepClone();//自己重写了该方法
    }
    public JinGuBang getJinGuBang() {
        return jinGuBang;
    }
    public void setJinGuBang(JinGuBang jinGuBang) {
        this.jinGuBang = jinGuBang;
    }
    /**
     * 序列化方式实现深度克隆(只是实现深度克隆的方式之一,反射也可以实现)
     * 把对象的字节码数组读出来,通过字节码数组重新构造一个新的对象
     * @return
     */
    public Object deepClone(){
        try {
            ByteArrayOutputStream bos=new ByteArrayOutputStream();
            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois=new ObjectInputStream(bis);

            QiTianDaSheng cloneB=(QiTianDaSheng)ois.readObject();
            cloneB.setBirthday(new Date());
            return cloneB;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

总结:以上得到的就是对象类型完全一样,但又不是同一个对象的深复制模式,其实要实现深复制模式有多种方式,这里只是列举了序列化这一种,而spring中的原型模式基本用的都是反射。

猜你喜欢

转载自blog.csdn.net/fu123123fu/article/details/80038918
今日推荐