【設(shè)計模式筆記】(二十)- 享元模式

簡介

享元模式,是對象池的一種實現(xiàn)坛芽,主要用于減少創(chuàng)建對象的數(shù)量,以減少內(nèi)存占用和提高性能翼抠。定義:運用共享技術(shù)有效地支持大量細粒度的對象咙轩。意思就是采用對象共享的形式來實現(xiàn)大量對象的情況。有大量對象的情況阴颖,有可能導(dǎo)致內(nèi)存溢出或者重復(fù)創(chuàng)建之前已經(jīng)創(chuàng)建的相同對象活喊。

先舉個簡單的例子,android中的ViewHolder緩存view量愧,為了優(yōu)化性能存在一種寫法钾菊,使用一個集合存儲已經(jīng)被實例化過的view帅矗,就不需要每次都去創(chuàng)建了,這就是享元模式的一種簡單應(yīng)用煞烫。

public abstract class RvBaseViewHolder<T> extends RecyclerView.ViewHolder {

    private SparseArray<View> mViews;

    public RvBaseViewHolder(@NonNull View itemView) {
        super(itemView);
        mViews = new SparseArray<>();
    }

    public abstract void bindData(int position, @Nullable T data);

    /**快速獲取view*/
    public <V extends View> V findViewById(int viewId){
        View view = mViews.get(viewId);
        if(null == view){
            view = itemView.findViewById(viewId);
            if(null != view){
                mViews.put(viewId,view);
            }else{
                return null;
            }
        }
        return (V) view;
    }
}

UML圖

享元模式.png
  • Flyweight:享元模式抽象類或接口
  • ConcreateFlyweight:具體的享元對象
  • FlyweightFactory:享元工廠浑此,負責(zé)管理享元對象池和創(chuàng)建享元對象

Flyweight代表輕量級的意思。

簡單示例

過年搶票滞详,大家肯定都不陌生凛俱,各種刷票插件、軟件什么的料饥。在用戶設(shè)置好出發(fā)和到達之后蒲犬,每次查詢請求都返回一系列的車票結(jié)果。當(dāng)數(shù)千萬的用戶在不斷請求查詢結(jié)果時岸啡,如果每次查詢結(jié)果都是重新創(chuàng)建返回的原叮,可想而知,肯定會有大量的重復(fù)對象的創(chuàng)建巡蘸、銷毀奋隶,內(nèi)存占用和GC的壓力都會隨之增大。而享元模式就能很好的應(yīng)對這種情況赡若,車次都是固定的达布,根據(jù)出發(fā)地和到達地查詢出來的車次基本都是相同的(當(dāng)然你還可以添加更多的篩選條件)。

我們可以將這些共享的對象緩存起來逾冬,用戶查詢時優(yōu)先使用緩存黍聂,如果沒有緩存則重新創(chuàng)建,這樣就不必要在重復(fù)創(chuàng)建和銷毀對象了身腻。

首先产还,創(chuàng)建一個Ticket接口,定義輸出車票信息的方法

public interface Ticket {
    public void showTicketInfo(String info);
}

再是具體的實現(xiàn)TrainTicket

public class TrainTicket implements Ticket {
    //出發(fā)地
    private final String from;
    //到達地
    private final String to;
    //鋪位
    private String bunk;
    //價格
    private int price;

    public TrainTicket(String from, String to) {
        this.from = from;
        this.to = to;
    }

    @Override
    public void showTicketInfo(String bunk) {
        price = new Random().nextInt(200);
        System.out.println("查詢 從 "+from+" 到 " + to + " 的 " + bunk + " 車票嘀趟,價格:" + price);
    }
}

接著就是TicketFactory類脐区,不同于之前的工廠模式,工廠模式每個返回的對象都是新創(chuàng)建的她按,而享元模式需要做緩存牛隅,具體代碼如下:

public class TicketFactory {
    private static Map<String,Ticket> ticketMap = new ConcurrentHashMap<>();

    public static Ticket getTicket(String from,String to){
        final String key = from + "-" + to;
        if(ticketMap.containsKey(key)){
            return ticketMap.get(key);
        }else{
            Ticket ticket = new TrainTicket(from, to);
            ticketMap.put(key,ticket);
            return ticket;
        }
    }
}

這就面了,每次獲取車票對象時的創(chuàng)建酌泰,享元模式有效的減少了重復(fù)對象的創(chuàng)建媒佣。

Android中的享元模式

當(dāng)我們需要用到Handler發(fā)送信息的時候可能會注意到一點,并不是每次都是創(chuàng)建一個Message對象陵刹,Handler有個obtainMessage方法其實還有幾個重寫方法默伍,這個方法可以從已經(jīng)創(chuàng)建過的Message中重新獲取一個Message對象,以此來降低創(chuàng)建Message對象的內(nèi)存開銷。

HandlerobtainMessage方法調(diào)用了Messageobtain方法也糊,最后具體的代碼如下:

public final class Message implements Parcelable {
    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    
    //代碼省略...

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    //代碼省略...
}

其中sPoolSync為同步鎖對象炼蹦,而sPool是一個Message對象,這里可能會有點奇怪為什么會叫sPool卻是一個Message對象狸剃。其實這里Message的實現(xiàn)是以列表的形式實現(xiàn)的掐隐,next也是一個Message對象指向的就是下一個Message對象。這樣子钞馁,這段代碼大概就能理解了瑟枫。

這里源碼只說這么多,有興趣的可以閱讀源碼指攒。

總結(jié)

享元模式還是比較簡單的,在創(chuàng)建重復(fù)對象的情況中大大降低了內(nèi)存的消耗僻焚,提高了程序的性能允悦。同時也提高了系統(tǒng)的復(fù)雜性,需要分離出外部狀態(tài)和內(nèi)部狀態(tài)虑啤,而且外部狀態(tài)應(yīng)具有固化性隙弛,不應(yīng)該隨內(nèi)部狀態(tài)改變而改變,這樣就使得程序變的很混亂狞山。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末全闷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子萍启,更是在濱河造成了極大的恐慌总珠,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勘纯,死亡現(xiàn)場離奇詭異局服,居然都是意外死亡,警方通過查閱死者的電腦和手機驳遵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門淫奔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人堤结,你說我怎么就攤上這事唆迁。” “怎么了竞穷?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵唐责,是天一觀的道長。 經(jīng)常有香客問我来庭,道長妒蔚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮肴盏,結(jié)果婚禮上科盛,老公的妹妹穿的比我還像新娘。我一直安慰自己菜皂,他們只是感情好贞绵,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恍飘,像睡著了一般榨崩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上章母,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天母蛛,我揣著相機與錄音,去河邊找鬼乳怎。 笑死彩郊,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚪缀。 我是一名探鬼主播秫逝,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼询枚!你這毒婦竟也來了违帆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤金蜀,失蹤者是張志新(化名)和其女友劉穎刷后,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體廉油,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡惠险,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了抒线。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片班巩。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嘶炭,靈堂內(nèi)的尸體忽然破棺而出抱慌,到底是詐尸還是另有隱情,我是刑警寧澤眨猎,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布抑进,位于F島的核電站,受9級特大地震影響睡陪,放射性物質(zhì)發(fā)生泄漏寺渗。R本人自食惡果不足惜匿情,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望信殊。 院中可真熱鬧炬称,春花似錦、人聲如沸涡拘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鳄乏。三九已至跷车,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間橱野,已是汗流浹背朽缴。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留水援,地道東北人不铆。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像裹唆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子只洒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容