設計模式二——原型模式

寫在前面:對於原型模式的介紹,網上有很多。但是給我的感覺,有些介紹並不是在單純的介紹原型模式本身的意義,而是附加了其他的設計模式。至少我在看過很多文章之後,不僅未清晰的瞭解原型模式的本質,反而愈發感覺糊里糊塗。當然,這或許是我個人還沒達到理解這種模式的水平。
本文關於原型模式的介紹,基本是出於我個人依據定義的理解。或許是正確的,或許存在謬誤,歡迎大家留言指正討論。


目錄

首先看下原型模式的定義:

用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。
Prototype原型模式是一種創建型設計模式,Prototype模式允許一個對象再創建另外一個可定製的對象,根本無需知道任何如何創建的細節,工作原理是:通過將一個原型對象傳給那個要發動創建的對象,這個要發動創建的對象通過請求原型對象拷貝它們自己來實施創建。

一、模式定義分析

通過上面的定義,得到的關鍵詞是:對象複製。那我們就從對象和複製展開來討論。

1、實現對象複製之前先回顧一下以下知識點:

  1. 在Java中,對象實例的創建一般都要通過new關鍵字,需要通過構造函數來實例化。既然要複製,可能要避開這個限制。
  2. 在Java中,創建的對象除去基本數據類型的非包裝類、字符串對象(在JDK7之前,待考證)等,絕大多數的對象都是在堆內存中創建的。複製,就需要在內存空間中創建出一個獨立的對象出來。
  3. 在Java中,提供了語言級別的複製支持,那就是Cloneable接口。這個接口是個標記接口,裏面並沒有要實現的方法。因爲所有的對象都隱式的繼承自Object類,Object類中有一個clone方法。這個clone方法有2個限制:①方法修飾符是protected,無法在非繼承類內部使用,所以要在重寫時把修飾符改爲public。②如果沒有實現Cloneable接口而直接調用clone方法,會報錯。
  4. 對象中既有基本數據類型的成員變量,也有可能有引用類型的成員變量,拷貝方式肯定有區別。所以需要了解淺拷貝和深拷貝的概念。

2、哪些場景下應該考慮使用複製的模式呢?

因爲和複製做對比的,主要是通過new關鍵字來創建對象,所以來看下new可能會遇到的性能問題。

①當new對象需要大量繁瑣的準備工作時,複製會提高性能。

示例場景
public Demo getDemoInstance() {
    Object obj = prepareObject();// 創建對象前需要執行一些耗時操作
    Demo demo = new Demo(obj);
}

public Demo(Object obj) {
    // 解析配置、初始化環境、設置變量等等操作
}

②當需要多次重複的調用new關鍵字執行構造函數時,累積的時間損耗也是可以考慮的優化點。

for(int i=0; i<1000000; i++) {
    Demo demo = new Demo(); // 每次都要新建對象,而new又比較耗時的時候
    handle(demo);
}

③當一個對象創建耗時,單實例又存在併發安全問題時,也可以考慮複製一份。

寫到這裏忍不住想起ThreadLocal類,這個號稱爲每個線程拷貝一份副本的類是怎麼實現的?深拷貝還是淺拷貝呢?拷貝時如果泛型裏的類沒有實現Cloneable接口會怎樣呢?回頭再去看看...

3、複製在哪裏操作?需要注意什麼?

  • 儘量減少new對象的地方,如果系統中大量通過new創建的對象,那麼複製也就沒啥意義了
  • 複製需要在用到原型類對象的地方來操作

二、代碼實現:

1、原型類代碼
public class Prototype implements Cloneable {

    private String name;
    private int age;

    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;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
2、調用類代碼
public class PrototypeClient {

    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype raw = new Prototype();
        for (int i=0; i<1000; i++) {
            Prototype clone = (Prototype) raw.clone();
            System.out.println(clone);
        }
    }

}

三、補充說明:

綜上,複製之前我們需要先有一個現成的對象,但是這個現成的對象數量不宜過多,否則就失去了複製的意義。原始對象可以考慮使用緩存的方式、單例模式等。

四、參考文章:

原型模式|菜鳥教程
設計模式之原型模式
【JAVA】設計模式之原型模式的使用分析

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