java設計模式——如此簡易的原型模式

今天老王來聊一聊原型模式,內容相對簡單,相信一看就懂。

一、引言

原型模式是什麼?爲什麼要學原型模式?原型模式到底怎麼做?還是按照這個學習套路來。
首先原型模式有幾個關鍵詞(克隆,拷貝,複製),故名思意,對於一個對象,我們將對他用原型模式進行克隆,拷貝,複製,相當於細胞分裂。

首先,在沒有原型模式之前,我們複製對象是這樣複製的(要一個,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之類的都有實現該方式進行克隆。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章