目錄
在面向對象程序設計過程中,有時會面臨要創建大量相同或相似對象實例的問題。創建那麼多的對象將會耗費很多的系統資源,它是系統性能提高的一個瓶頸。例如,圍棋和五子棋中的黑白棋子,圖像中的座標點或顏色,局域網中的路由器、交換機和集線器,教室裏的桌子和凳子等。這些對象有很多相似的地方,如果能把它們相同的部分提取出來共享,則能節省大量的系統資源。
也就是說在一個系統中如果有多個相同的對象,那麼只共享一份就可以了,不必每個都去實例化一個對象。比如說一個文本系統,每個字母定一個對象,那麼大小寫字母一共就是52個,那麼就要定義52個對象。如果有一個1M的文本,那麼字母是何其的多,如果每個字母都定義一個對象那麼內存早就爆了。那麼如果要是每個字母都共享一個對象,那麼就大大節約了資源。
在Flyweight模式中,由於要產生各種各樣的對象,所以在Flyweight(享元)模式中常出現Factory模式。Flyweight的內部狀態是用來共享的,Flyweight factory負責維護一個對象存儲池(Flyweight Pool)來存放內部狀態的對象。Flyweight模式是一個提高程序效率和性能的模式,會大大加快程序的運行速度。
定義
享元模式是一種結構型設計模式。該模式主要是通過將對象的粒度細分,從而減少創建大量對象所佔的內存。定義爲:
- 定義:使用共享對象有效的支持大量細粒度的對象
- Flyweight(抽象享元角色)—— 定義對象的內部狀態和外部狀態及其對應的方法。
- ConcreteFlyweight(具體享元角色)—— 實現抽象享元角色的方法,在具體的角色中,實現具體方法時需要注意將內部狀態與外部狀態區分開,不應出現二者同時被修改的方法。
- unshareConcreteFlyweight(不可共享的享元角色)—— 該角色是不能使用共享技術的對象,一般在討論線程安全時使用。
- FlyweightFactory(享元工廠)—— 負責創建和管理享元角色。當客戶對象請求一個享元對象時,享元工廠檢査系統中是否存在符合要求的享元對象,如果存在則提供給客戶;如果不存在的話,則創建一個新的享元對象。
內部狀態,就是各個對象不會隨着環境的改變而改變的可共享部分;
外部狀態,指對象隨環境改變而改變的不可以共享的部分。內部狀態和外部狀態彼此互不影響,改變其中一個並不會改變另一個的行爲。
下面用C++ 代碼實現大話設計模式本章代碼:
#include <iostream>
#include<string>
#include<map>
using namespace std;
//用戶類
class User
{
private:
string m_name;
public:
User(string name)
{
m_name = name;
}
std::string GetName()
{
return m_name;
}
};
//Flyweight類,此處爲抽象網站類
class WebSite
{
public:
virtual ~WebSite() = default;
virtual void Use(User user) = 0;
};
//ConcreteFlyweight類,此處爲具體網站類
class ConcreteWebSite :public WebSite
{
private:
string m_name;
public:
ConcreteWebSite(std::string name)
{
m_name = name;
}
void Use(User user)override
{
cout << "網站分類:" << m_name << " 用戶:" + user.GetName() << endl;
}
};
//FlyweightFactory類,此處爲網站工程類
class WebSiteFactory
{
private:
std::map<std::string, WebSite*> flyweights;
public:
~WebSiteFactory()
{
for (auto it = flyweights.begin(); it != flyweights.end(); ++it)
delete it->second;
}
WebSite* GetWebSiteCategory(string key)
{
for (auto it = flyweights.begin(); it != flyweights.end(); ++it)
{
if (it->first == key)
return it->second;
}
WebSite * website = new ConcreteWebSite(key);
flyweights.insert(pair<std::string, WebSite*>(key, website));
return website;
}
int GetWebSiteCount()
{
return flyweights.size();
}
};
int main()
{
WebSiteFactory f;
WebSite* fx = f.GetWebSiteCategory("產品展示");
fx->Use(User("小菜"));
WebSite* fy = f.GetWebSiteCategory("產品展示");
fy->Use(User("大鳥"));
WebSite* fz = f.GetWebSiteCategory("產品展示");
fz->Use(User("嬌嬌"));
WebSite* fl = f.GetWebSiteCategory("博客");
fl->Use(User("老頑童"));
WebSite* fm = f.GetWebSiteCategory("博客");
fm->Use(User("桃谷六仙"));
WebSite* fn = f.GetWebSiteCategory("博客");
fn->Use(User("南海鱷神"));
cout << "得到網站分類總數:" << f.GetWebSiteCount() << endl;
system("pause");
return 0;
}
優點
- 享元模式可以運用共享技術有效地支持大量細粒度的對象,大大減少對象的創建,降低系統的內存,使效率提高。
缺點
享元模式使得系統更加複雜。爲了使對象可以共享,需要將一些狀態外部化,這使得程序的邏輯複雜化。
享元模式將享元對象的狀態外部化,而讀取外部狀態使得運行時間稍微變長。
應用場景
- 系統中存在大量相同或相似的對象,這些對象耗費大量的內存資源。
- 大部分的對象可以按照內部狀態進行分組,且可將不同部分外部化,這樣每一個組只需保存一個內部狀態。
- 由於享元模式需要額外維護一個保存享元的數據結構,所以應當在有足夠多的享元實例時才值得使用享元模式。