設(shè)計模式-享元模式

享元模式介紹

享元模式(Flyweight Pattern)是結(jié)構(gòu)型設(shè)計模式的一種。其實(shí)對象池的一種實(shí)現(xiàn)方式,通過緩存可共享的對象,來減少對象的創(chuàng)建,可以降低程序內(nèi)存占用,提高程序性能。

享元模式定義

使用共享對象有效的支持大量細(xì)粒度的對象

享元模式的使用場景

  1. 系統(tǒng)中存在大量的相似對象。
  2. 細(xì)粒度的對象都具備接近的外部狀態(tài),而且內(nèi)部狀態(tài)與環(huán)境無關(guān)。
  3. 需要緩沖池的場景。

內(nèi)部狀態(tài):對象中可以共享的狀態(tài),其不會隨著環(huán)境變化。
外部狀態(tài):對象中不可以共享的狀態(tài),它們會隨著環(huán)境的改變而變化。

享元模式的 UML 類圖


角色介紹:

  • Flyweight:享元對象抽象類。
  • ConcreteFlyweight:具體享元對象。
  • FlyweightFactory:享元工廠,負(fù)責(zé)管理享元對象池和創(chuàng)建享元對象。

享元模式的簡單實(shí)現(xiàn)

這里某東出售手機(jī)為例,每個用戶選擇手機(jī)后都生成手機(jī)商品對象顯然耗費(fèi)很多資源,甚至造成 OOM,我們就可以采用享元模式優(yōu)化。

抽象享元角色

抽象享元角色是一個商品接口,它定義了showGoodsPrice方法用來展示商品的價格:

public interface IPrice {
    public void showGoodsPrice(String version);
}

具體享元角色

public class Phone implements IPrice {
    public String name;
    public String version;
    public int price;

    public Phone(String name) {
        this.name = name;
    }

    @Override
    public void showGoodsPrice(String version) {
        this.version = version;
        price = queryPrice(version);
        System.out.println("手機(jī) " + name + " 存儲版本為 " + version + ",售價為:" + price);
    }

    private int queryPrice(String version) {
        switch (version) {
            case "128G":
                return 5000;
            case "256G":
                return 6000;
        }
        return 99999;
    }
}

其中 name 屬于內(nèi)部狀態(tài),version 和 price 屬于外部狀態(tài)。showGoodsPrice方法根據(jù)手機(jī)存儲 version 的不同會打印出不同的價格。

享元工廠

public class PhoneFactory {
    private static Map<String, Phone> sPhoneMap = new HashMap<>();

    public static Phone getPhone(String name) {
        Phone ret = null;
        if (sPhoneMap.containsKey(name)) {
            System.out.println("使用緩存,key 為" + name);
            ret = sPhoneMap.get(name);
        } else {
            System.out.println("創(chuàng)建對象,key 為" + name);
            ret = new Phone(name);
            sPhoneMap.put(name, ret);
        }
        return ret;
    }
}

享元工廠PhoneFactory 用來創(chuàng)建 Phone 對象。通過Map容器來存儲 Phone 對象,將內(nèi)部狀態(tài) name 作為Map的key,以便標(biāo)識 Phone 對象。如果Map容器中包含此key,則使用Map容器中存儲的 Phone 對象,否則就新創(chuàng)建 Phone 對象,并放入Map容器中。

客戶端調(diào)用

public class Client {
    public static void main(String[] args) {
        Phone phone1 = PhoneFactory.getPhone("HUAWEI mate30");
        phone1.showGoodsPrice("128G");
        Phone phone2 = PhoneFactory.getPhone("HUAWEI mate30");
        phone2.showGoodsPrice("256G");
    }
}

輸出結(jié)果:

創(chuàng)建對象,key 為HUAWEI mate30
手機(jī) HUAWEI mate30 存儲版本為 128G,售價為:5000
使用緩存,key 為HUAWEI mate30
手機(jī) HUAWEI mate30 存儲版本為 256G,售價為:6000

從輸出結(jié)果可以看到,只有第一次查詢手機(jī)創(chuàng)建了一次對象,后續(xù)的查詢都使用的是對象池中的對象。該例子中內(nèi)存狀態(tài)就是手機(jī)名稱,在查詢HUAWEI mate30 時內(nèi)部狀態(tài)不會發(fā)生變化;外部狀態(tài)就是存儲版本和價格,價格會隨著存儲版本不同而變化。通過緩存較少了內(nèi)存占用,降低了gc 回收的次數(shù),從而性能大大提高。

總結(jié)

享元模式優(yōu)點(diǎn)
1.大大減少對象的創(chuàng)建,降低系統(tǒng)的內(nèi)存,減少 GC ,提高性能。
享元模式缺點(diǎn)
1.提高了系統(tǒng)的復(fù)雜度,需要分離出外部狀態(tài)和內(nèi)部狀態(tài),當(dāng)然為了設(shè)備性能,這點(diǎn)必須做的。

Android 源碼中的享元模式

Android 消息機(jī)制 Message 中 obtain() 和 recycle() 方法

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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