微信公众号:幼儿园的学霸
Flyweight Pattern: Use sharing to support large numbers of fine-grained objects efficiently.
使用共享对象可有效地支持大量的细粒度的对象
享元(Flyweight)模式的定义:运用共享技术来有効地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
不得不说,将Flyweight翻译为“享元”的人真是大佬,直指问题核心!
享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。
说到享元模式,第一个想到的应该就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式。
比如我们每次创建字符串对象时,都需要创建一个新的字符串对象的话,内存开销会很大,所以如果第一次创建了字符串对象“adam“,下次再创建相同的字符串”adam“时,只是把它的引用指向”adam“,这样就实现了”adam“字符串再内存中的共享。
需要说明一下的是,虽然可以使用享元模式实现对象池, 但是这两者还是有比较大的差异, 对象池着重在对象的复用上,池中的每个对象是可替换的,从同一个池中获得的A对象和B对象对客户端来说是完全相同的,它主要解决复用,而享元模式主要解决对象的共享问题,如何建立多个可共享的细粒度对象是其关注的重点.
举个最简单的例子,网络联机下棋的时候,一台服务器连接了多个客户端(玩家),如果我们每个棋子都要创建对象,那一盘棋可能就有上百个对象产生,玩家多点的话,因为内存空间有限,一台服务器就难以支持了,所以这里要使用享元模式,将棋子对象减少到几个实例。
在策略模式中会产生大量的策略类,是否能够通过享元模式来避免这种问题呢?
享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。其中:
模式所涉及的角色
非共享的类根据实际情况来定,实际情况也可以没非享元角色
元工厂是享元模式的核心,它需要确保系统可以共享相同的对象。一般情况下,享元工厂会维护一个对象列表,当任何组件尝试获取享元类时,如果请求的享元类已经被创建,则直接返回已有的享元类;若没有,则创建一个新的享元对象,并将它加入到维护队列中。
代码来源于此
问题模型:围棋系统,在一个围棋系统中,有黑子、白子、还有棋盘、装棋子棋盒。其中黑子181个,白子180个,总共361个棋子,在下棋的过程中,每个棋子都会有自己的一个坐标。棋盘总共有384格,如果你为每个实例化一个棋子对象,那么将由384个棋子对象。这里我们可以使用享元模式。接下来分析内部和外部状态。对棋子、棋盘、棋盒,它们都有固定的颜色,这个是不会改变的,所以颜色是内部状态。标准双人模式下,棋盒只有两个,棋盘一个,所以它们没有共享的意义(但是它们相对于界面来说,依然有个显示坐标的问题,所以外部变量对他们依然成立)。对棋子来说,每个对象的坐标就是外部模式,根据坐标的不同,棋子可以显示在棋盘上不同的位置。
#include
//抽象享元基类
class Flyweight {
public:
virtual void display(const int iX, const int iY) = 0;
Flyweight() = default;
virtual ~Flyweight() = default;
protected:
std::string m_strColor;
};
//共享类
class SharedConcreteFlyweightWhite : public Flyweight {
public:
void display(const int iX, const int iY) override {
std::cout << "I am a " << m_strColor
<< " Chess,my coordinate is (" << iX << "," << iY << ")."
<< std::endl;
}
SharedConcreteFlyweightWhite() {
m_strColor = "White";
}
~SharedConcreteFlyweightWhite() = default;
};
class SharedConcreteFlyweightBlack : public Flyweight {
public:
void display(const int iX, const int iY) override {
std::cout << "I am a black Chess,my coordinate is ("
<< iX << "," << iY << ")" << std::endl;
}
SharedConcreteFlyweightBlack() : Flyweight() {
m_strColor = "Black";
}
~SharedConcreteFlyweightBlack() = default;
};
//非享元角色
class UnsharedConcreteFlyweightChessbox : public Flyweight {
public:
void display(const int iX, const int iY) override {
std::cout << "I am a " << m_strColor
<< " chessbox,my coordinate is ("
<< iX << "," << iY << ")" << std::endl;
}
UnsharedConcreteFlyweightChessbox() : Flyweight() {
m_strColor = "Yellow";
}
~UnsharedConcreteFlyweightChessbox() = default;
};
//享元工厂
const std::string BLACKCHESS = "Black";
const std::string WHITECHESS = "White";
class FlyweightFactory {
private:
std::map> m_mapFlyweight;
public:
// strKey is defined class name or surname of class
std::shared_ptr getFlyweight(const std::string strKey) {
//if find return FlyWeight object whose key is equal,otherwise new object and insert into map
if (m_mapFlyweight.end() != m_mapFlyweight.find(strKey))
return m_mapFlyweight[strKey];
if (strKey == BLACKCHESS) {
auto pointer = std::make_shared();
m_mapFlyweight[strKey] = pointer;
return pointer;
} else if (strKey == WHITECHESS) {
auto pointer = std::make_shared();
m_mapFlyweight[strKey] = pointer;
return pointer;
} else {
std::cout << "The key is Error!" << std::endl;
return nullptr;
}
}
size_t getFlyweightCount() {
return m_mapFlyweight.size();
}
};
int main() {
FlyweightFactory objFactory;
auto objBlack = objFactory.getFlyweight(BLACKCHESS);
objBlack->display(3, 5);
auto objBlack1 = objFactory.getFlyweight(BLACKCHESS);
objBlack1->display(1, 4);
std::cout << "Now,total chess count " << objFactory.getFlyweightCount() << std::endl;
auto objWhite = objFactory.getFlyweight(WHITECHESS);
objWhite->display(9, 9);
std::cout << "Now,total chess count " << objFactory.getFlyweightCount() << std::endl;
auto objWhite1 = objFactory.getFlyweight(WHITECHESS);
objWhite1->display(8, 8);
std::cout << "Now,total chess count " << objFactory.getFlyweightCount() << std::endl;
UnsharedConcreteFlyweightChessbox unshChessbox;
unshChessbox.display(1, 2);
std::cout << "Now,total chess count " << objFactory.getFlyweightCount() << std::endl;
return 0;
//运行结果如下:
//I am a black Chess,my coordinate is (3,5)
//I am a black Chess,my coordinate is (1,4)
//Now,total chess count 1
//I am a White Chess,my coordinate is (9,9).
//Now,total chess count 2
//I am a White Chess,my coordinate is (8,8).
//Now,total chess count 2
//I am a Yellow chessbox,my coordinate is (1,2)
//Now,total chess count 2
}
享元模式与单例模式的区别
1.享元设计模式是一个类有很多对象,而单例是一个类仅一个对象。
2.享元模式是为了节约内存空间,提升程序性能,而单例模式则主要是出于共享状态的目的。
享元模式和工厂模式、单例模式
在区分出不同种类的外部状态后,创建新对象时需要选择不同种类的共享对象,这时就可以使用工厂模式来提供共享对象,在共享对象的维护上,经常会采用单例模式来提供单实例的共享对象。
享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
其主要缺点是:
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
读取享元模式的外部状态会使得运行时间稍微变长。
享元模式是通过减少内存中对象的数量来节省内存空间的,所以以下几种情形适合采用享元模式。
1.深入理解享元模式
2.享元模式(详解版)
3.设计模式之享元模式
下面的是我的公众号二维码图片,按需关注。