原型模式【創建型模式】

文章首發個人博客:https://www.xdx97.com/article/703593125049794560

一、引出原型模式

如果我們有一個類(sheep),它裏面有兩個屬性,名稱(name),年齡(age)。現在我們有一個它的實例(s1),我們需要按照這個實例的屬性再去創建兩個對象。


1、Sheep

@Data
public class Sheep {

    private String name;

    private Integer age;

    Sheep(String name,Integer age){
        this.name = name;
        this.age = age;
    }
}

2、main

public class Main {

    public static void main(String[] args) {
        Sheep s1 = new Sheep("多利",1);

        Sheep s2 = new Sheep(s1.getName(),s1.getAge());
        Sheep s3 = new Sheep(s1.getName(),s1.getAge());
    }
}

總結:

  • 這樣寫看起來還好不麻煩也沒有問題
  • 但是如果這類裏面有100個屬性呢?(我們寫的代碼會很多)
  • 或者我們現在對這個類再添加幾個屬性呢?(我們需要修改的地方很多)



二、原型模式

原型模式 : 用原型實例指定創建對象的種類,並且通過拷貝這些原型,創建新的對象。

也就是我們使用clone方法進行對象的創建,我們對上面的 sheep 類新增clone方法。

1、改造後的sheep類

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Sheep implements Cloneable{

    private String name;

    private Integer age;

    Sheep(String name,Integer age){
        this.name = name;
        this.age = age;
    }


    @Override
    protected Sheep clone()  {
        Object obj = null;
        try {
            obj = super.clone();
        }catch (Exception e){
            e.printStackTrace();
        }
        return (Sheep)obj;
    }
}

2、main 測試

public class Main {

    public static void main(String[] args) {
        Sheep s1 = new Sheep("多利",1);

        Sheep s2 = s1.clone();
        Sheep s3 = s1.clone();

        System.out.println(s1 == s2);   // false
        System.out.println(s2 == s3);   // false
    }
}

總結

  • 使用clone方法去創建對象,就解決上面的問題了。
  • 但其我們的clone方法是一個淺拷貝,如果類裏面有引入屬性,那麼創建出來的對象的引用都是指向一個的。

我們對 淺拷貝 進行一個測試

1、在上面的 sheep 類裏面新增一個 屬性

 private Sheep friend;

2、測試

public class Main {

    public static void main(String[] args) {
        Sheep s1 = new Sheep("多利",1);
        s1.setFriend(new Sheep("小道仙",2));

        Sheep s2 = s1.clone();
        Sheep s3 = s1.clone();

        System.out.println(s1.getFriend().getName() + " " + s2.getFriend().getName() + " " + s3.getFriend().getName());

        s2.getFriend().setName("王哈哈");

        System.out.println(s1.getFriend().getName() + " " + s2.getFriend().getName() + " " + s3.getFriend().getName());
    }

    // 打印結果如下
    小道仙 小道仙 小道仙
    王哈哈 王哈哈 王哈哈
}



三、淺拷貝、深拷貝

淺拷貝 :在拷貝對象的時候對引用類型,沒有從新創建一個對象,而是指向之前對象的引用。

深拷貝 :解決淺拷貝存在的問題,我們去爲引用類型創建一個新的對象。


我們使用深拷貝去解決淺拷貝的問題

方式一:使用多次clone

@Override
protected Sheep clone()  {
    Sheep obj = null;
    try {
        obj = (Sheep)super.clone();
        if (this.getFriend() != null){
            Sheep clone = this.getFriend().clone();
            obj.setFriend(clone);
        }
    }catch (Exception e){
        e.printStackTrace();
    }
    return obj;
 }
  • 從上面的代碼我們可以看到所謂多次克隆,也就是對引用類型再進行一次克隆。
  • 優點:它寫起來和理解都很簡單。
  • 缺點:如果引用類型過多那麼寫起來很麻煩,後續如果 新增/刪除 引用類型,我們還需要修改 clone 方法。

方式二:使用序列化和反序列化 (注:需要實現序列化接口 implements Serializable)

    @Override
    protected Sheep clone()  {

        Sheep obj = null;

       // 創建流對象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;

        try {
            // 序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            
            // 反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            obj = (Sheep)ois.readObject();

            return obj;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }finally {
            // 關閉流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
  • 使用序列化優勢很明顯,後續 新增/刪除 引用類型,我們不需要修改 clone 方法,只不過寫起來稍微複雜一點。但是推薦使用這個。

兩種方法的測試代碼如下,結果也都一樣

public class Main {

    public static void main(String[] args) {
        Sheep s1 = new Sheep("多利",1);
        s1.setFriend(new Sheep("小道仙",2));

        Sheep s2 = s1.clone();
        Sheep s3 = s1.clone();

        System.out.println(s1.getFriend().getName() + " " + s2.getFriend().getName() + " " + s3.getFriend().getName());

        s2.getFriend().setName("王哈哈");

        System.out.println(s1.getFriend().getName() + " " + s2.getFriend().getName() + " " + s3.getFriend().getName());
        
        // 打印結果
        小道仙 小道仙 小道仙
        小道仙 王哈哈 小道仙
    }
}




在這裏插入圖片描述

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