學好設計模式防被祭天:蠅量模式

蠅量模式

為了防止被“殺”了祭天,學點設計模式,并總結下還是有必要的。

一:理解

  1. 蠅量這個詞匯來自于拳擊,是比輕量更輕的一個級別。在設計模式中,蠅量表示的是通過減少不必要的對象實例以減小系統的負擔。
  2. 蠅量模式也稱享元模式,享元是共享對象實例(或共享部分屬性)的意思。在設計模式中,享元是手段,蠅量是結果。
  3. 蠅量模式可以是完全共享和局部共享,完全共享可以減少對象實例,局部共享可以共享不同對象中相同的部分。


二:例子

你是個富二代。

你喜歡開車,然而你比較節儉,每次開車都向租車公司借。

租車公司有汽車對應的接口,包含一個drive方法。

public interface CarI {
    void drive();
}

對于你而言,開什么車其實也沒什么,主要是牌子brand要對。

為方便舉例,汽車類Car中只包含brand一個屬性。

// 汽車類
public class Car implements CarI {
    private String brand;

    public Car(String brand) {
        this.brand = brand;
    }

    @Override
    public void drive() {
        System.out.println("開" + brand + "car");
    }
}

老司機開車代碼:

public class Client {
    public static void main(String[] args) {
        Car car1 = new Car("寶馬");
        car1.drive();
        Car car2 = new Car("奔馳");
        car2.drive();
        Car car3 = new Car("奧迪");
        car3.drive();
        Car car4 = new Car("寶馬");
        car4.drive();
    }
}

你發現,你每次開車之前,都得先租一輛車,即新建一個對象。

例如雖然都是開寶馬車,卻要新建car1和car4兩個對象。

對于高富帥而言,這有點麻煩,也有點不夠優雅。

在現實系統中,高并發情況下,每個請求都新建一個對象,會導致內存占用量過大。

高并發時新建過多實例會導致來不及GC或者是GC太頻繁。

于是,你叫來程序員小菜幫你解決這個問題。

小菜覺得你作為高富帥,應該在第一次借車的時候,直接把車買下來,下次再開的時候,直接從車庫取就可以了。

說到要買買買,作為高富帥的你覺得還是挺有意思的,同意讓小菜這么干。

買買買

映射到真實系統,在調用方發起第一次請求時,新建對象并將其保存,之后的請求就無需再新建對象。

小菜決定使用蠅量模式進行重構。

于是,小菜上來就是一頓敲。

就是一把梭
public class CarKeeper {
    private Map<String, Car> carKeeper = Maps.newConcurrentMap();

    public Car getCar(String brand) {
        Car car = carKeeper.get(brand);
        if (car == null) {
            car = new Car(brand);
            carKeeper.put(brand, car);
            System.out.println("新建了" + brand + "car對象!");
        }
        return car;
    }
}

他新建了一個CarKeeper類,該類中包含一個map,用于保存已經建立的Car對象。

該過程和單例模式新建對象類似,為懶加載模式。

不過這里會為每個品牌的車子保存一個實例。

同樣的,懶加載會遇到多線程安全問題,可用雙重檢查解決。

測試代碼:

public class ClientV2 {
    public static void main(String[] args) {
        CarKeeper carKeeper = new CarKeeper();
        Car car1 = carKeeper.getCar("寶馬");
        car1.drive();
        Car car2 = carKeeper.getCar("奔馳");
        car2.drive();
        Car car3 = carKeeper.getCar("奧迪");
        car3.drive();
        Car car4 = carKeeper.getCar("寶馬");
        car4.drive();
        Car car5 = carKeeper.getCar("奔馳");
        car5.drive();
        Car car6 = carKeeper.getCar("奧迪");
        car6.drive();
    }
}

輸入/輸出:

新建了寶馬car對象!
開寶馬car
新建了奔馳car對象!
開奔馳car
新建了奧迪car對象!
開奧迪car
開寶馬car
開奔馳car
開奧迪car

經過重構,發現只有在第一次借對應品牌車子時才新建對象,之后的請求直接開車就可以了。

你覺得這個模式非常棒,開心地飆起了車。

老司機開車

留下小菜一個人默默地和蒼蠅玩耍。


三:再理解

  1. 例子中相同牌子的車子可以做到完全共享,使用時直接從map中取出即可。如果不同對象只有局部屬性共享,則需要在取出之后,設置一些非共享屬性。
  2. 該模式共享了對象,執行的方法為無狀態的,例如例子中的drive方法只輸出字符串。如果方法帶狀態,需要考慮結果是否正確。
  3. 蠅量模式節約了內存空間,減少了對象創建時間。
  4. Java中的字符串常量用到了該模式。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容