享元模式介紹
享元模式(Flyweight Pattern)是結(jié)構(gòu)型設(shè)計(jì)模式的一種。其實(shí)對(duì)象池的一種實(shí)現(xiàn)方式,通過(guò)緩存可共享的對(duì)象,來(lái)減少對(duì)象的創(chuàng)建寒砖,可以降低程序內(nèi)存占用,提高程序性能嫉拐。
享元模式定義
使用共享對(duì)象有效的支持大量細(xì)粒度的對(duì)象
享元模式的使用場(chǎng)景
- 系統(tǒng)中存在大量的相似對(duì)象哩都。
- 細(xì)粒度的對(duì)象都具備接近的外部狀態(tài),而且內(nèi)部狀態(tài)與環(huán)境無(wú)關(guān)婉徘。
- 需要緩沖池的場(chǎng)景漠嵌。
內(nèi)部狀態(tài):對(duì)象中可以共享的狀態(tài),其不會(huì)隨著環(huán)境變化盖呼。
外部狀態(tài):對(duì)象中不可以共享的狀態(tài)儒鹿,它們會(huì)隨著環(huán)境的改變而變化。
享元模式的 UML 類圖
角色介紹:
- Flyweight:享元對(duì)象抽象類几晤。
- ConcreteFlyweight:具體享元對(duì)象约炎。
- FlyweightFactory:享元工廠,負(fù)責(zé)管理享元對(duì)象池和創(chuàng)建享元對(duì)象蟹瘾。
享元模式的簡(jiǎn)單實(shí)現(xiàn)
這里某東出售手機(jī)為例圾浅,每個(gè)用戶選擇手機(jī)后都生成手機(jī)商品對(duì)象顯然耗費(fèi)很多資源,甚至造成 OOM憾朴,我們就可以采用享元模式優(yōu)化狸捕。
抽象享元角色
抽象享元角色是一個(gè)商品接口,它定義了showGoodsPrice方法用來(lái)展示商品的價(jià)格:
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 + " 存儲(chǔ)版本為 " + version + "众雷,售價(jià)為:" + 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ī)存儲(chǔ) version 的不同會(huì)打印出不同的價(jià)格砾省。
享元工廠
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)建對(duì)象,key 為" + name);
ret = new Phone(name);
sPhoneMap.put(name, ret);
}
return ret;
}
}
享元工廠PhoneFactory 用來(lái)創(chuàng)建 Phone 對(duì)象鸡岗。通過(guò)Map容器來(lái)存儲(chǔ) Phone 對(duì)象,將內(nèi)部狀態(tài) name 作為Map的key编兄,以便標(biāo)識(shí) Phone 對(duì)象纤房。如果Map容器中包含此key,則使用Map容器中存儲(chǔ)的 Phone 對(duì)象翻诉,否則就新創(chuàng)建 Phone 對(duì)象炮姨,并放入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)建對(duì)象,key 為HUAWEI mate30
手機(jī) HUAWEI mate30 存儲(chǔ)版本為 128G碰煌,售價(jià)為:5000
使用緩存,key 為HUAWEI mate30
手機(jī) HUAWEI mate30 存儲(chǔ)版本為 256G舒岸,售價(jià)為:6000
從輸出結(jié)果可以看到,只有第一次查詢手機(jī)創(chuàng)建了一次對(duì)象芦圾,后續(xù)的查詢都使用的是對(duì)象池中的對(duì)象蛾派。該例子中內(nèi)存狀態(tài)就是手機(jī)名稱,在查詢HUAWEI mate30 時(shí)內(nèi)部狀態(tài)不會(huì)發(fā)生變化;外部狀態(tài)就是存儲(chǔ)版本和價(jià)格洪乍,價(jià)格會(huì)隨著存儲(chǔ)版本不同而變化眯杏。通過(guò)緩存較少了內(nèi)存占用,降低了gc 回收的次數(shù)壳澳,從而性能大大提高岂贩。
總結(jié)
享元模式優(yōu)點(diǎn)
1.大大減少對(duì)象的創(chuàng)建,降低系統(tǒng)的內(nèi)存巷波,減少 GC 萎津,提高性能。
享元模式缺點(diǎn)
1.提高了系統(tǒng)的復(fù)雜度抹镊,需要分離出外部狀態(tài)和內(nèi)部狀態(tài)锉屈,當(dāng)然為了設(shè)備性能,這點(diǎn)必須做的垮耳。