1、什么是享元模式 举个围棋的例子,围棋的棋盘共有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("鸟鸟","雀雀");
pieceboard.SetPiece(BLACK,PiecePos(0,0));
pieceboard.SetPiece(WHITE,PiecePos(1,1));
pieceboard.SetPiece(BLACK,PiecePos(2,2));
pieceboard.SetPiece(WHITE,PiecePos(3,3));
return 0;
}
2、 可以发现 棋盘的容器中存放了已下的棋子,而每个棋子包含棋子的所有属性,一盘棋往往需要含上百颗棋子,采用上面的这种实现,占用的空间太大了,如何改进呢?用享元模式, 其定义为:运用共享技术有效的支持大量细粒度对象。在围棋中棋子就是大量细粒度对象,其属性有内在的 颜色 ,形状等,也有外在的,比如在棋盘上的位置,内在的属性是可以共享的,区分在于外在的属性 ,因此可以这样设计,只需定义两个棋子对象,一颗黑棋 ,一颗白棋,这两个对象含棋子的内在属性 ,棋子的外在属性即在容器上的位置可以提取出来,放在单独的容器中。相比之前的方案,现在容器中仅仅存放了位置属性 而原来的则是棋子对象,显然,现在的方案大大减少了,对于空间的需求。
关注容器,之前是 vector<Piece*>m_vecPiece ,现在是vector<PiecePos>m_vecPos ,这里是关键。棋子新定义,只包含内在属性。
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<<"绘制一颗黑棋"<<endl;
}
};
class WhitePiece :public Piece
{
public:
WhitePiece(PieceColor color): Piece(color){}
~WhitePiece()
{
}
void Draw()
{
cout<<"绘制一颗白棋"<<endl;
}
};
class PieceBoard
{
private:
vector<PiecePos>m_vecPos;
BlackPiece *m_blackPiece;
WhitePiece *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);
}
};
主函数测试过程一样,不在赘述。 3、优点 享元模式可以避免大量非常相似对象的开销。在程序设计时,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例数据除了几个参数外基本都是相同的,使用享元模式就可以大幅度地减少对象的数量。 4、使用场合 Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它。当以下条件满足时,我们就可以使用享元模式了。 1)一个应用程序使用了大量的对象; 2)完全由于使用大量的对象,造成很大的存储开销; 3)对象的大多数状态都可变为外部状态; 4)如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。 5、总结 使用享元模式可以避免大量相似对象的开销,减小了空间消耗;而空间的消耗是由以下几个因素决定的:实例对象减少的数目;对象内部状态的数目;对象内部状态越多,消耗的空间也会越少;外部状态是计算的还是存储的;由于外部状态可能需要存储,如果外部状态存储起来,那么空间的节省就不会太多。
部分内容参考于:https://www.cnblogs.com/ring1992/p/9593235.html https://blog.csdn.net/wuzhekai1985/article/details/6670298
|