Coding精髓(设计模式)——细胞分裂之原型模式

115 阅读4分钟

一、引言

首先原型模式有几个关键词( **克隆,拷贝,复制 **),故名思意,对于一个对象,我们将对他用原型模式进行克隆,拷贝,复制,相当于细胞分裂。

首先,在没有原型模式之前,我们复制对象是这样复制的(要一个,new一个):

//这是老王类
public class Laowang {
    private String name;
    private String city;
    private School school;

    public Laowang(String name, String city, School school) {
        this.name = name;
        this.city = city;
        this.school = school;
    }
}
//Main方法
public static void main(String[] args) {
	Laowang laowang=new Laowang("老王", "广东广州", new School("广州某大学"));
    Laowang laowang1=new Laowang("老王", "广东广州", new School("广州某大学")); 
    Laowang laowang2=new Laowang("老王", "广东广州", new School("广州某大学"));
}

这样复制对象,有什么缺陷呢?比如每次我们都要整齐输入构造对象的参数“老王”,"广东广州","广州某大学"。这样显然过于笨重,因此原型模式出现了。接下来聊聊怎么实现。

二、实现原型模式

接下来介绍原型模式的2种实现方式(部分构造方法,set方法没放出来)

  • 浅克隆(不会复制属性是对象引用)

  • 深克隆(会复制属性是对象的引用)

1、浅克隆

浅克隆实现方式很简单 第一步:让Laowang类 **实现Cloneable接口 ** 第二步: **重写父类clone方法 **(任何一个对象都是继承于Object,因此都有clone方法)。 代码如下:

//这是Laowang类
public class Laowang implements Cloneable{
    private String name;
    private String city;
    private School school;
    
	//重写父类clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    } 
}
 //这是Main方法
    public static void main(String[] args) throws CloneNotSupportedException {
     	 //原型模式浅克隆
        Laowang laowang3= (Laowang) laowang.clone();
        System.out.println("-------------------浅克隆------------------------");
        System.out.println("本体的信息"+laowang.toString());
        System.out.println("浅克隆的信息"+laowang3.toString());
    }

运行结果如下: 可以看到,克隆是克隆了,但是属性 **School **并没有完全克隆,因为始终指向和本体同一个School对象。 在有属性是对象引用的情况下,这是浅克隆的劣势。

2、深克隆

深克隆的意思就会把原来的对象引用也同时克隆。 深克隆有2种实现方式:

  1. 修改重写的clone方法方式。
  2. 利用对象流方式深克隆

我们先来看第一种:改重写的clone方法方式 直接上代码 **让School类也实现Cloneable接口,重写父类clone方法 **

//这是School类
public class School implements Cloneable{
    private String schoolName;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

**Laowang类重写的clone方法进行修改: **

//这是Laowang类
public class Laowang implements Cloneable{
    private String name;
    private String city;
    private School school;
	//重写的clone方法进行修改
	@Override
   	protected Object clone() throws CloneNotSupportedException {
        School s1 = (School) school.clone();//让school自己先复制一个对象
        Laowang l1 = (Laowang) super.clone();//让自己也复制一个对象
        l1.setSchool(s1);//把刚才School复制后的对象赋值给l1
        return l1;//返回l1
    }
}
//这是Main方法
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型模式深克隆1
        Laowang laowang3= (Laowang) laowang.clone();
        System.out.println("-------------------深克隆1------------------------");
        System.out.println("本体的信息"+laowang.toString());
        System.out.println("深克隆的信息"+laowang3.toString());
    }

运行结果如下: 在这里插入图片描述 很明显,对象引用地址不同,已经发生了深度克隆。 这种做法其实不太被推荐,为什么?因为如果属性有一个对象引用,至少就 要修改对象引用类继承Cloneable接口。

因此更推荐下面做法:通过对象流进行深度克隆 代码如下: **让所有类都实现Serializabel接口(支持序列化和反序列化) **

public class School implements Serializable {
    private String schoolName;
    
    public School(String schoolName) {
        this.schoolName = schoolName;
    }
}

**写一个深度克隆deepClone方法如下 **

//这是Laowang类
public class Laowang implements Cloneable{
    private String name;
    private String city;
    private School school;

    //深度克隆
    public Laowang 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 (Laowang) ois.readObject();
    }
}
//这是Main方法
    public static void main(String[] args) throws IOException, ClassNotFoundException {
     	//模式克隆2
        Laowang laowang3= (Laowang) laowang.deepClone();
        System.out.println("-------------------深克隆2------------------------");
        System.out.println("本体的信息"+laowang.toString());
        System.out.println("深克隆的信息"+laowang3.toString());
    }

运行结果如下图: 在这里插入图片描述 可以看到,也是有进行了对象引用的深度克隆。

三、结束语

原型模式很好理解,就是一个对象的复制。只不过复制的方法有所不同。原型模式在java底层也有用到,例如map,set之类的都有实现该方式进行克隆。