突破编程_C++_设计模式(享元模式)

1 享元模式的基本概念

享元模式(Flyweight Pattern)是一种主要用于减少创建对象的数量,以减少内存占用和提高性能的设计模式。它通过使用共享对象来支持大量的细粒度对象,从而减少了内存占用。在享元模式中,有些对象可以被多个客户端共享,以减少创建对象的数量。享元模式的核心在于享元工厂类,它负责创建和管理享元对象,并提供对外访问的接口。

享元模式主要适用于以下情况:

  • 系统中存在大量的相似对象,这些对象消耗了大量的内存资源。
  • 大部分的对象可以按照内在状态进行分组,并且可将分组对象中的一部分外部状态存储在享元对象中,而将其余的部分外部状态在需要时由外部传入。
  • 系统不依赖于这些对象身份,这些对象是不可分辨的。

在 C++ 中实现享元模式,需要设计以下部分:

  • 抽象享元类(Flyweight):定义出对象的外部状态接口,让其子类可以接收外部状态并影响其行为。
  • 具体享元类(ConcreteFlyweight):实现抽象享元类定义的方法,并为其内部状态增加存储空间。
  • 享元工厂类(FlyweightFactory):负责创建和管理享元对象,它提供一个用于存储享元对象的享元池,在用户请求一个享元对象时,享元工厂首先检查享元池中是否存在符合要求的享元对象,如果存在则直接返回,否则创建一个新的享元对象并添加到享元池中。

2 享元模式的实现步骤

享元模式的实现步骤如下:

(1)定义抽象享元类(Flyweight):
创建一个抽象基类,定义享元对象的接口。
这个接口应该包括操作享元对象内部状态的方法,以及可能设置或获取外部状态的方法。
抽象享元类通常不包含与具体实现相关的数据成员,它仅定义了一个通用的接口。

(2)实现具体享元类(ConcreteFlyweight):
继承自抽象享元类,并实现其接口。
具体享元类包含享元对象的内部状态,这些内部状态在多个享元实例之间是共享的。
实现具体的业务逻辑,这些逻辑可能会依赖于外部状态。

(3)创建享元工厂类(FlyweightFactory):
设计一个工厂类,用于创建和管理享元对象。
工厂类内部维护一个享元对象的集合(通常是一个哈希表或字典),用于存储已经创建的享元对象。
提供获取享元对象的方法,当请求某个享元对象时,工厂类首先检查集合中是否存在该对象,如果存在则直接返回,否则创建新的享元对象并添加到集合中。

(4)定义外部状态:
享元模式的关键在于将对象的状态分为内部状态和外部状态。
内部状态是存储在享元对象内部,并在多个对象中共享的状态。
外部状态是依赖于具体上下文的状态,通常在运行时通过参数传递给享元对象的方法。

(5)客户端代码使用享元对象:
客户端代码通过享元工厂获取享元对象,而不是直接创建享元对象。
在使用享元对象时,客户端需要传递必要的外部状态给享元对象的方法。
客户端不应直接访问享元对象的内部状态,而是应该通过享元对象提供的接口进行操作。

(1)优化与扩展:
根据需要,可以对享元工厂进行扩展,比如支持配置不同的享元对象池大小、缓存策略等。
对于复杂的系统,可能需要设计多个抽象享元类和具体享元类,以适应不同的业务场景。

如下为样例代码:

#include <iostream>  
#include <unordered_map>  
#include <memory>  
#include <string>  // 抽象享元类  
class Flyweight {
public:virtual ~Flyweight() = default;virtual void operation(const std::string& extrinsicState) = 0;
};// 具体享元类  
class ConcreteFlyweight : public Flyweight {
public:ConcreteFlyweight(const std::string& state) : intrinsicState(state) {}void operation(const std::string& extrinsicState) override {std::cout << "Intrinsic: " << intrinsicState << ", Extrinsic: " << extrinsicState << std::endl;}private:std::string intrinsicState; // 内部状态  
};// 享元工厂类  
class FlyweightFactory {
public:std::shared_ptr<Flyweight> getFlyweight(const std::string& key) {if (flyweights.find(key) == flyweights.end()) {flyweights[key] = std::make_shared<ConcreteFlyweight>(key);}return flyweights[key];}private:std::unordered_map<std::string, std::shared_ptr<Flyweight>> flyweights;
};// 客户端代码  
int main() 
{FlyweightFactory factory;// 获取享元对象并操作  std::shared_ptr<Flyweight> fw1 = factory.getFlyweight("A");fw1->operation("X"); // 输出: Intrinsic: A, Extrinsic: X  std::shared_ptr<Flyweight> fw2 = factory.getFlyweight("A");fw2->operation("Y"); // 输出: Intrinsic: A, Extrinsic: Y  // 注意:fw1 和 fw2 指向同一个享元对象  std::shared_ptr<Flyweight> fw3 = factory.getFlyweight("B");fw3->operation("Z"); // 输出: Intrinsic: B, Extrinsic: Z  return 0;
}

上面代码的输出为:

Intrinsic: A, Extrinsic: X
Intrinsic: A, Extrinsic: Y
Intrinsic: B, Extrinsic: Z

在这个示例中,Flyweight 是一个抽象基类,定义了一个操作享元对象的方法 operation。ConcreteFlyweight 是具体的享元类,它继承了 Flyweight 并实现了 operation 方法。它有一个内部状态 intrinsicState,该状态在多个享元实例之间是共享的。

FlyweightFactory 是享元工厂类,它使用 std::unordered_map 存储了享元对象的 std::shared_ptr。getFlyweight 方法负责获取或创建享元对象,并返回其 std::shared_ptr。

在 main 函数中,客户端代码通过 FlyweightFactory 获取享元对象,并调用其 operation 方法。由于使用了 std::shared_ptr,享元对象的生命周期由智能指针管理,当没有引用指向享元对象时,它会被自动删除。

通过这种方式,即使多次调用 getFlyweight 请求同一个享元对象,也只会创建一个 ConcreteFlyweight 实例,并且其生命周期由 std::shared_ptr 智能管理,避免了不必要的内存分配和释放。

3 享元模式的应用场景

在 C++ 中,享元模式的应用场景主要出现在需要处理大量相似或重复对象,且这些对象的内存占用较大时。通过共享这些对象的状态,享元模式能够显著减少内存消耗,并提高系统的性能。

具体来说,以下是一些 C++ 中享元模式的应用场景:

图形界面开发:在图形用户界面中,可能需要大量的按钮、图标或其他 UI 元素。这些元素通常具有相似的外观和行为,但数量众多。通过应用享元模式,可以共享这些元素的内部状态,减少内存占用。

字符串处理:在 C++ 中,字符串的创建和销毁是一个常见的性能瓶颈。使用享元模式,可以设计一个字符串缓存池,对于相同的字符串,只在缓存池中保留一份实例,多次使用时直接引用该实例,避免了重复的创建和销毁操作。

数据库连接池:在数据库应用中,频繁地创建和关闭数据库连接会消耗大量的系统资源。通过使用享元模式实现连接池,可以复用已建立的数据库连接,提高系统性能和稳定性。
总的来说,享元模式在 C++ 中的应用场景主要是那些需要处理大量相似或重复对象,且内存消耗成为性能瓶颈的情况。通过共享对象的状态,享元模式能够优化内存使用,提高系统的整体性能。

游戏开发:在游戏中,经常需要创建大量的相似对象,如棋子、怪物、子弹等。这些对象可能具有相同的属性或行为,但由于数量众多,如果每个对象都单独创建,将会占用大量的内存。使用享元模式,可以将这些对象的共享部分提取出来,只保留一份实例,从而大大减少内存消耗。

3.1 享元模式应用于图形界面开发

在图形界面开发中,享元模式可以用于减少界面元素的内存占用,特别是当界面包含大量相似或重复的元素时。以下是一个简单的示例,展示了如何在图形界面开发中使用享元模式,并利用智能指针来管理对象生命周期。

#include <iostream>  
#include <memory>  
#include <unordered_map>  
#include <string>  // 抽象享元类  
class GUIElementFlyweight {
public:virtual ~GUIElementFlyweight() = default;virtual void draw() const = 0;
};// 具体享元类  
class ButtonFlyweight : public GUIElementFlyweight {
public:ButtonFlyweight(const std::string& label) : label(label) {}void draw() const override {std::cout << "Drawing button with label: " << label << std::endl;}private:std::string label;// 这里可以添加更多的共享状态  
};// 享元工厂类  
class GUIElementFlyweightFactory {
public:std::shared_ptr<GUIElementFlyweight> getFlyweight(const std::string& label) {if (flyweights.find(label) == flyweights.end()) {flyweights[label] = std::make_shared<ButtonFlyweight>(label);}return flyweights[label];}private:std::unordered_map<std::string, std::shared_ptr<GUIElementFlyweight>> flyweights;
};// 客户端代码  
int main() 
{GUIElementFlyweightFactory factory;// 获取并绘制按钮  std::shared_ptr<GUIElementFlyweight> button1 = factory.getFlyweight("Save");button1->draw(); // 输出: Drawing button with label: Save  std::shared_ptr<GUIElementFlyweight> button2 = factory.getFlyweight("Save");button2->draw(); // 输出: Drawing button with label: Save  // 注意:button1 和 button2 指向同一个享元对象  std::shared_ptr<GUIElementFlyweight> button3 = factory.getFlyweight("Cancel");button3->draw(); // 输出: Drawing button with label: Cancel  return 0;
}

上面代码的输出为:

Drawing button with label: Save
Drawing button with label: Save
Drawing button with label: Cancel

在上面代码中:

  • GUIElementFlyweight 是抽象享元类,定义了图形界面元素应该有的行为(在这里是 draw 方法)。
    ButtonFlyweight 是具体享元类,继承自 GUIElementFlyweight,代表一个具体的按钮元素,并实现了 draw 方法。
  • GUIElementFlyweightFactory 是享元工厂类,它维护了一个哈希表来存储已创建的享元对象。当客户端请求一个享元对象时,工厂首先检查哈希表中是否存在具有相同标签的享元对象,如果存在则直接返回,否则创建一个新的享元对象并添加到哈希表中。
  • 在 main 函数中,创建了享元工厂的一个实例,并通过它获取了几个按钮享元对象。这些对象被存储在 std::shared_ptr 智能指针中,当没有引用指向它们时,它们会被自动删除。

通过这个示例,可以看到享元模式如何帮助我们复用相似的图形界面元素,减少了内存的消耗。在实际应用中,draw 方法可能会涉及更复杂的渲染逻辑,而享元对象也可能包含更多的共享状态。但基本的原理是一样的:通过共享对象的状态来减少内存占用。

3.2 享元模式应用于字符串处理

在字符串处理中,享元模式可以应用于缓存和复用频繁使用的字符串对象,以减少内存分配和释放的开销。下面是一个简单的示例,展示了如何使用享元模式来处理字符串,并使用智能指针来管理字符串对象的生命周期。

#include <iostream>  
#include <memory>  
#include <unordered_map>  
#include <string>  // 抽象享元类  
class StringFlyweight {
public:virtual ~StringFlyweight() = default;virtual void display() const = 0;// 可能还有其他的方法或属性  
};// 具体享元类  
class StringFlyweightImpl : public StringFlyweight {
public:StringFlyweightImpl(const std::string& value) : value(value) {}void display() const override {std::cout << "String: " << value << std::endl;}// 可能还有其他方法,比如获取字符串值等  private:std::string value;
};// 享元工厂类  
class StringFlyweightFactory {
public:std::shared_ptr<StringFlyweight> getFlyweight(const std::string& value) {auto it = flyweights.find(value);if (it == flyweights.end()) {// 创建一个新的享元对象并添加到缓存中  flyweights[value] = std::make_shared<StringFlyweightImpl>(value);it = flyweights.find(value); // 更新迭代器位置  }return it->second;}private:std::unordered_map<std::string, std::shared_ptr<StringFlyweight>> flyweights;
};// 客户端代码  
int main() 
{StringFlyweightFactory factory;// 获取并显示字符串享元对象  std::shared_ptr<StringFlyweight> str1 = factory.getFlyweight("Hello");str1->display(); // 输出: String: Hello  std::shared_ptr<StringFlyweight> str2 = factory.getFlyweight("Hello");str2->display(); // 输出: String: Hello  // 注意:str1 和 str2 指向同一个享元对象  std::shared_ptr<StringFlyweight> str3 = factory.getFlyweight("World");str3->display(); // 输出: String: World  return 0;
}

上面代码的输出为:

String: Hello
String: Hello
String: World

在上面代码中:

  • StringFlyweight 是抽象享元类,定义了字符串对象应该有的行为(在这里是 display 方法)。
  • StringFlyweightImpl 是具体享元类,继承自 StringFlyweight,并实现了 display 方法来显示字符串的内容。
  • StringFlyweightFactory 是享元工厂类,它维护了一个哈希表来存储已创建的字符串享元对象。当客户端请求一个字符串享元对象时,工厂首先检查哈希表中是否存在具有相同值的字符串享元对象,如果存在则直接返回,否则创建一个新的享元对象并添加到哈希表中。
  • 在 main 函数中,我们创建了享元工厂的一个实例,并通过它获取了几个字符串享元对象。这些对象被存储在 std::shared_ptr 智能指针中,当没有引用指向它们时,它们会被自动删除。

通过这个示例,可以看到享元模式如何帮助我们复用频繁使用的字符串对象,减少了内存分配和释放的开销。

4 享元模式的优点与缺点

C++ 享元模式的优点主要包括:

(1)减少内存使用: 这是享元模式最显著的优点。通过共享对象的状态,可以减少系统中相似对象的数量,从而显著减少内存占用。这在处理大量相似对象时尤其有效,如界面上的大量相似按钮或大量重复使用的字符串等。

(2)提高性能: 由于减少了对象的创建和销毁次数,享元模式可以提高系统的性能。频繁地创建和销毁对象会带来一定的性能开销,而享元模式通过共享对象避免了这种开销。

(3)易于管理: 享元模式通常与工厂模式结合使用,通过工厂来创建和管理享元对象。这使得对象的创建和使用更加统一和集中,便于管理和维护。

然而,C++ 享元模式也存在一些缺点:

(1)增加了系统复杂性: 引入享元模式会增加系统的复杂性。需要设计额外的享元工厂类来管理享元对象,还需要处理享元对象的状态共享问题。这可能会增加代码量和开发难度。

(1)不适合所有场景: 享元模式适用于系统中存在大量相似对象且这些对象的状态可以共享的情况。如果系统中对象差异较大或状态不能共享,那么使用享元模式可能并不合适。

(1)可能引入额外的开销: 虽然享元模式可以减少内存使用和提高性能,但在某些情况下可能会引入额外的开销。例如,当需要频繁地更新享元对象的状态时,由于状态是共享的,所以更新操作可能会变得复杂和耗时。

(1)可能破坏封装性: 由于享元对象的状态是共享的,因此需要谨慎处理状态的访问和修改。这可能会破坏对象的封装性,使得代码更难以理解和维护。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/739256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【io.net】问题汇总

【io.net】问题汇总 大家最近挖挖的如火如荼&#xff0c;可是不论是社区活动积分和参与挖矿积分&#xff0c;大家遇到了很多类似问题&#xff0c;重复解决。 因此我这里整理了一下常见的相关问题&#xff0c;大家可以一站式找到解决方案。解决方案主要分为运营和挖矿两个两面…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的障碍物检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发障碍物检测系统对于道路安全性具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个障碍物检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#xff0c;展示了不同模型间的性能…

如何解决由触发器导致 MySQL 内存溢出?

由触发器导致得 OOM 案例分析过程和解决方式。 作者&#xff1a;龚唐杰&#xff0c;爱可生 DBA 团队成员&#xff0c;主要负责 MySQL 技术支持&#xff0c;擅长 MySQL、PG、国产数据库。 爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联系小编…

JavaScript 面试题

问题 1 // 请解释下面代码的输出结果 console.log(1 "2" "2"); console.log(1 "2" "2"); console.log(1 -"1" "2"); console.log("A" - "B" "2"); console.log(&qu…

零基础学习JS--基础篇--使用对象

JavaScript 的设计是一个简单的基于对象的范式。一个对象就是一系列属性的集合&#xff0c;一个属性包含一个名和一个值。一个属性的值可以是函数&#xff0c;这种情况下属性也被称为方法。除了浏览器里面预定义的那些对象之外&#xff0c;你也可以定义你自己的对象。本章节讲述…

苹果cms模板保护设置,防止被扒

苹果cms模板保护设置&#xff0c;防止被扒 如今互联网时代&#xff0c;网站模板前端被扒是常有的事&#xff0c;如何防止模板数据被扒&#xff1f; 保护设置方法&#xff1a; 登录宝塔 找到安装模板的网站 设置禁止访问文件 方法参考截图后缀填&#xff1a;php|html 目录填&a…

OA系统中的九大常用审批场景,你都晓得吗?

Hi&#xff0c;我是贝格前端工场&#xff0c;今天继续来剖析OA的功能&#xff0c;这次重点分析审批功能&#xff0c;欢迎老铁们点赞评论转发。 一、OA的审批功能和流程 OA的审批功能是指在办公自动化系统中&#xff0c;通过电子化的方式实现对各种申请、请求或业务流程的审批管…

项目管理工具及模板(甘特图、OKR周报、任务管理、头脑风暴等)

项目管理常用模板大全&#xff1a; 1. 项目组OKR周报 2. 项目组传统周报工作法 3. 项目甘特图 4. 团队名单 5. 招聘跟进表 6. 出勤统计 7. 年度工作日历 8. 项目工作年计划 9. 版本排期 10. 项目组任务管理 11. 项目规划模板 12. 产品分析报告 13. 头脑风暴 信息化项目建设全套…

阻塞队列学习

1、什么是阻塞队列&#xff1f; 顾名思义&#xff0c;就是支持阻塞的队列&#xff0c;相比于其他的队列&#xff0c;阻塞队列支持以下特性&#xff1a; 队列为空的时候&#xff0c;获取元素的线程会等待队列变为非空。队列为满的时候&#xff0c;存储元素的线程会等待队列可以…

Python 单元测试

本篇为Python的单元测试的方法及示例 目录 概念 结果 示例 对函数进行测试 创建函数文件 创建测试文件 测试结果 对类进行测试 创建待测试类 创建测试文件 文档测试 创建函数 进行测试 总结 概念 用来对一个函数、一个类或者一个模块来进行正确性校验工作 结果 …

提取B站视频教程详情

提取B站视频教程详情 背景 B站这个视频列表是真的体验感太差了,有时候想把章节复制下来,再对应的章节下面做笔记,实在是太难搞了,于是就有了这篇文文章 根据关键字获取视频id Test public void list() {String url "https://api.bilibili.com/x/web-interface/wbi/sea…

虚拟机(KVM)克隆

当需要批量部署虚拟机时&#xff0c;可以使用克隆虚拟机的方式来进行。 使用图形界面来克隆虚拟机。 [rootzhoujunru_node1 zhou]# virsh list --allId Name State ------------------------------ vm01 shut off- vm01-clone shut off克隆完成。

Django入门 整体流程跑通

Django学习笔记 一、Django整体流程跑通 1.1安装 pip install django //安装 import django //在python环境中导入django django.get_version() //获取版本号&#xff0c;如果能获取到&#xff0c;说明安装成功Django目录结构 Python310-Scripts\django-admi…

Centos7 安装mongodb 7.0

官方手册参考&#xff1a; https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-red-hat/ Mongodb支持的版本 安装 MongoDB 社区版 按照以下步骤使用包管理器安装 MongoDB Community Edition yum。 配置包管理系统 ( yum) 创建一个/etc/yum.repos.d/mongodb-o…

Mabatis缓存详细介绍

MyBatis缓存分为一级缓存和二级缓存&#xff0c;是 MyBatis 中用于提高性能的两种缓存机制。 一级缓存 定义&#xff1a; 一级缓存指的是在同一个 SqlSession 中执行相同的查询语句时&#xff0c;第二次及后续的查询会直接从内存中获取结果&#xff0c;而不需要再次查询数据库…

容量治理三板斧:扩容、限流与降级

前言 随着现代软件系统日益复杂和用户规模的不断增长&#xff0c;分布式架构成为了保持系统高可用性与高性能的标准解决方案。然而&#xff0c;随之而来的是对系统容量治理的新挑战。在这样的背景下&#xff0c;容量治理成为了分布式系统设计和运维中不可或缺的一环。要确保系…

Python 主线任务之列表和操作列表,知其然知其所以然【玩转Python】

主线任务 主线任务之数据类型已进行66.6%&#xff0c;今日主线任务为“列表和操作列表”的了解和掌握。了解“列表”是什么的同时也不要忽略“怎么用”。即"知其然知其所以然"&#xff0c;方能达到学习的更优效果。 接下来让我们一起开启今日份的Python的主线任务。…

Orange3数据预处理(转换器组件)

该组件接收数据&#xff0c;然后重新应用之前在模板数据上执行的转换。 这些转换包括选择变量的子集以及从数据中出现的其他变量计算新的变量&#xff0c; 例如&#xff0c;离散化、特征构建、主成分分析&#xff08;PCA&#xff09;等。 在Orange3中&#xff0c;描述的这个组件…

php运行报错: Class ‘SimpleXMLElement‘ not found in

前言 CentOS7php 8.1.27CentOS7 利用remi yum源安装php8.1 php运行报错&#xff1a; Class ‘SimpleXMLElement’ not found in [12-Mar-2024 05:47:29 UTC] PHP Warning: Undefined array key "token" in /data/www/a.php on line 3 [12-Mar-2024 05:47:29 UTC]…

初窥机器学习

人工智能 近几年来&#xff0c;人工智能&#xff08;AI&#xff09;已成为家喻户晓的术语&#xff0c;我们在游戏、电影&#xff08;还记得J.A.R.V.I.S吗&#xff1f;&#xff09;和书籍中经常看到它的提及和描绘&#xff0c;但人工智能究竟是什么呢&#xff1f; 人工智能简单…