C++常见设计模式之享元模式

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) //一颗黑棋也没有 需要new一颗黑棋 以后的黑棋除了位置 跟这颗黑棋没有差别 不需要在new 直接返回这颗黑棋对象即可
				m_blackPiece = new BlackPiece(color);

			cout<<m_blackName<<"在位置("<<pos.x<<','<<pos.y<<")";
			m_blackPiece->Draw(); //在棋盘上绘制出黑棋
		}
		else
		{
    
    
			if(m_whitePiece == NULL) //一颗白棋也没有 需要new一颗白棋
				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

おすすめ

転載: blog.csdn.net/scarificed/article/details/121319423