解释说明:享元模式(Flyweight Pattern)运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。
抽象享元类(Flyweight):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
具体享元类(ConcreteFlyweight):实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
非共享具体享元类(UnsharedConcreteFlyweight):并不是所有抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类,当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
享元工厂类(FlyweightFactory):用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
优点:
可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。
享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
缺点:
享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。
适用场景
一个系统有大量相同或者相似的对象,造成内存的大量耗费。
对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
在使用享元模式时,需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源。因此,应当在需要多次重复使用享元对象时才值得使用享元模式。
#pragma once
#include <iostream>
#include <string>
// 玩家 - 有武器和使命
class IPlayer
{
public:
virtual ~IPlayer() {}
// 分配武器
virtual void assignWeapon(std::string weapon) = 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) override {
m_weapon = weapon;
}
virtual void mission() override {
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) override {
m_weapon = weapon;
}
virtual void mission() override {
std::cout << "Counter Terrorist with weapon " + m_weapon + "," + " Task is " + m_task << std::endl;
}
};
#pragma once
#include "flyweight.h"
#include <map>
// 用于获取玩家
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 "flyweight.h"
#include "flyweight_factory.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;
}