享元模式

一,简介

享元模式(Flywight Pattern)主要用于减少对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

二,角色

FlyweightFactory

一个享元工厂,用来创建并管理Flyweight对象。它主要用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已经创建的实例或者创建一个(如果不存在的话)

Flyweight

所有具有享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态

ConcreteFlyweight

继承Flyweight超类或实现Flyweight接口,并为内部状态增加存储空间

UnsharedConcreteFlyweight

那些不需要共享的Flyweight子类,因为Flyweight接口可以共享,也可以不共享。

三,举例

CS 将玩家分为“恐怖份子”(Terrorists)与“反恐精英”(Counter Terrorists)两个阵营,每个队伍必须在地图上进行多回合的战斗。在“爆破模式”中,T 阵营的任务是在指定时间内装置 C4 并引爆,而 CT 阵营的任务是拆除被装置的 C4。当玩家请求武器时,系统会为其分配所需的武器。

现在,有 n 个玩 CS 的玩家,如果创建 n 个对象(每个玩家一个),这势必会占用大量内存。为了解决这个问题,可以使用享元模式(减少玩家数量),只需要为恐怖分子和反恐精英创建两个对象,在需要时反复使用即可。
内部状态和外部状态

享元对象之所以能做到共享,关键是区分了内部状态和外部状态:

    内部状态(Intrinsic State):存储在享元对象内部,并且不会随环境改变而改变。因此,内部状态可以共享。
    这里的“任务”就是两种类型玩家的内部状态,不会随外部环境的变化而变化。无论在任何环境下,T 的任务永远是装置 C4 并引爆,而 CT 的任务永远是拆除 C4。

    外部状态(Extrinsic State):随环境改变而改变的、不可以共享的状态。享元对象的外部状态通常由客户端保存,并在享元对象被创建之后,需要使用时再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。
    比如“武器”,每个玩家都可以携带自己选择的任何武器。有的玩家用的是 AK-47,有的则用的是沙漠之鹰。

由于区分了内部状态和外部状态,因此可以将具有相同内部状态的对象存储在享元池中来实现共享。当需要时,将对象从享元池中取出以实现对象的复用。通过向取出的对象注入不同的外部状态,可以得到一系列相似的对象,而这些对象在内存中实际上只存储一份。
 

#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <map>

//玩家-有武器和使命
class IPlayer{
public:
	virtual ~IPlayer(){}
	//分配武器
	virtual void assignWeapon(std::string ewapon)=0;
	//使命
	virtual void mission()=0;
protected:
    std::string m_task; // 内部状态
    std::string m_weapon; // 外部状态
	
};

// 恐怖分子 
class Terrorist : public IPlayer { 
	public: Terrorist() { m_task = "Plant a bomb"; } 

	virtual void assignWeapon(std::string weapon) { 
		m_weapon = weapon; 
	} 
	virtual void mission() { 
	std::cout << "Terrorist with weapon " + m_weapon + "," + " 				Task is " + m_task << std::endl; 
	} 
}; 

// 反恐精英
 class CounterTerrorist : public IPlayer { 
	public: CounterTerrorist() { m_task = "Diffuse bomb"; }

 	virtual void assignWeapon(std::string weapon){ 
		m_weapon = weapon; 
	} 
	virtual void mission() {
	 std::cout << "Counter Terrorist with weapon " + m_weapon + "," 		+ " Task is " + m_task << std::endl; 
	} 
};

// 用于获取玩家 
class PlayerFactory { public: 
// 如果 T/CT 对象存在,则直接从享元池获取;否则,创建一个新对象并添加到享元池中,然后返回。 
	static IPlayer* getPlayer(std::string type) { 
		IPlayer *p = NULL; 
		if (m_map.find(type) != m_map.end()) 
		{ 
			p = m_map[type]; 
		} else
 		{ 
			// 创建 T/CT 对象 
			if (type == "T") 
			{ 
				std::cout << "Terrorist Created" << std::endl; 
				p = new Terrorist(); 
			} 
			else if (type == "CT")
		        { 
				std::cout << "Counter Terrorist Created" << std::endl; 
				p = new CounterTerrorist(); 
			} 
			else { 
				std::cout << "Unreachable code!" << std::endl; 
			} 
			// 一旦创建,将其插入到 map 中
 			m_map.insert(std::make_pair(type, p)); 
		} 
		return p;
	} 
private: 
	// 存储 T/CT 对象(享元池) 
	static std::map<std::string, IPlayer*> m_map; 
};


#include "example.h"
#include <ctime>

std::map<std::string, IPlayer*> PlayerFactory::m_map = std::map<std::string, IPlayer*>();
// 玩家类型和武器 
static std::string s_playerType[2] = { "T", "CT" };
static std::string s_weapons[4] = { "AK-47", "Maverick", "Gut Knife", "Desert Eagle" };
#define GET_ARRAY_LEN(array, len) {len = (sizeof(array) / sizeof(array[0]));}
int main() 
{
 	srand((unsigned)time(NULL)); 
	int playerLen; 
	int weaponsLen; 
	GET_ARRAY_LEN(s_playerType, playerLen); 
	GET_ARRAY_LEN(s_weapons, weaponsLen); 
	// 假设,游戏中有十位玩家 
	for (int i = 0; i < 10; i++) { 
		// 获取随机玩家和武器 
		int typeIndex = rand() % playerLen; 
		int weaponIndex = rand() % weaponsLen;
 		std::string type = s_playerType[typeIndex]; 
		std::string weapon = s_weapons[weaponIndex]; 
		// 获取玩家 
		IPlayer *p = PlayerFactory::getPlayer(type); 
		// 从武器库中随机分配武器
 		p->assignWeapon(weapon); 
		// 派玩家去执行任务 
		p->mission(); 
	} 
	getchar();
 	return 0; 
}

xl@xl-X250:~/桌面$ g++ -o client example.h main.cpp
xl@xl-X250:~/桌面$ ls
client  example.h  example.h~  main.cpp  main.cpp~
xl@xl-X250:~/桌面$ ./client
Terrorist Created
Terrorist with weapon AK-47,                 Task is Plant a bomb
Terrorist with weapon AK-47,                 Task is Plant a bomb
Counter Terrorist Created
Counter Terrorist with weapon Gut Knife, Task is Diffuse bomb
Terrorist with weapon Maverick,                 Task is Plant a bomb
Counter Terrorist with weapon AK-47, Task is Diffuse bomb
Terrorist with weapon Desert Eagle,                 Task is Plant a bomb
Terrorist with weapon Gut Knife,                 Task is Plant a bomb
Terrorist with weapon Maverick,                 Task is Plant a bomb
Counter Terrorist with weapon Desert Eagle, Task is Diffuse bomb
Terrorist with weapon Desert Eagle,                 Task is Plant a bomb

 

四,优缺点

优点:

    可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。
    享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

缺点:

    享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
    为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。

五,使用场景

  • 一个系统有大量相同或者相似的对象,造成内存的大量耗费。
  • 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
  • 在使用享元模式时,需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源。因此,应当在需要多次重复使用享元对象时才值得使用享元模式。

 

参考:https://blog.csdn.net/liang19890820/article/details/79629690

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土拨鼠不是老鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值