享元模式介紹
享元模式(Flyweight Pattern)是結(jié)構(gòu)型設(shè)計模式的一種。其實(shí)對象池的一種實(shí)現(xiàn)方式,通過緩存可共享的對象,來減少對象的創(chuàng)建,可以降低程序內(nèi)存占用,提高程序性能。
享元模式定義
使用共享對象有效的支持大量細(xì)粒度的對象
享元模式的使用場景
- 系統(tǒng)中存在大量的相似對象。
- 細(xì)粒度的對象都具備接近的外部狀態(tài),而且內(nèi)部狀態(tài)與環(huán)境無關(guān)。
- 需要緩沖池的場景。
內(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)必須做的。