C++设计模式之享元模式
本文导语: 前言 无聊的时候,也去QQ游戏大厅玩五子棋或者象棋;作为程序员,看到一个产品,总要去想想它是怎么设计的,怎么完成的,我想这个是所有程序员都会做的事情吧(强迫症???)。有的时候,想完了,还要做一个DEMO出来...
前言
无聊的时候,也去QQ游戏大厅玩五子棋或者象棋;作为程序员,看到一个产品,总要去想想它是怎么设计的,怎么完成的,我想这个是所有程序员都会做的事情吧(强迫症???)。有的时候,想完了,还要做一个DEMO出来,才能体现自己的NB,然后还有点小成就感。
在玩五子棋或象棋的时候,我就想过,腾讯那帮伙计是怎么做的呢?五子棋的棋子有黑白两色,难道每次放一个棋子就new一个对象么?象棋有车、马、相、士、帅、炮和兵,是不是每盘棋都要把所有的棋子都new出来呢?如果真的是每一个棋子都new一个,那么再加上那么多人玩;那要new多少对象啊,如果是这样做的话,我想有多少服务器都是搞不定的,可能QQ游戏大厅会比12306还糟糕。那腾讯那帮伙计是如何实现的呢?那就要说到今天总结的享元模式了。
什么是享元模式?
在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对象,这可以保证对它们适当地进行共享;由于共享一个实例,所以在创建这个实例时,就可以考虑使用单例模式来进行实现。
享元模式的工厂类维护了一个实例列表,这个列表中保存了所有的共享实例;当用户从享元模式的工厂类请求共享对象时,首先查询这个实例表,如果不存在对应实例,则创建一个;如果存在,则直接返回对应的实例。
代码实现:
#include
#include
#include
using namespace std;
typedef struct pointTag
{
int x;
int y;
pointTag(){}
pointTag(int a, int b)
{
x = a;
y = b;
}
bool operator GetColor() == color)
{
bFound = true;
pPiece = *it;
break;
}
bFound = false;
}
if (!bFound)
{
pPiece = new CGomoku(color);
m_vecPiece.push_back(pPiece);
}
}
return pPiece;
}
~CPieceFactory()
{
for (vector::iterator it = m_vecPiece.begin(); it != m_vecPiece.end(); ++it)
{
if (*it != NULL)
{
delete *it;
*it = NULL;
}
}
}
private:
vector m_vecPiece;
};
class CChessboard
{
public:
void Draw(CPiece *piece)
{
if (piece->GetColor())
{
cout