享元模式——FlyWeight
阿新 • • 發佈:2019-02-13
軟體領域中的設計模式為開發人員提供了一種使用專家設計經驗的有效途徑。設計模式中運用了面向物件程式語言的重要特性:封裝、繼承、多型,真正領悟設計模式的精髓是可能一個漫長的過程,需要大量實踐經驗的積累。最近看設計模式的書,對於每個模式,用C++寫了個小例子,加深一下理解。主要參考《大話設計模式》和《設計模式:可複用面向物件軟體的基礎》(DP)兩本書。本文介紹享元模式的實現。
舉個圍棋的例子,圍棋的棋盤共有361格,即可放361個棋子。現在要實現一個圍棋程式,該怎麼辦呢?首先要考慮的是棋子棋盤的實現,可以定義一個棋子的類,成員變數包括棋子的顏色、形狀、位置等資訊,另外再定義一個棋盤的類,成員變數中有個容器,用於存放棋子的物件。下面給出程式碼表示:
棋子的定義,當然棋子的屬性除了顏色和位置,還有其他的,這裡略去。這兩個屬性足以說明問題。
客戶的使用方式如下:
可以發現,棋盤的容器中存放了已下的棋子,而每個棋子包含棋子的所有屬性。一盤棋往往需要含上百顆棋子,採用上面這種實現,佔用的空間太大了。如何改進呢?用享元模式。其定義為:運用共享技術有效地支援大量細粒度的物件。
在圍棋中,棋子就是大量細粒度的物件。其屬性有內在的,比如顏色、形狀等,也有外在的,比如在棋盤上的位置。內在的屬性是可以共享的,區分在於外在屬性。因此,可以這樣設計,只需定義兩個棋子的物件,一顆黑棋和一顆白棋,這兩個物件含棋子的內在屬性;棋子的外在屬性,即在棋盤上的位置可以提取出來,存放在單獨的容器中。相比之前的方案,現在容器中僅僅存放了位置屬性,而原來則是棋子物件。顯然,現在的方案大大減少了對於空間的需求。
關注PieceBoard 的容器,之前是vector<Piece*> m_vecPiece,現在是vector<PiecePos> m_vecPos。這裡是關鍵。
棋子的新定義,只包含內在屬性:
舉個圍棋的例子,圍棋的棋盤共有361格,即可放361個棋子。現在要實現一個圍棋程式,該怎麼辦呢?首先要考慮的是棋子棋盤的實現,可以定義一個棋子的類,成員變數包括棋子的顏色、形狀、位置等資訊,另外再定義一個棋盤的類,成員變數中有個容器,用於存放棋子的物件。下面給出程式碼表示:
棋子的定義,當然棋子的屬性除了顏色和位置,還有其他的,這裡略去。這兩個屬性足以說明問題。
//棋子顏色 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; //顏色 PiecePos m_pos; //位置 public: Piece(PieceColor color, PiecePos pos): m_color(color), m_pos(pos) {} ~Piece() {} virtual void Draw() {} }; class BlackPiece: public Piece { public: BlackPiece(PieceColor color, PiecePos pos): Piece(color, pos) {} ~BlackPiece() {} void Draw() { cout<<"繪製一顆黑棋"<<endl;} }; class WhitePiece: public Piece { public: WhitePiece(PieceColor color, PiecePos pos): Piece(color, pos) {} ~WhitePiece() {} void Draw() { cout<<"繪製一顆白棋"<<endl;} }; //棋盤的定義: class PieceBoard { private: vector<Piece*> m_vecPiece; //棋盤上已有的棋子 string m_blackName; //黑方名稱 string m_whiteName; //白方名稱 public: PieceBoard(string black, string white): m_blackName(black), m_whiteName(white){} ~PieceBoard() { Clear(); } void SetPiece(PieceColor color, PiecePos pos) //一步棋,在棋盤上放一顆棋子 { Piece * piece = NULL; if(color == BLACK) //黑方下的 { piece = new BlackPiece(color, pos); //獲取一顆黑棋 cout<<m_blackName<<"在位置("<<pos.x<<','<<pos.y<<")"; piece->Draw(); //在棋盤上繪製出棋子 } else { piece = new WhitePiece(color, pos); cout<<m_whiteName<<"在位置("<<pos.x<<','<<pos.y<<")"; piece->Draw(); } m_vecPiece.push_back(piece); //加入容器中 } void Clear() //釋放記憶體 { int size = m_vecPiece.size(); for(int i = 0; i < size; i++) delete m_vecPiece[i]; } };
客戶的使用方式如下:
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));
}
可以發現,棋盤的容器中存放了已下的棋子,而每個棋子包含棋子的所有屬性。一盤棋往往需要含上百顆棋子,採用上面這種實現,佔用的空間太大了。如何改進呢?用享元模式。其定義為:運用共享技術有效地支援大量細粒度的物件。
在圍棋中,棋子就是大量細粒度的物件。其屬性有內在的,比如顏色、形狀等,也有外在的,比如在棋盤上的位置。內在的屬性是可以共享的,區分在於外在屬性。因此,可以這樣設計,只需定義兩個棋子的物件,一顆黑棋和一顆白棋,這兩個物件含棋子的內在屬性;棋子的外在屬性,即在棋盤上的位置可以提取出來,存放在單獨的容器中。相比之前的方案,現在容器中僅僅存放了位置屬性,而原來則是棋子物件。顯然,現在的方案大大減少了對於空間的需求。
關注PieceBoard 的容器,之前是vector<Piece*> m_vecPiece,現在是vector<PiecePos> m_vecPos。這裡是關鍵。
棋子的新定義,只包含內在屬性:
//棋子顏色 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";} }; 相應棋盤的定義為: [cpp] view plaincopyprint? 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); } };
客戶的使用方式一樣,這裡不重複給出,現在給出享元模式的UML圖,以圍棋為例。棋盤中含兩個共享的物件,黑棋子和白棋子,所有棋子的外在屬性都存放在單獨的容器中。