C++設計模式之享元模式(flyweight)(結構型)

一 定義

享元模式(Flyweight):運用共享技術有效地支持大量細粒度的對象。

享元模式的優點
1)享元模式的優點在於它可以極大減少內存中對象的數量,使得相同對象或相似對象在內存中只保存一份。
2)享元模式的外部狀態相對獨立,而且不會影響其內部狀態,從而使得享元對象可以在不同的環境中被共享。
享元模式的缺點
1)享元模式使得系統更加複雜,需要分離出內部狀態和外部狀態,這使得程序的邏輯複雜化。
2)爲了使對象可以共享,享元模式需要將享元對象的狀態外部化,而讀取外部狀態使得運行時間變長。

享元模式可以避免大量非常相似類的開銷。在程序設計中,有時需要生成大量細粒度的類實例來表示數據。如果能發現這些實例除了幾個參數外基本上都是相同的,有時就能夠大幅度的減少需要實例化的類的數量。如果能把那些參數移到類實例的外面,在方法調用時將它們傳遞進來,就可以通過共享大幅度地減少單個實例的數目。

也就是說,享元模式Flyweight執行時所需的狀態是有內部的也可能有外部的,內部狀態存儲於ConcreteFlyweight對象之中,而外部對象則應該考慮由客戶端對象存儲或計算,當調用Flyweight對象的操作時,將該狀態傳遞給它。

適用性

1)一個應用程序使用大量相同或者相似的對象,造成很大的存儲開銷。

2)對象的大部分狀態都可以外部化,可以將這些外部狀態傳入對象中。

3)如果刪除對象的外部狀態,那麼可以用相對較少的共享對象取代很多組對象。

4) 應用程序不依賴於對象標識。由於Flyweight對象可以被共享,對於概念上明顯有別的對象,標識測試將返回真值。

5)使用享元模式需要維護一個存儲享元對象的享元池,而這需要耗費資源,因此,應當在多次重複使用享元對象時才值得使用享元模式。

如果一個應用程序使用了大量的對象,而大量的這些對象造成了很大的存儲開銷時就應該考慮使用;還有就是對象的大多數狀態可以外部狀態,如果刪除對象的外部狀態,那麼可以用相對較少的共享對象取代很多組對象,此時可以考慮使用享元模式。

二 ULM圖

1) 抽象享元類(Flyweight): 描述一個接口,通過這個接口flyweight可以接受並作用於外部狀態。
2) 具體享元類(ConcreteFlyweight):實現Flyweight接口 ,併爲內部狀態( 如果有的話 )增加存儲空間 。ConcreteFlyweight對象必須是可共享的。它所存儲的狀態必須是內部的;即,它必須獨立於ConcreteFlyweight對象的場景。
3) 非共享具體享元類(UnsharedConcreteFlyweight):— 並非所有的Flyweight子類都需要被共享。Flyweight接口使共享成爲可能,但它並不強制共享。在Flyweight對象結構的某些層次,UnsharedConcreteFlyweight對象通常將ConcreteFlyweight對象作爲子節點。
4) 享元工廠類(FlyweightFactory):創建並管理flyweight對象, 確保合理地共享flyweight。本角色必須保證享元對象可以被系統適當地共享。當一個客戶端對象調用一個享元對象   flyweight的時候,享元工廠角色(Flyweight Factory對象)會檢查系統中是否已經有一個符合要求的享元對象。如果已經有了,享元工廠角色就應當提供這個已有的享元對象;如果系統中沒有一個適當的享元對象的話,享元工廠角色就應當創建一個合適的享元對象。
5)客戶(Client):維持一個對flyweight的引用。本角色需要自行計算或存儲一個(多個)flyweight的外部狀態。

三 實例

3.1 圍棋

摘自:https://blog.csdn.net/wuzhekai1985/article/details/6670298

圍棋的棋盤共有361格,即可放361個棋子。現在要實現一個圍棋程序,該怎麼辦呢?首先要考慮的是棋子棋盤的實現,可以定義一個棋子的類,成員變量包括棋子的顏色、形狀、位置等信息,另外再定義一個棋盤的類,成員變量中有個容器,用於存放棋子的對象。棋子的屬性除了顏色和位置,還有其他的,這裏略去。這兩個屬性足以說明問題。

棋子就是大量細粒度的對象。其屬性有內在的,比如顏色、形狀等,也有外在的,比如在棋盤上的位置。內在的屬性是可以共享的,區分在於外在屬性。因此,可以這樣設計,只需定義兩個棋子的對象,一顆黑棋和一顆白棋,這兩個對象含棋子的內在屬性;棋子的外在屬性,即在棋盤上的位置可以提取出來,存放在單獨的容器中。

#include <iostream>
#include <string>
#include <vector>

using namespace std;
//棋子顏色
enum PieceColor { BLACK, WHITE };
//棋子位置
struct PiecePos
{
	int x;
	int y;
	PiecePos(int a, int b) : x(a), y(b) {}
};
//棋子定義
class Piece
{
protected:
	PieceColor m_color; //顏色
public:
	Piece(PieceColor color) : m_color(color) {}
	~Piece() {}
	virtual void Draw() {}
};
class BlackPiece : public Piece
{
public:
	BlackPiece(PieceColor color) : Piece(color) {}
	~BlackPiece() {}
	void Draw() { cout << "繪製一顆黑棋\n"; }
};
class WhitePiece : public Piece
{
public:
	WhitePiece(PieceColor color) : Piece(color) {}
	~WhitePiece() {}
	void Draw() { cout << "繪製一顆白棋\n"; }
};

class PieceBoard
{
private:
	vector<PiecePos> m_vecPos; //存放棋子的位置
	Piece* m_blackPiece;       //黑棋棋子 
	Piece* m_whitePiece;       //白棋棋子
	string m_blackName;
	string m_whiteName;
public:
	PieceBoard(string black, string white) : m_blackName(black), m_whiteName(white)
	{
		m_blackPiece = NULL;
		m_whitePiece = NULL;
	}
	~PieceBoard() { delete m_blackPiece; delete m_whitePiece; }
	void SetPiece(PieceColor color, PiecePos pos)
	{
		if (color == BLACK)
		{
			if (m_blackPiece == NULL)  //只有一顆黑棋
				m_blackPiece = new BlackPiece(color);
			cout << m_blackName << "在位置(" << pos.x << ',' << pos.y << ")";
			m_blackPiece->Draw();
		}
		else
		{
			if (m_whitePiece == NULL)
				m_whitePiece = new WhitePiece(color);
			cout << m_whiteName << "在位置(" << pos.x << ',' << pos.y << ")";
			m_whitePiece->Draw();
		}
		m_vecPos.push_back(pos);
	}
};


int main()
{
	PieceBoard pieceBoard("A", "B");
	pieceBoard.SetPiece(BLACK, PiecePos(4, 4));
	pieceBoard.SetPiece(WHITE, PiecePos(4, 16));
	pieceBoard.SetPiece(BLACK, PiecePos(16, 4));
	pieceBoard.SetPiece(WHITE, PiecePos(16, 16));

	return 0;
}

與其他相關模式

客戶端要引用享元對象,是通過工廠對象創建或者獲得的,客戶端每次引用一個享元對象,都是可以通過同一個工廠對象來引用所需要的享元對象。因此,可以將享元工廠設計成單例模式,這樣就可以保證客戶端只引用一個工廠實例。因爲所有的享元對象都是由一個工廠對象統一管理的,所以在客戶端沒有必要引用多個工廠對象。不管是單純享元模式還是複合享元模式中的享元工廠角色,都可以設計成爲單例模式,對於結果是不會有任何影響的。

       Composite模式:Flyweight模式通常和Composite 模式結合起來,用共享葉結點的有向無環圖實現一個邏輯上的層次結構。複合享元模式實際上是單純享元模式與合成模式的組合。單純享元對象可以作爲樹葉對象來講,是可以共享的,而複合享元對象可以作爲樹枝對象, 因此在複合享元角色中可以添加聚集管理方法。通常,最好用Flyweight實現State 和Strategy 對象。

總結

1)  享元模式是一個考慮系統性能的設計模式,通過使用享元模式可以節約內存空間,提高系統的性能。 
2)  享元模式的核心在於享元工廠類,享元工廠類的作用在於提供一個用於存儲享元對象的享元池,用戶需要對象時,首先從享元池中獲取,如果享 元池中不存在,則創       建一個新的享元對象返回給用戶,並在享元池中保存該新增對象。
3)   享元模式以共享的方式高效地支持大量的細粒度對象,享元對象能做到共享的關鍵是區分內部狀態(Internal State)和外部狀態(External State)。
       (1)   內部狀態是存儲在享元對象內部並且不會隨環境改變而改變的狀態,因此內部狀態可以共享。
       (2)   外部狀態是隨環境改變而改變的、不可以共享的狀態。享元對象的外部狀態必須由客戶端保存,並在享元對象被創建之後,在需要使用的時候 再傳入到享元對               象內部。一個外部狀態與另一個外部狀態之間是相互獨立的。
 

發佈了243 篇原創文章 · 獲贊 37 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章