1.定义
享元模式是一种结构型设计模式, 它允许你在消耗少量内存的情况下支持大量对象。
2.滑滑梯问题
在说明亨元模式之前,我们先看看关于滑滑梯的程序设计。小区的楼下只有三个滑滑梯,但是想玩的小朋友却非常多。怎么设计计滑滑梯资源的管理,避免小朋友使用滑滑梯的时发生冲突呢。(这里我们设定每个滑滑梯每次仅能供一个小朋友玩耍)。解决这个问题,我们就可以使用享元模式(Flyweight),它就是运用共享技术有效的支持大量细粒度的对象。
怎么理解大量细粒度的对象呢?我一开始理解亨元模式的时候其实对这个概念理解的并不清楚。就滑滑梯这个问题,大家认为大量细粒度对象是滑滑梯还是小朋友呢?如果这个问题回答的不好,那么说明你对亨元模式还是不够了解。这里我直接给出答案,大量细粒度的对象指的是滑滑梯。有朋友会很疑惑,滑滑梯我们不是设定有三个么?怎么是大量细粒度对象,哪儿来的大量。其实这个问题逻辑很简单,我们假设有一百个小朋友要玩滑滑梯,那事实上我们只要造一百个滑滑梯就好了,对么。但现实是,我们并没有那么蠢,我们让小朋友排着队轮流玩就好了。三个滑滑梯事实上是果,是采用了亨元模式的果,已经是亨元模式优化过的情况。如果没有亨元模式,那么我们确实要造一百个滑滑梯,每个小朋友专属一个。
3.实现思路
如果不对滑滑梯的资源进行管理,可能会出现这个情况:小朋友们在使用滑滑梯时,并考虑当前滑滑梯是否被使用。很可能两个小朋友同时使用一个滑滑梯,导致冲突,不符合预期。所以我们重点是要对滑滑梯的资源使用进行有效的管理,比如这时候有一个大人来维护滑滑梯的使用秩序。大人作为三个滑滑梯的管理员,小朋友们使用滑滑梯需要向他提出申请,得到准许之后才能玩。经过大人管理之后的滑滑梯使用会变成这个情况:小朋友们排队申请滑滑梯的使用权限,大人根据滑滑梯的现有使用状况,如果还有滑滑梯上没有小朋友玩耍,那么可以提供该滑滑梯给小朋友,如果滑滑梯全部被使用则小朋友申请资源失败,需要继续等待。
我们进一步假设,加入这时候小区其实存在若干个备用的充气滑滑梯,而且小区虽然有一百个小朋友,但他们并不是同时都在楼下玩的。这个时候整个系统就是一个动态的过程。只要有小朋友申请玩滑滑梯,管理员有限在现有的滑滑梯资源里找,如果有空闲的滑滑梯就会提供给当前申请的小朋友,如果没有那么就(新建资源)使用备用的充气滑滑梯。如果连备用的充气滑滑梯都用完了,这就已经触及到系统的资源上限了(内存上限),还是需要排队。
总而言之,恒元模式就是解决需要为一个小朋友定制一个滑滑梯的问题。通过共享资源池来达到相同的目的。
4.组成结构
- 抽象享元:为具体享元角色规定了必须实现的方法,而外部状态就是以参数的形式通过此方法传入。(滑滑梯)
- 具体享元:实现抽象角色规定的方法。如果存在内部状态,就负责为内在部状态提供存储空间。(社区里具体的滑滑梯)
- 享元工厂角色:负责创建和管理享元角色。要想达到共享的目的,这个角色的实现是关键!(管理员)
- 客户端角色:维护对所有享元对象的引用,而且还需要存储对应的外部状态。 (小朋友)
内部状态,就是各个对象不会随着环境的改变而改变的可共享部分;(滑梯本身)
外部状态,指对象随环境改变而改变的不可以共享的部分。内部状态和外部状态彼此互不影响,改变其中一个并不会改变另一个的行为。(滑梯使用者)。
享元模式将享元对象的状态外部化,而读取外部状态可能使得运行时间稍微变长。
5.示例代码
#include <iostream>
#include<string>
#include<map>
using namespace std;
//客户端类
class Kid
{
private:
string m_name;
public:
Kid(string name)
{
m_name = name;
}
std::string GetName()
{
return m_name;
}
};
//抽象亨元类(滑滑梯)
class Slide
{
public:
virtual ~Slide() = default;
virtual void Use(Kid user) = 0;
};
//具体亨元类(小区的滑滑梯)
class ConcreteSlide :public Slide
{
private:
string m_id;
public:
ConcreteSlide(std::string id)
{
m_id = id;
}
void Use(Kid user)override
{
cout << "滑滑梯:" << m_id << " 小朋友:" + user.GetName() << endl;
}
};
//滑滑梯管理员
//如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
class SlideFactory
{
private:
std::map<std::string, Slide*> flyweights;
public:
~SlideFactory()
{
for (auto it = flyweights.begin(); it != flyweights.end(); ++it)
delete it->second;
}
Slide* GetSlideCategory(string key)
{
for (auto it = flyweights.begin(); it != flyweights.end(); ++it)
{
if (it->first == key)
return it->second;
}
Slide* slide = new ConcreteSlide(key);
flyweights.insert(pair<std::string, Slide*>(key, slide));
return slide;
}
int GetSlideCount()
{
return flyweights.size();
}
};
int main()
{
SlideFactory f;
Slide* fx = f.GetSlideCategory("一号滑梯");
fx->Use(Kid("奇奇"));
Slide* fy = f.GetSlideCategory("二号滑梯");
fy->Use(Kid("天天"));
Slide* fz = f.GetSlideCategory("三号滑梯");
fz->Use(Kid("甜甜"));
Slide* fl = f.GetSlideCategory("一号滑梯");
fl->Use(Kid("嘟嘟"));
Slide* fm = f.GetSlideCategory("二号滑梯");
fm->Use(Kid("豆芽"));
Slide* fn = f.GetSlideCategory("三号滑梯");
fn->Use(Kid("年年"));
cout << "得到滑滑梯总数:" << f.GetSlideCount() << endl;
system("pause");
return 0;
}
6.引用
C++设计模式 - 享元模式 - 知乎 (zhihu.com)
C++设计模式——享元模式 - 冰糖葫芦很乖 - 博客园 (cnblogs.com)