【设计模式系列5】深入分析原型模式及浅克隆和深克隆的区别

java设计模式之原型模式

  • 设计模式系列总览
  • 什么是原型模式
  • 原型模式示例1
    • 1.标准的原型模式需要先创建一个原型接口
    • 2.创建一个对象实现原型接口
    • 3.编写测试类
  • 原型模式示例2
    • 1.创建一个原型对象,实现Cloneable接口
    • 2.编写测试类
    • 3.改写PrototypeB 类,实现深克隆
    • 4.深克隆带来的问题
  • 原型模式适用场景
  • 原型模式优点
  • 原型模式缺点
  • 总结

设计模式系列总览

设计模式 飞机票
三大工厂模式 登机入口
策略模式 登机入口
委派模式 登机入口
模板方法模式 登机入口
观察者模式 登机入口
单例模式 登机入口
原型模式 登机入口
代理模式 登机入口
装饰者模式 登机入口
适配器模式 登机入口
建造者模式 登机入口
责任链模式 登机入口
享元模式 登机入口
组合模式 登机入口
门面模式 登机入口
桥接模式 登机入口
中介者模式 登机入口
迭代器模式 登机入口
状态模式 登机入口
解释器模式 登机入口
备忘录模式 登机入口
命令模式 登机入口
访问者模式 登机入口
软件设计7大原则和设计模式总结 登机入口

什么是原型模式

原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,调用者不需要知道任何创建细节,不调用构造函数,原型模式属于创建型模式。

原型模式示例1

1.标准的原型模式需要先创建一个原型接口

package com.zwx.design.pattern.prototype;

public interface IPrototype {
     
    IPrototype clone();
}

2.创建一个对象实现原型接口

package com.zwx.design.pattern.prototype.impl;

import com.zwx.design.pattern.prototype.IPrototype;
import java.util.List;

public class PrototypeImplA implements IPrototype{
     
    private String name;

    private int age;

    private List<String> phoneList;

    public String getName() {
     
        return name;
    }

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

    public int getAge() {
     
        return age;
    }

    public void setAge(int age) {
     
        this.age = age;
    }
    public List<String> getPhoneList() {
     
        return phoneList;
    }

    public void setPhoneList(List<String> phoneList) {
     
        this.phoneList = phoneList;
    }

    @Override
    public IPrototype clone() {
     
        PrototypeImplA prototypeImplA = new PrototypeImplA();
        prototypeImplA.setAge(this.age);
        prototypeImplA.setName(this.name);
        prototypeImplA.setPhoneList(this.phoneList);
        return prototypeImplA;
    }
}

PrototypeImplA实现了接口IPrototype,并且实现了clone方法,返回了一个新的对象

3.编写测试类

package com.zwx.design.pattern.prototype;

import com.zwx.design.pattern.prototype.impl.PrototypeImplA;
import java.util.ArrayList;
import java.util.List;

public class ProtoTypeTest {
     
    public static void main(String[] args) throws Exception {
     
        PrototypeImplA prototypeImplA = new PrototypeImplA();
        prototypeImplA.setAge(18);
        prototypeImplA.setName("张三");
        List<String> phoneList = new ArrayList<>();
        phoneList.add("88888888");
        phoneList.add("77777777");
        prototypeImplA.setPhoneList(phoneList);

        PrototypeImplA cloneProtoType = (PrototypeImplA) prototypeImplA.clone();
        System.out.println(prototypeImplA.getPhoneList() == cloneProtoType.getPhoneList());//true
    }
}

最后一个会输出true,这是因为这种克隆方式是浅克隆,对象中如果有引用对象那么被克隆后的对象依然会指向原对象,如果需要复制两个独立的对象,则需要使用深克隆,后面示例2中我们会对比一下两种克隆方式

原型模式示例2

1.创建一个原型对象,实现Cloneable接口

package com.zwx.design.pattern.prototype.impl;

import java.io.*;
import java.util.List;

public class PrototypeB implements Cloneable {
     
    private String name;

    private int age;

    private List<String> phoneList;

    public String getName() {
     
        return name;
    }

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

    public int getAge() {
     
        return age;
    }

    public void setAge(int age) {
     
        this.age = age;
    }

    public List<String> getPhoneList() {
     
        return phoneList;
    }

    public void setPhoneList(List<String> phoneList) {
     
        this.phoneList = phoneList;
    }
    public Object clone() throws CloneNotSupportedException {
     
        return super.clone();
    }
}

Object类默认有clone()方法,protected级别,且是浅克隆,如果我们需要使用默认的clone()方法,则必须实现一个Cloneable接口(Cloneable是一个标记接口,类似的还有Serializable等接口),否则会抛出异常CloneNotSupportedException

2.编写测试类

package com.zwx.design.pattern.prototype;

import com.zwx.design.pattern.prototype.impl.PrototypeB;
import java.util.ArrayList;
import java.util.List;

public class ProtoTypeTest2 {
     
    public static void main(String[] args) throws CloneNotSupportedException {
     
        PrototypeB prototypeImplB = new PrototypeB();
        prototypeImplB.setAge(18);
        prototypeImplB.setName("张三");
        List<String> phoneList = new ArrayList<>();
        phoneList.add("88888888");
        phoneList.add("77777777");
        prototypeImplB.setPhoneList(phoneList);

        PrototypeB cloneProtoTypeB = (PrototypeB)prototypeImplB.clone();
        System.out.println(prototypeImplB.getPhoneList() == cloneProtoTypeB.getPhoneList());//true
    }
}

这时候输出的结果还是true,因为上面我们用的是Object自带的clone()方法,默认是浅克隆,那么如何实现深克隆呢?接下来我们对PrototypeB类进行改写。

3.改写PrototypeB 类,实现深克隆

package com.zwx.design.pattern.prototype.impl;

import java.io.*;
import java.util.List;

public class PrototypeB implements Cloneable, Serializable {
     
    private String name;

    private int age;

    private List<String> phoneList;

    public String getName() {
     
        return name;
    }

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

    public int getAge() {
     
        return age;
    }

    public void setAge(int age) {
     
        this.age = age;
    }

    public List<String> getPhoneList() {
     
        return phoneList;
    }

    public void setPhoneList(List<String> phoneList) {
     
        this.phoneList = phoneList;
    }
    public Object clone() throws CloneNotSupportedException {
     
//        return super.clone();
        return this.deepClone();
    }

    public PrototypeB 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);

            PrototypeB clone = (PrototypeB)ois.readObject();
            return clone;
        }catch (Exception e){
     
            return null;
        }
    }
}

多实现了一个Serializable接口,然后clone()方法中返回了自定义的深克隆方法deepClone(),这时候再运行测试类,返回的就是false!

4.深克隆带来的问题

深克隆会破坏单例对象,所以如果是单例对象我们可以通过两个办法防止破坏:
1、不实现Cloneable接口,使得该类不支持克隆
2、重写clone()方法,并且返回单例对象

原型模式适用场景

1、类初始化消耗资源较多时
2、初始化一个对象时需要非常繁琐的过程时
3、构造函数比较复杂时
4、循环体中生产大量对象时,可读性下降时

原型模式优点

1、当创建一个对象比较复杂时,使用原型对象通常效率会更高

原型模式缺点

1、需要为每个对象配备克隆或者可拷贝的方法
2、对克隆复杂对象或者对克隆出来的对象进行复杂改造时,易带来风险,深克隆、浅克隆要运用得当

总结

原型模式就是如何快速构建对象的方法总结, 通过简单工厂将getter、setter封装到某个方法中,实现快速复制。
原型模式到底应该采用浅克隆还是深克隆?这个应根据实际业务场景具体分析,但是即使浅克隆能满足需求,也应该实现Cloneable接口,将clone()方法定义为public,并调用super.clone()返回克隆对象

你可能感兴趣的:(设计模式,设计模式,java,prototype)