「全網(wǎng)最細(xì) + 實戰(zhàn)源碼案例」設(shè)計模式——享元模式
?
核心思想
- 享元模式(Flyweight Pattern)是一種結(jié)構(gòu)型設(shè)計模式,主要用于減少程序中大量對象的內(nèi)存消耗。該模式通過共享相同的數(shù)據(jù)來有效減少內(nèi)存的使用,適用于對象非常多且可以共享一部分狀態(tài)的場景。
- 核心:將對象的內(nèi)部狀態(tài)和外部狀態(tài)分離:
-
- 內(nèi)部狀態(tài):存儲在享元對象內(nèi)部的、不會改變的狀態(tài),通常是可以共享的。
- 外部狀態(tài):依賴于環(huán)境且變化的狀態(tài),不可共享。
?
編輯
結(jié)構(gòu)
1. Flyweight(享元角色)
- 聲明享元對象的接口,通常是不可變的。
2. ConcretFlyweight(具體享元角色)
- 實現(xiàn) Flyweight 接口,存儲共享狀態(tài)。
3. UnsharedConcreteFlyweight(非共享具體享元角色)
- 非共享的享元對象,一般不是由享元工廠創(chuàng)建。
4. FlyweightFactory(享元工廠)
- 用來管理享元對象的工廠類,確保共享對象的唯一性。
?
編輯
適用場景
- 存在大量相似對象。
- 對象狀態(tài)可分為內(nèi)部、外部狀態(tài)。
- 節(jié)省內(nèi)存,避免重復(fù)創(chuàng)建對象。
優(yōu)缺點
優(yōu)點:
- 節(jié)省內(nèi)存:避免重復(fù)創(chuàng)建相似對象。
- 提高性能:減少重復(fù)創(chuàng)建對象的開銷。
缺點:
- 增加復(fù)雜性:享元模式、工廠類的設(shè)計。
實現(xiàn)步驟
- 將需要改寫為享元的類成員變量拆分為兩個部分:
-
- 內(nèi)在狀態(tài): 包含不變的、 可在許多對象中重復(fù)使用的數(shù)據(jù)的成員變量。
- 外在狀態(tài): 包含每個對象各自不同的情景數(shù)據(jù)的成員變量
- 保留類中表示內(nèi)在狀態(tài)的成員變量, 并將其屬性設(shè)置為不可修改。 這些變量僅可在構(gòu)造函數(shù)中獲得初始數(shù)值。
- 找到所有使用外在狀態(tài)成員變量的方法, 為在方法中所用的每個成員變量新建一個參數(shù), 并使用該參數(shù)代替成員變量。
- 你可以有選擇地創(chuàng)建工廠類來管理享元緩存池, 它負(fù)責(zé)在新建享元時檢查已有的享元。 如果選擇使用工廠, 客戶端就只能通過工廠來請求享元, 它們需要將享元的內(nèi)在狀態(tài)作為參數(shù)傳遞給工廠。
- 客戶端必須存儲和計算外在狀態(tài) (情景) 的數(shù)值, 因為只有這樣才能調(diào)用享元對象的方法。 為了使用方便, 外在狀態(tài)和引用享元的成員變量可以移動到單獨的情景類中。
示例
?
編輯
?
編輯
// 享元角色
public abstract class AbstractBox {
// 獲取圖形
public abstract String getShape();
// 顯示圖形及顏色
public void display(String color){
System.out.println("方塊:" + getShape() + ",顏色:" + color);
}
}
// 具體享元角色——I
public class IBox extends AbstractBox{
@Override
public String getShape() {
return "I";
}
}
// 具體享元角色——L
public class LBox extends AbstractBox{
@Override
public String getShape() {
return "L";
}
}
// 具體享元角色——O
public class OBox extends AbstractBox{
@Override
public String getShape() {
return "O";
}
}
// 享元工廠(靜態(tài)內(nèi)部類單例方式)
public class BoxFactory {
// 享元對象池
private Map<String, AbstractBox> boxes;
// 私有化構(gòu)造器
private BoxFactory() {
boxes = new HashMap<>();
boxes.put("L", new LBox());
boxes.put("I", new IBox());
boxes.put("O", new OBox());
}
// 獲取享元對象
public AbstractBox getBox(String type) {
return boxes.get(type);
}
// 創(chuàng)建靜態(tài)內(nèi)部類
private static class SingletonHolder {
private static final BoxFactory INSTANCE = new BoxFactory();
}
// 獲取享元工廠實例
public static BoxFactory getInstance() {
return SingletonHolder.INSTANCE;
}
}
// 客戶端
public class Client {
public static void main(String[] args) {
// 1.獲取享元工廠
BoxFactory boxFactory = BoxFactory.getInstance();
// 2.獲取享元對象
AbstractBox box1 = boxFactory.getBox("L");
AbstractBox box2 = boxFactory.getBox("I");
AbstractBox box3 = boxFactory.getBox("O");
AbstractBox box4 = boxFactory.getBox("O");
// 3.獲取非享元對象并顯示
box1.display("red");
box2.display("blue");
box3.display("green");
box4.display("yellow");
// 4.驗證享元對象是否共享
System.out.println(box3 == box4);
}
}
在源碼中的應(yīng)用
?
編輯
?
編輯
?
編輯
?
編輯
與其他模式的關(guān)系
- 你可以使用享元模式實現(xiàn)組合模式樹的共享葉節(jié)點以節(jié)省內(nèi)存。
- 享元展示了如何生成大量的小型對象, 外觀模式則展示了如何用一個對象來代表整個子系統(tǒng)。
- 如果你能將對象的所有共享狀態(tài)簡化為一個享元對象, 那么享元就和單例模式類似了。 但這兩個模式有兩個根本性的不同。
-
- 只會有一個單例實體, 但是享元類可以有多個實體, 各實體的內(nèi)在狀態(tài)也可以不同。
- 單例對象可以是可變的。 享元對象是不可變的。
?

浙公網(wǎng)安備 33010602011771號