目录
1.概述
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,通过使用共享对象来支持大量的细粒度对象,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。在享元模式中,有些对象可以被多个客户端共享,以减少创建对象的数量。享元模式的核心在于享元工厂类,它负责创建和管理享元对象,并提供对外访问的接口。
2.结构
享元模式的结构图如下所示:
具体角色定义:
抽象享元(Flyweight): 定义了具体享元和非共享享元的接口,通常包含了设置外部状态的方法。
具体享元(Concrete Flyweight) :实现了抽象享元接口,包含了内部状态和外部状态。内部状态是可以被共享的,而外部状态则由客户端传递。上面的具体享元类ConcreteFlyweight是的内部状态是可以共享的,UnsharedConcreteFlyweight是指那些不需要共享的Flyweight子类,因为Flyweight接口共享成为可能,但是并不强制共享。
享元工厂(Flyweight Factory) :负责创建和管理享元对象,通常包含一个池(缓存)用于存储和复用已经创建的享元对象。工厂类内部维护一个享元对象的集合(通常是一个哈希表或字典),用于存储已经创建的享元对象。提供获取享元对象的方法,当请求某个享元对象时,工厂类首先检查集合中是否存在该对象,如果存在则直接返回,否则创建新的享元对象并添加到集合中。
客户端(Client) : 使用享元工厂获取享元对象,并通过设置外部状态来操作享元对象。客户端通常不需要关心享元对象的具体实现。
3.实现
简单实现代码如下:
cpp
#include <iostream>
#include <unordered_map>
#include <string>
// 享元接口
class Flyweight {
public:
virtual ~Flyweight() {}
virtual void operation(const std::string& extrinsicState) = 0;
};
// 具体的享元类
class ConcreteFlyweight : public Flyweight {
private:
std::string intrinsicState; // 内部状态
public:
ConcreteFlyweight(const std::string& state) : intrinsicState(state) {}
void operation(const std::string& extrinsicState) override {
std::cout << "Intrinsic: " << intrinsicState << ", Extrinsic: " << extrinsicState << std::endl;
}
};
// 享元工厂类
class FlyweightFactory {
public:
~FlyweightFactory(){
for (auto& it : m_flyweights){
delete it.second;
}
m_flyweights.clear();
}
public:
Flyweight* getFlyweight(const std::string& key) {
if (m_flyweights.find(key) == m_flyweights.end()) {
m_flyweights[key] = new ConcreteFlyweight(key);
}
return m_flyweights[key];
}
private:
std::unordered_map<std::string, Flyweight*> m_flyweights;
};
int main() {
FlyweightFactory factory;
Flyweight* fw1 = factory.getFlyweight("A");
Flyweight* fw2 = factory.getFlyweight("B");
Flyweight* fw3 = factory.getFlyweight("A"); // 复用 fw1
fw1->operation("X");
fw2->operation("Y");
fw3->operation("Z"); // 使用与 fw1 相同的对象
return 0;
}
在这个示例中,Flyweight
是一个接口,定义了享元对象应该具有的操作。ConcreteFlyweight
是具体的享元类,它包含内部状态(intrinsicState
)并实现了操作。FlyweightFactory
是一个工厂类,它负责创建和管理享元对象。通过使用 FlyweightFactory
,我们可以确保对于相同的内部状态,只创建一个 ConcreteFlyweight
对象,并在后续请求时复用它。
4.应用场景
在 C++ 中,享元模式的应用场景主要出现在需要处理大量相似或重复对象,且这些对象的内存占用较大时。通过共享这些对象的状态,享元模式能够显著减少内存消耗,并提高系统的性能。
具体来说,以下是一些 C++ 中享元模式的应用场景:
图形界面开发:在图形用户界面中,可能需要大量的按钮、图标或其他 UI 元素。这些元素通常具有相似的外观和行为,但数量众多。通过应用享元模式,可以共享这些元素的内部状态,减少内存占用。
字符串处理:在 C++ 中,字符串的创建和销毁是一个常见的性能瓶颈。使用享元模式,可以设计一个字符串缓存池,对于相同的字符串,只在缓存池中保留一份实例,多次使用时直接引用该实例,避免了重复的创建和销毁操作。
数据库连接池:在数据库应用中,频繁地创建和关闭数据库连接会消耗大量的系统资源。通过使用享元模式实现连接池,可以复用已建立的数据库连接,提高系统性能和稳定性。
游戏开发:在游戏中,经常需要创建大量的相似对象,如棋子、怪物、子弹等。这些对象可能具有相同的属性或行为,但由于数量众多,如果每个对象都单独创建,将会占用大量的内存。使用享元模式,可以将这些对象的共享部分提取出来,只保留一份实例,从而大大减少内存消耗。
总的来说,享元模式在 C++ 中的应用场景主要是那些需要处理大量相似或重复对象,且内存消耗成为性能瓶颈的情况。通过共享对象的状态,享元模式能够优化内存使用,提高系统的整体性能。
5.总结
优点:
(1) 减少内存占用:享元模式通过共享相同或相似的对象,避免了大量相同对象的重复创建,从而显著降低了系统的内存占用。
(2) 提高系统性能:由于减少了对象的创建和销毁,享元模式可以提高系统的运行效率。特别是在需要大量创建对象的场景中,这种性能提升尤为明显。
(3) 支持高并发:在高并发的系统中,对象的创建和销毁可能会成为性能瓶颈。享元模式通过共享对象实例,减少了对象的创建和销毁,从而支持更高的并发量。
缺点:
(1) 提高了系统的复杂度:享元模式需要分离出对象的内部状态和外部状态,这增加了系统的复杂性和编程难度。同时,为了管理这些状态,可能需要引入额外的数据结构(如哈希表等),进一步增加了系统的复杂性。
(2) 可能导致系统混乱:如果外部状态具有固有化的性质,不应该随着内部状态的变化而变化,但在实际使用中违反了这一原则,就可能导致系统混乱。因此,在使用享元模式时,需要特别注意外部状态的管理。
(3) 不适合所有场景:虽然享元模式在某些场景下可以提高系统性能和减少内存占用,但并不是所有场景都适合使用享元模式。例如,当对象的状态经常变化或者对象之间的差别较大时,使用享元模式可能并不合适。
综上所述,享元模式在减少内存占用和提高系统性能方面具有明显的优势,但同时也存在提高系统复杂度和可能导致系统混乱的缺点。因此,在决定是否使用享元模式时,需要根据具体的应用场景和需求进行权衡。