【再谈设计模式】享元模式~对象共享的优化妙手

devtools/2024/12/29 6:42:46/

一、引言

        在软件开发过程中,我们常常面临着创建大量细粒度对象的情况,这可能会导致内存占用过高、性能下降等问题。享元模式(Flyweight Pattern)就像是一位空间管理大师,它能够在不影响功能的前提下,有效地减少对象的数量,从而优化系统资源的使用。

二、定义与描述

        享元模式是一种结构型设计模式,它主要用于通过共享尽可能多的相似对象来减少内存使用和提高性能。其核心思想是将对象的状态分为内部状态(intrinsic state)和外部状态(extrinsic state)。内部状态是对象可共享的部分,它不会随环境的改变而改变;外部状态则是随环境变化而变化的部分,不能被共享。

三、抽象背景

        假设我们正在开发一个游戏,游戏中有许多相同类型的怪物,这些怪物可能只有一些属性(如生命值、攻击力等)的差异。如果我们为每个怪物都创建一个独立的对象,那么随着怪物数量的增加,内存的消耗将变得非常大。享元模式就可以用来解决这个问题,将怪物的通用属性(如外观、基本行为等内部状态)进行共享,而将每个怪物特有的属性(如当前生命值、当前攻击力等外部状态)单独处理。

四、适用场景与现实问题解决

  • 图形绘制系统
    • 在图形绘制系统中,可能需要绘制大量相同类型的图形,如圆形、矩形等。这些图形的形状(内部状态)是固定的,但它们的位置、颜色(外部状态)可能不同。通过享元模式,可以共享图形的形状对象,减少内存占用。

  • 文档编辑器中的字符处理
    • 文档编辑器中有大量的字符,每个字符的字体样式、大小等属性可能不同,但字符的基本形状(内部状态)是相同的。享元模式可以用来共享字符的基本形状对象。

五、享元模式的现实生活的例子

  • 汽车租赁公司
    • 汽车租赁公司有多种类型的汽车可供租赁,如轿车、SUV等。每一种类型的汽车(内部状态)是固定的,包括车型、座位数等。而汽车的颜色、当前里程数(外部状态)是随每一次租赁而变化的。租赁公司可以将相同类型的汽车看作是享元对象,共享汽车的基本信息,从而更好地管理车辆资源。

  • 咖啡店的咖啡杯
    • 咖啡店有不同种类的咖啡杯,如拿铁杯、卡布奇诺杯等。杯子的形状(内部状态)是固定的,但是杯子里咖啡的量、是否加糖(外部状态)是不同的。咖啡店可以将相同类型的杯子看作享元对象,共享杯子的基本形状信息。

六、初衷与问题解决

        初衷是为了减少内存中对象的数量,提高系统的性能和资源利用率。通过共享内部状态,避免了创建大量重复的对象,从而解决了因对象数量过多导致的内存占用过大和性能下降的问题。

七、代码示例

Java示例

类图:

  • FlyweightFactory 类有一个私有属性 flyweights(类型为 Map<String, Flyweight>)用于存储享元对象,并且有 getFlyweight 方法,根据传入的 key 来获取或创建具体的享元对象。
  • Flyweight 是抽象类,有受保护的属性 key,构造方法以及抽象方法 operation,定义了享元对象的基本结构和行为规范。
  • ConcreteFlyweight 类继承自 Flyweight 类,实现了自己的构造方法,并覆写了 operation 方法,用于提供具体的享元行为实现。

流程图:

        首先创建 FlyweightFactory 对象,然后两次调用 getFlyweight 方法来获取享元对象(第二次调用时会复用第一次创建的对象,因为已经存在对应 key 的对象了),最后分别调用获取到的享元对象的 operation 方法来执行具体操作。 

代码:

import java.util.HashMap;
import java.util.Map;// 享元工厂类
class FlyweightFactory {private Map<String, Flyweight> flyweights = new HashMap<>();public Flyweight getFlyweight(String key) {if (!flyweights.containsKey(key)) {flyweights.put(key, new ConcreteFlyweight(key));}return flyweights.get(key);}
}// 抽象享元类
abstract class Flyweight {protected String key;public Flyweight(String key) {this.key = key;}abstract void operation();
}// 具体享元类
class ConcreteFlyweight extends Flyweight {public ConcreteFlyweight(String key) {super(key);}@Overridevoid operation() {System.out.println("具体享元 " + key + " 被调用");}
}public class Main {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight flyweight1 = factory.getFlyweight("A");Flyweight flyweight2 = factory.getFlyweight("A");flyweight1.operation();flyweight2.operation();}
}

C++示例

#include <iostream>
#include <unordered_map>// 抽象享元类
class Flyweight {
public:virtual void operation() = 0;virtual ~Flyweight() {}
};// 具体享元类
class ConcreteFlyweight : public Flyweight {
private:std::string key;
public:ConcreteFlyweight(std::string key) : key(key) {}void operation() override {std::cout << "具体享元 " << key << " 被调用" << std::endl;}
};// 享元工厂类
class FlyweightFactory {
private:std::unordered_map<std::string, Flyweight*> flyweights;
public:Flyweight* getFlyweight(std::string key) {if (flyweights.find(key) == flyweights.end()) {flyweights[key] = new ConcreteFlyweight(key);}return flyweights[key];}~FlyweightFactory() {for (auto it : flyweights) {delete it.second;}}
};int main() {FlyweightFactory factory;Flyweight* flyweight1 = factory.getFlyweight("A");Flyweight* flyweight2 = factory.getFlyweight("A");flyweight1->operation();flyweight2->operation();return 0;
}

Python示例

class FlyweightFactory:def __init__(self):self.flyweights = {}def get_flyweight(self, key):if key not in self.flyweights:self.flyweights[key] = ConcreteFlyweight(key)return self.flyweights[key]class Flyweight:def __init__(self, key):self.key = keydef operation(self):passclass ConcreteFlyweight(Flyweight):def operation(self):print(f"具体享元 {self.key} 被调用")if __name__ == "__main__":factory = FlyweightFactory()flyweight1 = factory.get_flyweight("A")flyweight2 = factory.get_flyweight("A")flyweight1.operation()flyweight2.operation()

Go示例

package mainimport ("fmt"
)// 抽象享元接口
type Flyweight interface {operation()
}// 具体享元结构体
type ConcreteFlyweight struct {key string
}func (cf *ConcreteFlyweight) operation() {fmt.Printf("具体享元 %s 被调用\n", cf.key)
}// 享元工厂结构体
type FlyweightFactory struct {flyweights map[string]Flyweight
}func NewFlyweightFactory() *FlyweightFactory {return &FlyweightFactory{flyweights: make(map[string]Flyweight),}
}func (ff *FlyweightFactory) getFlyweight(key string) Flyweight {if _, ok := ff.flyweights[key];!ok {ff.flyweights[key] = &ConcreteFlyweight{key}}return ff.flyweights[key]
}func main() {factory := NewFlyweightFactory()flyweight1 := factory.getFlyweight("A")flyweight2 := factory.getFlyweight("A")flyweight1.operation()flyweight2.operation()
}

八、享元模式的优缺点

  • 优点

    • 减少内存占用:通过共享对象,大大减少了创建对象所需的内存空间,特别是在处理大量相似对象时效果显著。
    • 提高性能:减少了对象的创建和销毁操作,从而提高了系统的运行速度。
    • 易于维护:将对象的内部状态和外部状态分离,使得代码结构更加清晰,易于理解和维护。
  • 缺点

    • 增加复杂性:需要额外的代码来管理享元对象的创建、共享和维护,这可能会增加系统的复杂性。
    • 外部状态管理:外部状态的处理需要额外的设计考虑,如果处理不当可能会导致逻辑混乱。

九、享元模式的升级版

        一种常见的升级版是组合享元模式(Composite Flyweight Pattern)。在这种模式下,享元对象可以组合成更复杂的结构。例如,在图形绘制系统中,不仅可以共享单个图形(如圆形、矩形)的享元对象,还可以将这些享元对象组合成更复杂的图形(如由多个圆形和矩形组成的复杂图案),而这个复杂图案本身也可以作为一个享元对象被共享。这样可以进一步提高系统的灵活性和资源利用率。

思维导图:


http://www.ppmy.cn/devtools/146325.html

相关文章

深度解析:电商平台API接口的安全挑战与应对策略

随着电子商务的蓬勃发展&#xff0c;电商平台与外部服务、内部系统之间的数据交换和通信变得日益频繁。API&#xff08;应用程序编程接口&#xff09;接口作为这一过程中的关键枢纽&#xff0c;其安全性显得尤为重要。API接口不仅承载着商品管理、订单处理、支付结算、用户管理…

macos安装maven以及.bash_profile文件优化

文章目录 下载和安装maven本地仓库配置国内镜像仓库配置.bash_profile文件优化 下载和安装maven maven下载地址 存放在/Library/Java/env/maven目录 本地仓库配置 在maven-3.9.9目录下创建maven-repo目录作为本地文件仓库打开setting配置文件 在setting标签下&#xff0c;添…

基于Spring Boot的高校请假管理系统

一、系统背景与意义 随着高校规模的扩大和学生数量的增加&#xff0c;传统的请假管理方式已经难以满足高校管理的需求。人工请假流程繁琐、耗时长&#xff0c;且容易出现信息错误或遗漏。因此&#xff0c;开发一套基于Spring Boot的高校请假管理系统具有重要意义&#xff0c;它…

目标检测中的正负样本是什么,是如何起作用的?

1、正负样本的定义 1.1、正样本 正样本是指那些与真实目标&#xff08;Ground Truth, GT&#xff09;有很高关联性的样本&#xff0c;其作用是让模型学习目标的位置和类别特征。举个例子&#xff1a; Anchor-based中&#xff0c;如果一个候选框&#xff08;Anchor&#xff09;与…

workman服务端开发模式-应用开发-vue-element-admin封装websocket

一、用于保存WebSocket实例对象 export const WebSocketHandle undefined 二、外部根据具体登录地址实例化WebSocket&#xff0c;然后回传保存WebSocket export const WebsocketINI function (websocketinstance) {this.WebSocketHandle websocketinstancethis.WebSocket…

overleaf中出现TeX capacity exceeded PDF object stream buffer=5000000的原因和解决方案

在插入pdf 配图后&#xff0c;编译出错提示信息如图&#xff0c;很可能的一个原因是pdf文件大小太大了&#xff0c;最好压缩一下&#xff0c;压缩到1MB以内。

【ES6复习笔记】迭代器(10)

什么是迭代器&#xff1f; 迭代器&#xff08;Iterator&#xff09;是一种对象&#xff0c;它能够遍历并访问一个集合中的元素。在 JavaScript 中&#xff0c;迭代器提供了一种统一的方式来处理各种集合&#xff0c;如数组、字符串、Map、Set 等。通过迭代器&#xff0c;我们可…

Vue 3 与 Tauri 集成开发跨端APP

1、安装RUST 下载地址&#xff1a;Install Rust - Rust Programming Language 安装&#xff1a; 安装完成后&#xff0c;在命令行里运行: rustup 2、安装 Node.js 与 npm 或 pnpm &#xff0c;如果已经安装&#xff0c;可以忽略 # 使用 nvm 安装 Node.js 最新版本 nvm install…