學(xué)好設(shè)計模式防被祭天:蠅量模式

蠅量模式

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

一:理解

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


二:例子

你是個富二代。

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

租車公司有汽車對應(yīng)的接口,包含一個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();
    }
}

你發(fā)現(xiàn),你每次開車之前,都得先租一輛車,即新建一個對象。

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

對于高富帥而言,這有點麻煩,也有點不夠優(yōu)雅。

在現(xiàn)實系統(tǒng)中,高并發(fā)情況下,每個請求都新建一個對象,會導(dǎo)致內(nèi)存占用量過大。

高并發(fā)時新建過多實例會導(dǎo)致來不及GC或者是GC太頻繁。

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

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

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

買買買

映射到真實系統(tǒng),在調(diào)用方發(fā)起第一次請求時,新建對象并將其保存,之后的請求就無需再新建對象。

小菜決定使用蠅量模式進行重構(gòu)。

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

就是一把梭
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,用于保存已經(jīng)建立的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

經(jīng)過重構(gòu),發(fā)現(xiàn)只有在第一次借對應(yīng)品牌車子時才新建對象,之后的請求直接開車就可以了。

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

老司機開車

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


三:再理解

  1. 例子中相同牌子的車子可以做到完全共享,使用時直接從map中取出即可。如果不同對象只有局部屬性共享,則需要在取出之后,設(shè)置一些非共享屬性。
  2. 該模式共享了對象,執(zhí)行的方法為無狀態(tài)的,例如例子中的drive方法只輸出字符串。如果方法帶狀態(tài),需要考慮結(jié)果是否正確。
  3. 蠅量模式節(jié)約了內(nèi)存空間,減少了對象創(chuàng)建時間。
  4. Java中的字符串常量用到了該模式。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容