【c++设计模式14】结构型6:享元模式(Flyweight Pattern)
- 一、定义
- 二、适用场景
- 三、过程
- 四、享元模式类图
- 五、C++示例代码
- 六、使用注意事项
类型 | 序号 | 设计模式 | 描述 |
结构型 | 1 | 适配器模式 (Adapter Pattern) | 它用于在不修改已有类的情况下,将其接口转换为客户端所期望的接口。 |
2 | 桥接模式 (Bridge Pattern) | 实现了抽象化与实现化的脱耦。他们两个互相独立,不会影响到对方。 | |
3 | 组合模式 (Composite Pattern) | 将对象组合成树状结构来表示“部分-整体”的层次结构。 | |
4 | 装饰模式 (Decorator Pattern) | 它允许你在不改变现有对象结构的情况下,动态地将责任(功能)附加到对象上。 | |
5 | 外观模式 (Facade Pattern) | 对一个子系统的接口.它提供了一个简化的接口,用于访问复杂系统中的一组接口。 | |
6 | 享元模式 (Flyweight Pattern) | 对象的存储开销,它通过共享对象来减少内存使用和提高性能 | |
7 | 代理模式 (Proxy Pattern) | 它提供了一个代理类来控制对于原始对象的访问 |
一、定义
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存使用和提高性能。享元模式将对象分为两部分:内部状态(Intrinsic State)和外部状态(Extrinsic State)。其中,内部状态是可以共享的,而外部状态是不可共享的。
二、适用场景
享元模式适用于以下情况:
- 当应用程序需要创建大量的相似对象,并且这些对象之间存在较多的共享内部状态时,可以使用享元模式来减少内存占用。
- 当需要缓存对象以提高系统性能时,可以使用享元模式来实现对象的重用。
三、过程
享元模式的过程包括以下几个步骤:
- 创建享元工厂类(FlyweightFactory):享元工厂类负责创建和管理享元对象,它维- 护一个享元池(Flyweight Pool),用于缓存已创建的享元对象。
std::vector<Phone*> phones
- 定义享元接口(Flyweight):享元接口定义了享元对象的公共方法,可以是抽象类或接口。这些方法可以接受外部状态作为参数。
- 实现具体享元类(ConcreteFlyweight):具体享元类(Phone)实现了享元接口,并包含内部状态。具体享元类可以被共享和重用。
- 客户端使用享元对象:客户端通过享元工厂类获取或创建享元对象,然后调用享元对象的方法,传入外部状态。
四、享元模式类图
- 享元模式中的享元类可以有子类也可以没有
- 享元模式中可以添加享元工厂也可以不添加
- 享元工厂的作用和单例模式类似,但二者的关注点略有不同
- 单例模式关注的是类的对象有且只有一个
- 享元工厂关注的是某个实例对象是否可以共享
五、C++示例代码
以下是一个使用享元模式的示例代码,在这个示例中,我们将以电子设备制造为例,展示如何使用享元模式来减少内存占用:
- 内部状态:型号是可以共享的
- 外部状态:序列号是不可共享的,不共享的也可以提取成一个类,这个不共享的继承共享的类。
#include <iostream>
#include <unordered_map>// 享元接口:电子设备
class ElectronicDevice {
public:virtual void displayInfo(const std::string& serialNumber) = 0;
};// 具体享元类:手机
class Phone : public ElectronicDevice {
private:std::string model;public:Phone(const std::string& model) : model(model) {}void displayInfo(const std::string& serialNumber) override {std::cout << "Model: " << model << ", Serial Number: " << serialNumber << std::endl;}
};// 享元工厂类:电子设备工厂
class ElectronicDeviceFactory {
private:std::unordered_map<std::string, ElectronicDevice*> flyweights;public:ElectronicDevice* getPhone(const std::string& model) {if (flyweights.find(model) == flyweights.end()) {flyweights[model] = new Phone(model);}return flyweights[model];}
};int main() {ElectronicDeviceFactory deviceFactory;// 内部状态:型号是可以共享的ElectronicDevice* phone1 = deviceFactory.getPhone("iPhone X");ElectronicDevice* phone2 = deviceFactory.getPhone("iPhone X");ElectronicDevice* phone3 = deviceFactory.getPhone("Pixel 5");// 外部状态:序列号是不可共享的,不共享的也可以提取成一个类,这个不共享的继承共享的phone1->displayInfo("123456");phone2->displayInfo("654321");phone3->displayInfo("987654");phone3->displayInfo("123");return 0;
}
输出
在上述示例中,我们定义了一个享元接口 ElectronicDevice,具体享元类 Phone 实现了该接口。Phone 类中包含了内部状态 model,即手机型号。
享元工厂类 ElectronicDeviceFactory 负责创建和管理享元对象。在 getPhone 方法中,我们通过遍历 phones 容器来查找是否已经有该型号的手机。如果有,则直接返回共享对象;如果没有,则创建一个新的手机对象,并将其添加到 phones 容器中。
在 main 函数中,我们通过 ElectronicDeviceFactory 创建了三个电子设备对象,其中两个是相同型号的手机。通过调用 displayInfo 方法,我们可以看到输出的结果中共享了相同型号的手机对象。
六、使用注意事项
享元模式可以减少内存占用,但增加了系统的复杂性。需要权衡共享的程度和复杂性之间的关系,避免过度共享导致维护困难。
外部状态(Extrinsic State)是不可共享的,因此在使用享元模式时,需要将外部状态作为参数传递给享元对象的方法。——或者建立一个不共享的类,来继承共享的类。
注意线程安全性,特别是当享元对象在多个线程中被并发访问时。