一、引言
????????在軟件開發過程中,我們常常面臨著創建大量細粒度對象的情況,這可能會導致內存占用過高、性能下降等問題。享元模式(Flyweight Pattern)就像是一位空間管理大師,它能夠在不影響功能的前提下,有效地減少對象的數量,從而優化系統資源的使用。
二、定義與描述
????????享元模式是一種結構型設計模式,它主要用于通過共享盡可能多的相似對象來減少內存使用和提高性能。其核心思想是將對象的狀態分為內部狀態(intrinsic state)和外部狀態(extrinsic state)。內部狀態是對象可共享的部分,它不會隨環境的改變而改變;外部狀態則是隨環境變化而變化的部分,不能被共享。
三、抽象背景
????????假設我們正在開發一個游戲,游戲中有許多相同類型的怪物,這些怪物可能只有一些屬性(如生命值、攻擊力等)的差異。如果我們為每個怪物都創建一個獨立的對象,那么隨著怪物數量的增加,內存的消耗將變得非常大。享元模式就可以用來解決這個問題,將怪物的通用屬性(如外觀、基本行為等內部狀態)進行共享,而將每個怪物特有的屬性(如當前生命值、當前攻擊力等外部狀態)單獨處理。
四、適用場景與現實問題解決
- 圖形繪制系統
- 在圖形繪制系統中,可能需要繪制大量相同類型的圖形,如圓形、矩形等。這些圖形的形狀(內部狀態)是固定的,但它們的位置、顏色(外部狀態)可能不同。通過享元模式,可以共享圖形的形狀對象,減少內存占用。
- 文檔編輯器中的字符處理
- 文檔編輯器中有大量的字符,每個字符的字體樣式、大小等屬性可能不同,但字符的基本形狀(內部狀態)是相同的。享元模式可以用來共享字符的基本形狀對象。
五、享元模式的現實生活的例子
- 汽車租賃公司
- 汽車租賃公司有多種類型的汽車可供租賃,如轎車、SUV等。每一種類型的汽車(內部狀態)是固定的,包括車型、座位數等。而汽車的顏色、當前里程數(外部狀態)是隨每一次租賃而變化的。租賃公司可以將相同類型的汽車看作是享元對象,共享汽車的基本信息,從而更好地管理車輛資源。
- 咖啡店的咖啡杯
- 咖啡店有不同種類的咖啡杯,如拿鐵杯、卡布奇諾杯等。杯子的形狀(內部狀態)是固定的,但是杯子里咖啡的量、是否加糖(外部狀態)是不同的。咖啡店可以將相同類型的杯子看作享元對象,共享杯子的基本形狀信息。
六、初衷與問題解決
????????初衷是為了減少內存中對象的數量,提高系統的性能和資源利用率。通過共享內部狀態,避免了創建大量重復的對象,從而解決了因對象數量過多導致的內存占用過大和性能下降的問題。
七、代碼示例
Java示例
類圖:
FlyweightFactory
?類有一個私有屬性?flyweights
(類型為?Map<String, Flyweight>
)用于存儲享元對象,并且有?getFlyweight
?方法,根據傳入的?key
?來獲取或創建具體的享元對象。Flyweight
?是抽象類,有受保護的屬性?key
,構造方法以及抽象方法?operation
,定義了享元對象的基本結構和行為規范。ConcreteFlyweight
?類繼承自?Flyweight
?類,實現了自己的構造方法,并覆寫了?operation
?方法,用于提供具體的享元行為實現。
流程圖:
????????首先創建?FlyweightFactory
?對象,然后兩次調用?getFlyweight
?方法來獲取享元對象(第二次調用時會復用第一次創建的對象,因為已經存在對應?key
?的對象了),最后分別調用獲取到的享元對象的?operation
?方法來執行具體操作。?
代碼:
import java.util.HashMap;
import java.util.Map;// 享元工廠類
class FlyweightFactory {private Map<String, Flyweight> flyweights = new HashMap<>();public Flyweight getFlyweight(String key) {if (!flyweights.containsKey(key)) {flyweights.put(key, new ConcreteFlyweight(key));}return flyweights.get(key);}
}// 抽象享元類
abstract class Flyweight {protected String key;public Flyweight(String key) {this.key = key;}abstract void operation();
}// 具體享元類
class ConcreteFlyweight extends Flyweight {public ConcreteFlyweight(String key) {super(key);}@Overridevoid operation() {System.out.println("具體享元 " + key + " 被調用");}
}public class Main {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight flyweight1 = factory.getFlyweight("A");Flyweight flyweight2 = factory.getFlyweight("A");flyweight1.operation();flyweight2.operation();}
}
C++示例
#include <iostream>
#include <unordered_map>// 抽象享元類
class Flyweight {
public:virtual void operation() = 0;virtual ~Flyweight() {}
};// 具體享元類
class ConcreteFlyweight : public Flyweight {
private:std::string key;
public:ConcreteFlyweight(std::string key) : key(key) {}void operation() override {std::cout << "具體享元 " << key << " 被調用" << std::endl;}
};// 享元工廠類
class FlyweightFactory {
private:std::unordered_map<std::string, Flyweight*> flyweights;
public:Flyweight* getFlyweight(std::string key) {if (flyweights.find(key) == flyweights.end()) {flyweights[key] = new ConcreteFlyweight(key);}return flyweights[key];}~FlyweightFactory() {for (auto it : flyweights) {delete it.second;}}
};int main() {FlyweightFactory factory;Flyweight* flyweight1 = factory.getFlyweight("A");Flyweight* flyweight2 = factory.getFlyweight("A");flyweight1->operation();flyweight2->operation();return 0;
}
Python示例
class FlyweightFactory:def __init__(self):self.flyweights = {}def get_flyweight(self, key):if key not in self.flyweights:self.flyweights[key] = ConcreteFlyweight(key)return self.flyweights[key]class Flyweight:def __init__(self, key):self.key = keydef operation(self):passclass ConcreteFlyweight(Flyweight):def operation(self):print(f"具體享元 {self.key} 被調用")if __name__ == "__main__":factory = FlyweightFactory()flyweight1 = factory.get_flyweight("A")flyweight2 = factory.get_flyweight("A")flyweight1.operation()flyweight2.operation()
Go示例
package mainimport ("fmt"
)// 抽象享元接口
type Flyweight interface {operation()
}// 具體享元結構體
type ConcreteFlyweight struct {key string
}func (cf *ConcreteFlyweight) operation() {fmt.Printf("具體享元 %s 被調用\n", cf.key)
}// 享元工廠結構體
type FlyweightFactory struct {flyweights map[string]Flyweight
}func NewFlyweightFactory() *FlyweightFactory {return &FlyweightFactory{flyweights: make(map[string]Flyweight),}
}func (ff *FlyweightFactory) getFlyweight(key string) Flyweight {if _, ok := ff.flyweights[key];!ok {ff.flyweights[key] = &ConcreteFlyweight{key}}return ff.flyweights[key]
}func main() {factory := NewFlyweightFactory()flyweight1 := factory.getFlyweight("A")flyweight2 := factory.getFlyweight("A")flyweight1.operation()flyweight2.operation()
}
八、享元模式的優缺點
-
優點
- 減少內存占用:通過共享對象,大大減少了創建對象所需的內存空間,特別是在處理大量相似對象時效果顯著。
- 提高性能:減少了對象的創建和銷毀操作,從而提高了系統的運行速度。
- 易于維護:將對象的內部狀態和外部狀態分離,使得代碼結構更加清晰,易于理解和維護。
-
缺點
- 增加復雜性:需要額外的代碼來管理享元對象的創建、共享和維護,這可能會增加系統的復雜性。
- 外部狀態管理:外部狀態的處理需要額外的設計考慮,如果處理不當可能會導致邏輯混亂。
九、享元模式的升級版
????????一種常見的升級版是組合享元模式(Composite Flyweight Pattern)。在這種模式下,享元對象可以組合成更復雜的結構。例如,在圖形繪制系統中,不僅可以共享單個圖形(如圓形、矩形)的享元對象,還可以將這些享元對象組合成更復雜的圖形(如由多個圓形和矩形組成的復雜圖案),而這個復雜圖案本身也可以作為一個享元對象被共享。這樣可以進一步提高系統的靈活性和資源利用率。
思維導圖: