
HoRain 云小助手个人主页⛺️生活的理想就是为了理想的生活!⛳️ 推荐前些天发现了一个超棒的服务器购买网站性价比超高大内存超划算忍不住分享一下给大家。点击跳转到网站。目录⛳️ 推荐核心思想为什么需要享元模式问题场景实例解决方案享元模式的实现基本结构实例使用示例实例享元模式的核心组件1. Flyweight享元接口或抽象类2. ConcreteFlyweight具体享元类3. FlyweightFactory享元工厂4. Client客户端更复杂的示例游戏开发中的应用实例享元模式的优缺点优点缺点适用场景适合使用享元模式的场景不适合使用的场景实践练习练习 1改进字符渲染系统实例练习 2实现图标管理系统实例总结享元模式是一种结构型设计模式它通过共享对象来最大限度地减少内存使用和提高性能。简单来说就是共享元数据的思想。想象一下你去图书馆借书如果每本书都被不同的人单独购买图书馆就需要存储成千上万本相同的书。但实际上图书馆只需要存储一本《Python 入门》所有想读这本书的人都可以借阅同一本。享元模式就是这样的图书馆它管理着可共享的对象。核心思想享元模式将对象的状态分为两种内部状态(Intrinsic State)不变的、可共享的部分外部状态(Extrinsic State)变化的、不可共享的部分通过共享内部状态避免创建大量相似对象从而节省系统资源。为什么需要享元模式问题场景假设我们正在开发一个文字处理器需要渲染文档中的字符。如果每个字符都创建一个独立的对象实例# 不好的实现每个字符都是独立对象class Character:def __init__(self, char, font, size, color):self.char char # 字符self.font font # 字体self.size size # 字号self.color color # 颜色def render(self, position):print(f在位置 {position} 渲染字符 {self.char})# 使用示例char_a Character(A, 宋体, 12, 黑色)char_b Character(B, 宋体, 12, 黑色)char_a_another Character(A, 宋体, 12, 黑色) # 重复创建相同的A这种实现方式的问题内存浪费相同的字符被重复创建性能低下大量对象创建和销毁开销难以维护对象数量爆炸式增长解决方案使用享元模式我们可以共享相同的字符对象只存储一份内部状态字符本身外部状态位置在渲染时传入享元模式的实现基本结构让我们用文字处理器的例子来实现享元模式实例from typing import Dict# 享元类 - 存储内部状态class CharacterFlyweight:def __init__(self, char: str, font: str, size: int, color: str):self.char char # 内部状态字符内容self.font font # 内部状态字体self.size size # 内部状态字号self.color color # 内部状态颜色def render(self, position: tuple):渲染字符position 是外部状态x, y positionprint(f在位置({x}, {y})渲染: 字符{self.char} f[字体:{self.font}, 大小:{self.size}, 颜色:{self.color}])# 享元工厂 - 管理共享对象class CharacterFactory:_characters: Dict[str, CharacterFlyweight] {}classmethoddef get_character(cls, char: str, font: str, size: int, color: str) - CharacterFlyweight:# 创建对象的唯一标识键key f{char}_{font}_{size}_{color}# 如果对象不存在则创建并缓存if key not in cls._characters:cls._characters[key] CharacterFlyweight(char, font, size, color)print(f创建新字符对象: {key})else:print(f重用现有字符对象: {key})return cls._characters[key]# 客户端类 - 使用享元对象class TextDocument:def __init__(self):self.characters [] # 存储字符和位置信息def add_character(self, char: str, font: str, size: int, color: str, position: tuple):# 从工厂获取享元对象character CharacterFactory.get_character(char, font, size, color)# 存储字符对象和外部状态位置self.characters.append((character, position))def render(self):print(\n 开始渲染文档 )for character, position in self.characters:character.render(position)print( 文档渲染完成 \n)使用示例实例# 创建文档document TextDocument()# 添加字符到文档document.add_character(H, Arial, 12, black, (0, 0))document.add_character(e, Arial, 12, black, (1, 0))document.add_character(l, Arial, 12, black, (2, 0))document.add_character(l, Arial, 12, black, (3, 0)) # 重用 ldocument.add_character(o, Arial, 12, black, (4, 0))document.add_character(!, Arial, 12, red, (5, 0)) # 不同颜色创建新对象document.add_character(H, Arial, 12, black, (0, 1)) # 重用 H# 渲染文档document.render()# 查看工厂中的对象数量print(f工厂中总共创建了 {len(CharacterFactory._characters)} 个字符对象)输出结果创建新字符对象: H_Arial_12_black 创建新字符对象: e_Arial_12_black 创建新字符对象: l_Arial_12_black 重用现有字符对象: l_Arial_12_black 创建新字符对象: o_Arial_12_black 创建新字符对象: !_Arial_12_red 重用现有字符对象: H_Arial_12_black 开始渲染文档 在位置(0, 0)渲染: 字符H [字体:Arial, 大小:12, 颜色:black] 在位置(1, 0)渲染: 字符e [字体:Arial, 大小:12, 颜色:black] 在位置(2, 0)渲染: 字符l [字体:Arial, 大小:12, 颜色:black] 在位置(3, 0)渲染: 字符l [字体:Arial, 大小:12, 颜色:black] 在位置(4, 0)渲染: 字符o [字体:Arial, 大小:12, 颜色:black] 在位置(5, 0)渲染: 字符! [字体:Arial, 大小:12, 颜色:red] 在位置(0, 1)渲染: 字符H [字体:Arial, 大小:12, 颜色:black] 文档渲染完成 工厂中总共创建了 6 个字符对象享元模式的核心组件1. Flyweight享元接口或抽象类定义享元对象的接口通常包含操作外部状态的方法。2. ConcreteFlyweight具体享元类实现享元接口存储内部状态。内部状态必须是不可变的。3. FlyweightFactory享元工厂创建和管理享元对象确保正确地共享享元对象。4. Client客户端维护外部状态在需要时向享元工厂请求享元对象。更复杂的示例游戏开发中的应用让我们看一个游戏开发中的实际例子 - 树木渲染系统实例from typing import Dict, Listfrom dataclasses import dataclassdataclassclass TreeType:享元类 - 树的类型内部状态name: str # 树种名称texture: str # 纹理文件color: str # 基础颜色def render(self, x: int, y: int, height: int):渲染树位置和高度是外部状态print(f在({x}, {y})渲染{self.name}树高度{height}米 f[纹理:{self.texture}, 颜色:{self.color}])class TreeFactory:享元工厂 - 管理树类型_tree_types: Dict[str, TreeType] {}classmethoddef get_tree_type(cls, name: str, texture: str, color: str) - TreeType:key f{name}_{texture}_{color}if key not in cls._tree_types:cls._tree_types[key] TreeType(name, texture, color)print(f创建新的树类型: {name})return cls._tree_types[key]classmethoddef list_tree_types(cls):print(f\n当前共有 {len(cls._tree_types)} 种树类型:)for tree_type in cls._tree_types.values():print(f - {tree_type.name})class Tree:树对象 - 包含享元引用和外部状态def __init__(self, x: int, y: int, height: int, tree_type: TreeType):self.x x # 外部状态X坐标self.y y # 外部状态Y坐标self.height height # 外部状态高度self.tree_type tree_type # 享元对象引用def render(self):self.tree_type.render(self.x, self.y, self.height)class Forest:森林 - 客户端类def __init__(self):self.trees: List[Tree] []def plant_tree(self, x: int, y: int, height: int,name: str, texture: str, color: str):tree_type TreeFactory.get_tree_type(name, texture, color)tree Tree(x, y, height, tree_type)self.trees.append(tree)def render(self):print(\n 开始渲染森林 )for tree in self.trees:tree.render()print( 森林渲染完成 )# 使用示例forest Forest()# 种植树木 - 相同类型的树会共享享元对象forest.plant_tree(10, 20, 15, 松树, pine_texture.png, 深绿色)forest.plant_tree(30, 40, 12, 松树, pine_texture.png, 深绿色) # 重用松树类型forest.plant_tree(50, 60, 18, 橡树, oak_texture.png, 浅绿色)forest.plant_tree(70, 80, 20, 松树, pine_texture.png, 深绿色) # 再次重用forest.plant_tree(90, 100, 16, 枫树, maple_texture.png, 红色)# 渲染森林forest.render()# 查看树类型统计TreeFactory.list_tree_types()享元模式的优缺点优点大幅减少内存使用通过共享相似对象显著降低内存占用提高性能减少对象创建和垃圾回收的开销代码复用相同的对象逻辑只需实现一次易于扩展新增享元类型不会影响现有代码缺点增加复杂性需要区分内部状态和外部状态线程安全问题共享对象在多线程环境下需要额外处理可能引入bug如果错误地修改了内部状态会影响所有使用者不适用于所有场景只有当对象真正可共享时才有效适用场景适合使用享元模式的场景大量相似对象系统需要创建大量相似对象时内存敏感应用移动设备、嵌入式系统等内存受限环境缓存系统需要缓存和重用对象时游戏开发渲染大量相同类型的游戏对象文档处理文字处理器、表格处理等不适合使用的场景对象差异很大如果每个对象都有独特的状态外部状态复杂如果外部状态的管理比对象创建更复杂性能要求不高在内存充足且性能要求不高的场景实践练习练习 1改进字符渲染系统尝试改进我们之前的字符渲染系统添加对粗体、斜体等样式的支持实例# 你的改进代码在这里class AdvancedCharacterFlyweight:# 添加对粗体、斜体、下划线的支持pass# 测试你的实现def test_advanced_system():# 创建包含不同样式的文档pass练习 2实现图标管理系统设计一个图标管理系统相同的图标在不同位置显示时共享同一个对象实例class IconFlyweight:# 存储图标文件路径、尺寸等内部状态passclass IconFactory:# 管理图标享元对象passclass Application:# 在界面不同位置显示图标pass总结享元模式是一种强大的优化技术它通过共享对象来减少资源消耗。关键要点区分状态明确区分内部状态可共享和外部状态不可共享使用工厂通过工厂类管理享元对象的创建和共享权衡利弊在内存节省和代码复杂性之间找到平衡适用场景主要用于存在大量相似对象的场景❤️❤️❤️本人水平有限如有纰漏欢迎各位大佬评论批评指正如果觉得这篇文对你有帮助的话也请给个点赞、收藏下吧非常感谢! Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧