舉個圍棋的例子,圍棋的棋盤共有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";}
- };
相應棋盤的定義爲:
- 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圖,以圍棋爲例。棋盤中含兩個共享的對象,黑棋子和白棋子,所有棋子的外在屬性都存放在單獨的容器中。