12套蒂、享元模式(Flyweight Pattern)

1. 享元模式

1.1 簡介

??享元(FlyWeight)模式顧名思義,既是輕量級的茫蛹。享元即是共享元素,或者說是共享對象烁挟。如何共享對象呢婴洼?就是在檢測對象產(chǎn)生的時候,如果產(chǎn)生的是同一個對象撼嗓,那么直接使用已經(jīng)產(chǎn)生的柬采,聽起來很像是單例模式,其實享元模式的內(nèi)部實現(xiàn)就是很類似與單例模式的懶漢模式且警。享元的好處就是粉捻,在某些場景下可以節(jié)省內(nèi)存,從而使得程序的性能得到提升斑芜。

flyweight設(shè)計模式意圖是:

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

??Flyweight設(shè)計模式是一種結(jié)構(gòu)設(shè)計模式肩刃,當(dāng)需要創(chuàng)建一個類的很多對象時,可以使用Flyweight設(shè)計模式杏头。應(yīng)用Flyweight模式主要考慮一下幾個因素:

  • 應(yīng)用程序要創(chuàng)建的對象數(shù)量應(yīng)該很大盈包。
  • 對象創(chuàng)建在內(nèi)存上很重要,也可能很耗時醇王。
  • 對象屬性可以分為內(nèi)在屬性和外在屬性呢燥,對象的外在屬性應(yīng)該由客戶端程序定義。

??應(yīng)用flyweight模式寓娩,我們需要將Object屬性劃分為內(nèi)部(intrinsic)屬性和外部(extrinsic)屬性叛氨。內(nèi)在屬性使對象唯一,而外在屬性由客戶端代碼設(shè)置并用于執(zhí)行不同的操作棘伴。

??Flyweight模式一般會和Factory模式搭配使用寞埠,一般會創(chuàng)建一個管理共享對象的Flyweight工廠,這樣實現(xiàn)對象的創(chuàng)建排嫌、保存畸裳、返回等。

2. FlyWeight示例

??我們模擬一個搜索過程淳地,在搜索中可以使用google,baidu,bing進(jìn)行搜索怖糊,因為相同搜索引擎只需要創(chuàng)建一個帅容,不需要創(chuàng)建多個,同時相同的搜索結(jié)果(網(wǎng)址)只需要返回同一個即可伍伤,所以都用到了享元模式并徘。

搜索結(jié)果類:

public class Website {

    private String title;
    private String url;
    private long publishTimestamp;

    public Website(String title, String url, long publishTimestamp) {
        super();
        this.title = title;
        this.url = url;
        this.publishTimestamp = publishTimestamp;
    }

    public String getTitle() {
        return title;
    }

    public String getUrl() {
        return url;
    }

    public long getPublishTimestamp() {
        return publishTimestamp;
    }

    public String toString() {
        return String.format("[title:%s, url:%s, time:%d]", title, url, publishTimestamp);
    }
}

搜索結(jié)果享元類:

public class WebsiteRepository {

    private  static WebsiteRepository repo = new WebsiteRepository();

    public Map<String, Website> websites = new HashMap<String, Website>();

    public static WebsiteRepository getInstance() {
        return repo;
    }

    public Website getWebsite(String name, String url, long timestamp) {
        if (!websites.containsKey(url)) {
            Website newsite = new Website(name, url, timestamp);
            websites.put(url, newsite);
            return newsite;
        }

        return websites.get(url);
    }

    public int howmanyWebsite() {
        return websites.size();
    }
}

搜索引擎抽象類:

public abstract class SearchEngine {

    private long timeoutInMs;
    private String name;

    public SearchEngine (Long timeoutInMs, String name) {
        this.timeoutInMs = timeoutInMs;
        this.name = name;
    }

    public List<Website> search(String keyword) {
        System.out.println(name + " search engine with timeout set to "  + timeoutInMs + "start to search");
        return doSearch(keyword);
    }

    protected abstract List<Website> doSearch(String keyword);
}

** Google:**

public class Google extends SearchEngine {

    public Google(Long timeoutInMs, String name) {
        super(timeoutInMs, name);
    }

    @Override
    public List<Website> doSearch(String keyword) {
        List<Website> website = new ArrayList<Website>();
        website.add(WebsiteRepository.getInstance().getWebsite("title1", "url1", 1l));
        website.add(WebsiteRepository.getInstance().getWebsite("title2", "url2", 1l));
        website.add(WebsiteRepository.getInstance().getWebsite("title3", "url3", 1l));
        website.add(WebsiteRepository.getInstance().getWebsite("title4", "url4", 1l));
        return website;
    }
}

** Bing:**

public class Bing extends SearchEngine {

    public Bing(Long timeoutInMs, String name) {
        super(timeoutInMs, name);
    }

    @Override
    public List<Website> doSearch(String keyword) {
        List<Website> website = new ArrayList<Website>();
        website.add(WebsiteRepository.getInstance().getWebsite("title1", "url1", 1l));
        website.add(WebsiteRepository.getInstance().getWebsite("title2", "url2", 1l));
        website.add(WebsiteRepository.getInstance().getWebsite("title4", "url4", 1l));
        return website;
    }
}

BaiDu:

public class Baidu extends SearchEngine {

    public Baidu(Long timeoutInMs, String name) {
        super(timeoutInMs, name);
    }

    @Override
    public List<Website> doSearch(String keyword) {
        List<Website> website = new ArrayList<Website>();
        website.add(WebsiteRepository.getInstance().getWebsite("莆田醫(yī)院", "莆田醫(yī)院", 1l));
        website.add(WebsiteRepository.getInstance().getWebsite("title1", "url1", 1l));
        website.add(WebsiteRepository.getInstance().getWebsite("title2", "url2", 1l));
        website.add(WebsiteRepository.getInstance().getWebsite("title3", "url3", 1l));
        return website;
    }
}

搜索引擎享元類:

public class SearchEngineFlyweightFactory {

    private static final Map<String, SearchEngine> engines = new ConcurrentHashMap<>();

    public static SearchEngine getEngine(String name, long timeoutInMs, Class<? extends SearchEngine> clazz) {
        String key = name + timeoutInMs;
        SearchEngine engine = null;
        if (engines.get(key) == null) {
                try {
                    Constructor<? extends SearchEngine> cons =  clazz.getConstructor(Long.class, String.class);
                    engine = cons.newInstance(timeoutInMs, name);
                } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
                        | IllegalArgumentException | InvocationTargetException e) {
                    e.printStackTrace();
                }
                SearchEngine existing = engines.put(key, engine);
                if (existing != null) {
                    engine = existing;
                }
        } else {
            engine = engines.get(key);
        }

        return engine;
    }

    public static int howManyEngine() {
        return engines.size();
    }
}

客戶端調(diào)用:

public class FlyWeightPatternClient {

    public static void main(String[] args) throws SecurityException, ClassNotFoundException {
        search("baidu", 1000L, Baidu.class, "keyword");
        search("google", 1000L, Google.class, "keyword");
        search("bing", 1000L, Bing.class, "keyword");

        search("baidu", 1000L, Baidu.class, "keyword");
    }

    private static void search(String name, long timeoutInms, Class<? extends SearchEngine> clazz, String keyword) {
        SearchEngine engine = SearchEngineFlyweightFactory.getEngine(name, timeoutInms, clazz);
        List<Website> websites = engine.search(keyword);
        renderWebsite(websites);
        System.out.println(SearchEngineFlyweightFactory.howManyEngine());
        System.out.println(WebsiteRepository.getInstance().howmanyWebsite());
    }

    public static void renderWebsite(List<Website> websites) {
        System.out.println("start to render " );
        for (Website website : websites) {
            System.out.println(website.toString());
        }
    }
}

??在WebsiteRepository類中,使用了HashMap來將Website對象保存起來扰魂,這樣就形成了一個DAC(有向無環(huán)圖)麦乞,只要websites變量不被釋放,我們使用的共享單元是不會被釋放的劝评。這樣就保證了Website對象數(shù)組不被釋放姐直,在使用享元模式的時候一定要特別注意這種情況,因為垃圾回收器(GC)在內(nèi)存占用過多的時候被喚醒蒋畜,然后清理那些被再被使用的內(nèi)存声畏,采用的方式就是DAC。

3. FlyWeight應(yīng)用

FlyWeight應(yīng)用:

  • jvm中的字符串常量池姻成。相同的字符串會共享一個字符串對象插龄;
  • 在HystrixCommandKey.Factory類會存儲所有線程group相同的HystrixCommandKey
  • HystrixPropertiesFactory會存儲線程熔斷配置,這些都是享元模式的使用科展。

FlyWeight模式和Factory模式異同:

  • 二者都提供了對象工廠的功能均牢,生成了類的實例供外部調(diào)用。
  • 但Flyweight模式的目的是使某些相似對象共用類的同一個實例以達(dá)到節(jié)省內(nèi)存空間的目的才睹。
  • 工廠模式則不強(qiáng)制這一點徘跪,它只是負(fù)責(zé)生成類的實例,另外砂竖,工廠模式還通過工廠的繼承來生成具有繼承關(guān)系的不同類的實例真椿,而Flyweight模式不強(qiáng)調(diào)這一點。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乎澄,一起剝皮案震驚了整個濱河市突硝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌置济,老刑警劉巖解恰,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異浙于,居然都是意外死亡护盈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門羞酗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腐宋,“玉大人,你說我怎么就攤上這事⌒鼐海” “怎么了欺嗤?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卫枝。 經(jīng)常有香客問我煎饼,道長,這世上最難降的妖魔是什么校赤? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任吆玖,我火速辦了婚禮,結(jié)果婚禮上马篮,老公的妹妹穿的比我還像新娘沾乘。我一直安慰自己,他們只是感情好浑测,可當(dāng)我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布意鲸。 她就那樣靜靜地躺著,像睡著了一般尽爆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上读慎,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天漱贱,我揣著相機(jī)與錄音,去河邊找鬼夭委。 笑死幅狮,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的株灸。 我是一名探鬼主播崇摄,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼慌烧!你這毒婦竟也來了逐抑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤屹蚊,失蹤者是張志新(化名)和其女友劉穎厕氨,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汹粤,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡命斧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嘱兼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片国葬。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出汇四,到底是詐尸還是另有隱情接奈,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布船殉,位于F島的核電站鲫趁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏利虫。R本人自食惡果不足惜挨厚,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望糠惫。 院中可真熱鬧疫剃,春花似錦、人聲如沸硼讽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽固阁。三九已至壤躲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間备燃,已是汗流浹背碉克。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留并齐,地道東北人漏麦。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像况褪,于是被迫代替她去往敵國和親撕贞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,472評論 2 348