享元模式

在同类对象十分多的情况下,改变模式,当需要某个对象时,尽量共用已经创建出来的同类对象,从而避免频繁创建,节约内存,提高效率

本文介绍享元模式

简介

模式:享元模式

英文:flyweight

定义:使用共享技术有效地支持大量细粒度的对象的复用,从而到达使用大量相似对象的同样的效果。

命名由来:flyweight是特轻量级,蝇量级的意思

意图:在同类对象十分多的情况下,改变模式,当需要某个对象时,尽量共用已经创建出来的同类对象,从而避免频繁new,节约内存,提高效率。

概念

享元:被共享的单元或者对象

内部状态:存储在享元对象内部的,一直不会发生改变的状态,这种状态可以被共享,一般可以作为享元类的成员变量

外部状态:随着外部环境改变而改变的状态,不可以被共享

总之,将内部状态相同的对象存储在享元池中,传递不同的外部状态。确定内部状态的时候,需要将在多个地方共享的成员变量作为享元对象的内部状态

享元池

享元模式中使用了简单工厂,这个工厂一般用于创建享元对象,并保存在一个容器中,这个容器专门用于保存一个或者多个享元对象,称为享元池

享元池的引入造成了程序的复杂性,需要衡量性价比

享元池和对象池,连接池,线程池都可以看成是对象的复用,但是后三者强调储备量,比如线程池可以规定线程池中线程数最多为多少,但是享元池强调的是创建对象这个过程可以使用享元池中的已有一种对象进行代替

代码结构

一般由以下几个部分组成

  1. 抽象享元类:Piece
  2. 具体享元类:BlackPiece,可以考虑使用单件模式实现
  3. 享元工厂类:pieceFactory,可以考虑使用单件模式实现

代码

使用享元模式前:

#include<iostream>
#include<list>

enum EnumColor {
	Black,White
};
struct Position {
	int m_x;
	int m_y;
	Position(int tmpx, int tmpy) :m_x(tmpx), m_y(tmpy){}
};
class Piece
{
public:
	Piece(EnumColor tmpcolor, Position tmppos) :m_color(tmpcolor), m_pos(tmppos) {

	}
	void draw();
private:
	EnumColor m_color;
	Position m_pos;
};
void Piece::draw()
{
	if (m_color == Black){
		std::cout << "绘制黑棋于" << m_pos.m_x << "," << m_pos.m_y << std::endl;
	}
	else {
		std::cout << "绘制白棋于" << m_pos.m_x << "," << m_pos.m_y << std::endl;
	}
}

int main()
{

	Piece* p_piece1 = new Piece(Black, Position(3, 3));
	p_piece1->draw();

	Piece* p_piece2 = new Piece(White, Position(5, 5));
	p_piece2->draw();

	Piece* p_piece3 = new Piece(Black, Position(4, 6));
	p_piece3->draw();

	std::list <Piece*>piece_list;
	piece_list.push_back(p_piece1);
	piece_list.push_back(p_piece2);
	piece_list.push_back(p_piece3);

	delete p_piece1;
	delete p_piece2;
	delete p_piece3;
	return 0;
}

使用享元模式后:

#include<iostream>
#include<list>
#include<map>
enum EnumColor {
	Black, White
};
struct Position {
	int m_x;
	int m_y;
	Position(int tmpx, int tmpy) :m_x(tmpx), m_y(tmpy) {}
};
class Piece
{
public:
	virtual ~Piece() {};
	virtual void draw(Position tmppos)=0;
};
class BlackPiece:public Piece {
public:
	virtual void draw(Position tmppos) {
		std::cout << "绘制黑棋于" << tmppos.m_x << "," << tmppos.m_y << std::endl;
	}
};
class WhitePiece :public Piece {
public:
	virtual void draw(Position tmppos) {
		std::cout << "绘制白棋于" << tmppos.m_x << "," << tmppos.m_y << std::endl;
	}
};
class pieceFactory {
public:
	~pieceFactory()
	{
		for (auto iter = m_FlyWeightMap.begin(); iter != m_FlyWeightMap.end(); iter++)
		{
			Piece* tmpfw = iter->second;
			delete tmpfw;
		}
		m_FlyWeightMap.clear();
	}
	Piece* getFlyWeight(EnumColor tmpcolor)
	{
		auto iter = m_FlyWeightMap.find(tmpcolor);
		if(iter==m_FlyWeightMap.end())
		{
			Piece* tmpfw = nullptr;
			if (tmpcolor == Black) tmpfw = new BlackPiece();
			else tmpfw = new WhitePiece();
			m_FlyWeightMap.insert(std::make_pair(tmpcolor, tmpfw));
			return tmpfw;
		}
		else 
			return iter->second;
	}
private:
	std::map<EnumColor, Piece*>m_FlyWeightMap;
};
int main()
{
	pieceFactory* pfactory = new pieceFactory();
	Piece* p_piece1 = pfactory->getFlyWeight(Black);
	p_piece1->draw(Position(3, 3));

	Piece* p_piece2 = pfactory->getFlyWeight(Black);
	p_piece2->draw(Position(5, 5));

	Piece* p_piece3 = pfactory->getFlyWeight(Black);
	p_piece3->draw(Position(4, 6));

	Piece* p_piece4 = pfactory->getFlyWeight(Black);
	p_piece4->draw(Position(5, 7));

	delete pfactory;
	return 0;
}

关闭命令行输出时两种代码创建相同数量的类的运行速度:

对象数/ms普通模式享元模式
1000(波动大)400170-356
一万25721800-2200
十万19643-3391115749-16401
百万249130-216128159932
千万2417764-21231811616665-1656742
一亿19677707-2032419315560363

可以看出来性能提升还不小,优化了25%左右

使用场景

  1. 程序中有大量相同或者相似的对象造成内存的大量消耗
  2. 对象的大部分状态可以定义为外部状态作为参数传入

相关模式

  1. 简单工厂模式
  2. 单件模式

© 2023 github-lanyily. All rights reserved.

Powered by Hydejack v9.1.6