什麼是享元模式?
在gof的《設計模式:可復用物件導向軟體的基礎》一書中對享元模式是這樣說的:運用共享技術有效地支援大量細粒度的物件。
就如上面說的棋子,如果每個棋子都new乙個物件,就會存在大量細粒度的棋子物件,這對伺服器的記憶體空間是一種考驗,也是一種浪費。我們都知道,比如我在2013號房間和別人下五子棋,2014號房間也有人在下五子棋,並不會因為我在2013號房間,而別人在2014號房間,而導致我們的棋子是不一樣的。這就是說,2013號房間和2014號房間的棋子都是一樣的,所有的五子棋房間的棋子都是一樣的。唯一的不同是每個棋子在不同的房間的不同棋盤的不同位置上。所以,對於棋子來說,我們不用放乙個棋子就new乙個棋子物件,只需要在需要的時候,去請求獲得對應的棋子物件,如果沒有,就new乙個棋子物件;如果有了,就直接返回棋子物件。這裡以五子棋為例子,進行分析,當玩家在棋盤上放入第乙個白色棋子時,此時由於沒有白色棋子,所以就new乙個白色棋子;當另乙個玩家放入第乙個黑色棋子時,此時由於沒有黑色棋子,所以就需要new乙個黑色棋子;當玩家再次放入乙個白色棋子時,就去查詢是否有已經存在的白色棋子物件,由於第一次已經new了乙個白色棋子物件,所以,現在不會再次new乙個白色棋子物件,而是返回以前new的白色棋子物件;對於黑色棋子,亦是同理;獲得了棋子物件,我們只需要設定棋子的不同棋盤位置即可。
uml類圖
flyweight:描述乙個介面,通過這個介面flyweight可以接受並作用於外部狀態;
concreteflyweight:實現flyweight介面,並為定義了一些內部狀態,concreteflyweight物件必須是可共享的;同時,它所儲存的狀態必須是內部的;即,它必須獨立於concreteflyweight物件的場景;
unsharedconcreteflyweight:並非所有的flyweight子類都需要被共享。flyweight介面使共享成為可能,但它並不強制共享。
flyweightfactory:建立並管理flyweight物件。它需要確保合理地共享flyweight;當使用者請求乙個flyweight時,flyweightfactory物件提供乙個已建立的例項,如果請求的例項不存在的情況下,就新建立乙個例項;
client:維持乙個對flyweight的引用;同時,它需要計算或儲存flyweight的外部狀態。
實現要點
根據我們的經驗,當要將乙個物件進行共享時,就需要考慮到物件的狀態問題了;不同的客戶端獲得共享的物件之後,可能會修改共享物件的某些狀態;大家都修改了共享物件的狀態,那麼就會出現物件狀態的紊亂。對於享元模式,在實現時一定要考慮到共享物件的狀態問題。那麼享元模式是如何實現的呢?
在享元模式中,有兩個非常重要的概念:內部狀態和外部狀態。
內部狀態儲存於flyweight中,它包含了獨立於flyweight場景的資訊,這些資訊使得flyweight可以被共享。而外部狀態取決於flyweight場景,並根據場景而變化,因此不可共享。使用者物件負責在必要的時候將外部狀態傳遞給flyweight。
flyweight執行時所需的狀態必定是內部的或外部的。內部狀態儲存於concreteflyweight物件之中;而外部物件則由client物件儲存或計算。當使用者呼叫flyweight物件的操作時,將該狀態傳遞給它。同時,使用者不應該直接對concreteflyweight類進行例項化,而只能從flyweightfactory物件得到concreteflyweight物件,這可以保證對它們適當地進行共享;由於共享乙個例項,所以在建立這個例項時,就可以考慮使用單例模式來進行實現。
享元模式的工廠類維護了乙個例項列表,這個列表中儲存了所有的共享例項;當使用者從享元模式的工廠類請求共享物件時,首先查詢這個例項表,如果不存在對應例項,則建立乙個;如果存在,則直接返回對應的例項。
**實現
1 #include 2 #include 3 #include 4內部狀態包括棋子的顏色,外部狀態包括棋子在棋盤上的位置。最終,我們省去了多個例項物件儲存棋子顏色的空間,從而達到了空間的節約。using
namespace
std;
56 typedef struct
pointtag
712 pointtag(int a, int
b)13
1718
bool
operator
1924
else
if (x ==other.x)
2528
29return
false;30
}31}point;
3233 typedef enum
piececolortag
34piececolor;
3839
class
cpiece
4043 piececolor getcolor()
4445
//set the external state
46void setpoint(point point)
47 point getpoint()
4849
protected:50
//internal state
51piececolor m_color;
5253
//external state
54point m_point;
55};
5657
class cgomoku : public
cpiece
5861
};62
63class
cpiecefactory
6474
else
7583}84
if (ppiece ==null)
8589}90
return
ppiece;
91}
9293 ~cpiecefactory()
94102
}103
}104
105private
:106 vectorm_vecpiece;
107};
108109
class
cchessboard
110118
else
119123
}124
125void
showallpieces()
126133
else
134137
}138
}139
140private
:142
};143
144int
main()
145180
if (ppiecefactory !=null)
181185 }
在上面的**中,我建立了乙個ccheseboard用於表示棋盤,棋盤類中儲存了放置的黑色棋子和白色棋子;這就相當於在外部儲存了共享物件的外部狀態;對於棋盤物件,我們是不是又可以使用享元模式呢?再設計乙個棋局類進行管理棋盤上的棋子布局,用來儲存外部狀態。對於這個,這裡不進行討論了。
優點享元模式可以避免大量非常相似物件的開銷。在程式設計時,有時需要生成大量細粒度的類例項來表示資料。如果能發現這些例項資料除了幾個引數外基本都是相同的,使用享元模式就可以大幅度地減少物件的數量。
使用場合
flyweight模式的有效性很大程度上取決於如何使用它以及在何處使用它。當以下條件滿足時,我們就可以使用享元模式了。
乙個應用程式使用了大量的物件;
完全由於使用大量的物件,造成很大的儲存開銷;
物件的大多數狀態都可變為外部狀態;
如果刪除物件的外部狀態,那麼可以用相對較少的共享物件取代很多組物件。
擴充套件之前總結了組合模式組合模式,現在回過頭來看看,享元模式就好比在組合模式的基礎上加上了乙個工廠類,進行共享控制。是的,組合模式有的時候會產生很多細粒度的物件,很多時候,我們會將享元模式和組合模式進行結合使用。
總結使用享元模式可以避免大量相似物件的開銷,減小了空間消耗;而空間的消耗是由以下幾個因素決定的:
例項物件減少的數目;
物件內部狀態的數目;物件內部狀態越多,消耗的空間也會越少;
外部狀態是計算的還是儲存的;由於外部狀態可能需要儲存,如果外部狀態儲存起來,那麼空間的節省就不會太多。
共享的flyweight越多,儲存節約也就越多,節約量隨著共享狀態的增多而增大。當物件使用大量的內部及外部狀態,並且外部狀態是計算出來的而非儲存的時候,節約量將達到最大。所以,可以使用兩種方法來節約儲存:用共享減少內部狀態的消耗;用計算時間換取對外部狀態的儲存。
同時,在實現的時候,一定要控制好外部狀態與共享物件的對應關係,比如我在**實現部分,在ccheseboard類中使用了乙個map進行彼此之間的對映,這個對映在實際開發中需要考慮的。
c 設計模式(享元模式)
good 運用共享技術有效地支援大量細粒度的物件 對於c 來說就是共用乙個記憶體塊啦,物件指標指向同乙個地方 如果乙個應用程式使用了大量的物件,而這些物件造成了很大的儲存開銷就應該考慮使用。還有就是物件的大多數狀態可以外部狀態,如果刪除物件的外部狀態,那麼可以用較少的共享物件取代多組物件,此時可以考...
C 設計模式 享元模式
ifndef flyweight h define flyweight h include include 說明 當系統中細粒度物件數量太多時,會導致執行代價過高,帶來效能下降等問題。享元模式正是為解決這一類問題而誕生的。在享元模式中通常會出現工廠模式,需要建立乙個享元工廠來負責維護乙個享元池,用於...
c 設計模式 享元模式
二 uml類圖 三 例子 四 優缺點 五 使用場景 關注類和物件的組合。繼承的概念被用來組合介面和定義組合物件,從而獲得新功能。運用共享技術來有効地支援大量細粒度物件的復用。它通過共享已經存在的物件來大幅度減少需要建立的物件數量 避免大量相似類的開銷,從而提高系統資源的利用率。能夠大幅度地減少需要例...