設計模式學習(五):原型模式

2022-11-08 21:02:23

設計模式學習(五):原型模式

作者:Grey

原文地址:

部落格園:設計模式學習(五):原型模式

CSDN:設計模式學習(五):原型模式

原型模式

原型模式是建立型模式。

如果物件的建立成本比較大,而同一個類的不同物件之間差別不大(大部分欄位的值都相同),在這種情況下,我們可以利用對已有物件(原型)進行復制(或者叫拷貝)的方式來建立新物件,以達到節省建立時間的目的。這種基於原型來建立物件的方式就叫作原型設計模式。

實際上,建立物件包含的申請記憶體、給成員變數賦值這一過程,本身並不會花費太多時間,或者說對於大部分業務系統來說,這點時間完全是可以忽略的。應用一個複雜的模式,只得到一點點的效能提升,這就是所謂的過度設計,得不償失。但是,如果物件中的資料需要經過複雜的計算才能得到(比如排序、計算雜湊值),或者需要從 RPC 、網路、資料庫、檔案系統等非常慢速的 I/O 中讀取,每次讀取一次的代價都很高,在這種情況下,我們就可以利用原型模式,從其他已有物件中直接拷貝得到,而不用每次在建立新物件的時候,都重複執行這些耗時的操作。

原型模式用原型範例指定建立物件的種類,並且通過拷貝這些原型建立新的物件,典型的應用是物件的克隆方法,範例程式碼如下

public class Person implements Cloneable {
    String name = "lisa";
    int age = 1;
    Location loc = new Location("xy", 10);

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person p = (Person) super.clone();
        p.loc = (Location) loc.clone();
        return p;
    }

    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + ", loc=" + loc + '}';
    }
}
public class Location implements Cloneable {
    private String street;
    private int roomNo;

    public Location(String street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Location{" + "street='" + street + '\'' + ", roomNo=" + roomNo + '}';
    }
}
public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person p = new Person();
        System.out.println(p);
        Person p2 = (Person) p.clone();
        System.out.println(p2);
    }
}

本範例的 UML 圖如下:

注:Java 自帶的 clone() 方法進行的就是淺克隆。而如果我們想進行深克隆,可以直接在類中呼叫父類別的克隆方法,即: super.clone() 後,手動給克隆物件的相關屬性分配另一塊記憶體,不過如果當原型物件維護很多參照屬性的時候,手動分配會比較煩瑣。因此,在 Java 中,如果想完成原型物件的深克隆,則通常使用序列化的方式。

例如:克隆一個巨大的 HashMap,如果構建該 HashMap 的代價很大,我們可以通過

方式一:先呼叫 HashMap 預設的克隆方法,然後遞迴拷貝 HashMap 裡面的內容,直到型別是基礎型別為止;

方式二:使用序列化方式克隆。

關於序列化克隆的範例程式碼如下

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

public class DeepCloneDemo {
    public static void main(String[] args) {
        List<String> hobbies = new ArrayList<>();
        hobbies.add("唱歌");
        hobbies.add("跳舞");
        Prototype p = new Prototype();
        p.setAge(18);
        p.setName("zhangsan");
        p.setHobbits(hobbies);
        Prototype clone = p.clone();
        System.out.println(clone.getAge());
        System.out.println(clone.getName());
        System.out.println(clone.getHobbits());
    }
}

// 待克隆物件
class Prototype implements Cloneable, Serializable {
    private int age;
    private String name;
    private List<String> hobbits;

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public List<String> getHobbits() {
        return hobbits;
    }

    public void setHobbits(List<String> hobbits) {
        this.hobbits = hobbits;
    }

    @Override
    protected Prototype clone() {
        return deepClone();
    }

    public Prototype 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);
            return (Prototype) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String toString() {
        return "Prototype{" + "age=" + age + ", name='" + name + '\'' + ", hobbits=" + hobbits + '}';
    }
}

如果只是增量拷貝,可以通過淺拷貝拿到一個新的 HashMap ,然後拿到增量的資料單獨進行深拷貝即可。

更多地,Spring 中建立物件的方式預設採用單例模式,可以通過設定 @Scope("prototype") 註解將其改為原型模式。

UML 和 程式碼

UML 圖

程式碼

更多

設計模式學習專欄

參考資料