c++ 享元模式(flyweight)
举个围棋的例子,围棋的棋盘共有361格,即可放361个棋子。现在要实现一个围棋程 序,该怎么办呢?首先要考虑的是棋子棋盘的实现,可以定义一个棋子的类,成员变量包括棋子的颜色、形状、位置等信息,另外再定义一个棋盘的类,成员变量中 有个容器,用于存放棋子的对象。下面给出代码表示:
棋子的定义,当然棋子的属性除了颜色和位置,还有其他的,这里略去。这两个属性足以说明问题。
#include <iostream> #include <vector> #include <string> enum PieceColor {BLACK,WHITE}; using namespace std; class Pos { public: Pos(int x,int y):m_x(x),m_y(y) { } int getX() { return m_x; } int getY() { return m_y; } private: int m_x; int m_y; }; class Piece { public: Piece(PieceColor color,Pos pos):m_color(color),m_pos(pos){}; ~Piece() {} virtual void Draw() {} protected: PieceColor m_color; Pos m_pos; }; class WhitePiece : public Piece { public: WhitePiece(PieceColor color, Pos pos):Piece(color,pos){} ~WhitePiece(); virtual void Draw() { cout << "draw a white piece" << endl; } }; class BlackPiece:public Piece { public: BlackPiece(PieceColor color,Pos pos):Piece(color,pos){} ~BlackPiece(); virtual void Draw(){ cout << "draw a black piece" <<endl; } }; class PieceBoard { public: PieceBoard(string black,string white):m_blackName(black),m_whiteName(white) { } ~PieceBoard() { Clear(); } void SetPiece(PieceColor color,Pos pos) { Piece * piece = NULL; if(color == BLACK) { piece = new BlackPiece(color,pos); std::cout << m_blackName << "在位置(" << pos.getX() << ","<<pos.getY() <<")" <<endl ; piece->Draw(); m_pieceArray.push_back(piece); } else { piece = new WhitePiece(color,pos); std::cout << m_whiteName << "在位置(" << pos.getX() << ","<<pos.getY() <<")" <<endl; piece->Draw(); m_pieceArray.push_back(piece); } } void Clear() { int size = m_pieceArray.size(); for(int i = 0; i < size ; i++) delete m_pieceArray[i]; } private: std::vector<Piece*> m_pieceArray; std::string m_blackName; std::string m_whiteName; };
主函数:
#include "flyweight.h" int main() { PieceBoard pieceBoard("A","B"); pieceBoard.SetPiece(BLACK,Pos(4,4)); pieceBoard.SetPiece(WHITE,Pos(16,6)); system("pause"); return 0; }
可以发现,棋盘的容器中存放了已下的棋子,而每个棋子包含棋子的所有属性。一盘棋往往需要含上百颗棋子,采用上面这种实现,占用的空间太大了。如何改进呢?用享元模式。其定义为:运用共享技术有效地支持大量细粒度的对象。
在围棋中,棋子就是大量细粒度的对象。其属性有内在的,比如颜色、形 状等,也有外在的,比如在棋盘上的位置。内在的属性是可以共享的,区分在于外在属性。因此,可以这样设计,只需定义两个棋子的对象,一颗黑棋和一颗白棋, 这两个对象含棋子的内在属性;棋子的外在属性,即在棋盘上的位置可以提取出来,存放在单独的容器中。相比之前的方案,现在容器中仅仅存放了位置属性,而原 来则是棋子对象。显然,现在的方案大大减少了对于空间的需求。
关注PieceBoard 的容器,之前是vector<Piece*> m_vecPiece,现在是vector<PiecePos> m_vecPos。这里是关键。
棋子的新定义,只包含内在属性:
#include <iostream> #include <vector> #include <string> enum PieceColor {BLACK,WHITE}; using namespace std; class Pos { public: Pos(int x,int y):m_x(x),m_y(y) { } int getX() { return m_x; } int getY() { return m_y; } private: int m_x; int m_y; }; class Piece { public: Piece(PieceColor color):m_color(color){}; ~Piece() {} virtual void Draw() {} protected: PieceColor m_color; }; class WhitePiece : public Piece { public: WhitePiece(PieceColor color):Piece(color){} ~WhitePiece(); virtual void Draw() { cout << "draw a white piece" << endl; } }; class BlackPiece:public Piece { public: BlackPiece(PieceColor color):Piece(color){} ~BlackPiece(); virtual void Draw(){ cout << "draw a black piece" <<endl; } }; class PieceBoard { public: PieceBoard(string black,string white):m_blackName(black),m_whiteName(white) { } ~PieceBoard() { Clear(); } void SetPiece(PieceColor color,Pos pos) { Piece * piece = NULL; if(color == BLACK) { piece = new BlackPiece(color); std::cout << m_blackName << "在位置(" << pos.getX() << ","<<pos.getY() <<")" <<endl ; piece->Draw(); m_blackPosArray.push_back(pos); } else { piece = new WhitePiece(color); std::cout << m_whiteName << "在位置(" << pos.getX() << ","<<pos.getY() <<")" <<endl; piece->Draw(); m_whitePosArray.push_back(pos); } } void Clear() { //int size = m_pieceArray.size(); //for(int i = 0; i < size ; i++) // delete m_pieceArray[i]; } private: std::vector<Pos> m_whitePosArray; std::vector<Pos> m_blackPosArray; std::string m_blackName; std::string m_whiteName; };
主函数:
#include "flyweight.h" int main() { PieceBoard pieceBoard("A","B"); pieceBoard.SetPiece(BLACK,Pos(4,4)); pieceBoard.SetPiece(WHITE,Pos(16,6)); system("pause"); return 0; }