当前位置: 首页 > article >正文

23种设计模式-享元(Flyweight)设计模式

文章目录

  • 一.什么是享元设计模式?
  • 二.享元模式的特点
  • 三.享元模式的结构
  • 四.享元模式的优缺点
  • 五.享元模式的 C++ 实现
  • 六.享元模式的 JAVA 实现
  • 七.代码解析
  • 八.总结

类图: 享元设计模式类图

一.什么是享元设计模式?

享元(Flyweight)设计模式是一种结构型设计模式,通过共享对象来减少内存占用和对象创建开销。它通过将对象的可共享部分不可共享部分分离,减少重复对象的数量,从而节省内存。

 享元模式的核心思想是共享对象状态,在需要大量相似对象时,仅存储少量可复用的对象,其他状态通过外部管理。

二.享元模式的特点

  1. 共享性:通过共享相同状态的对象,降低内存占用。
  2. 分离状态
    • 内部状态:对象内部不变的部分,可共享。
    • 外部状态:对象动态变化的部分,由客户端负责管理。

三.享元模式的结构

  • Flyweight(抽象享元类):定义对象共享的接口,通常是一个轻量化接口。
  • ConcreteFlyweight(具体享元类):实现抽象享元类,存储对象的共享部分。
  • UnsharedConcreteFlyweight(非共享享元类):不需要共享的对象。
  • FlyweightFactory(享元工厂类):管理享元对象的创建和共享。
  • Client(客户端):负责维护外部状态,调用享元对象完成操作。
    在这里插入图片描述

四.享元模式的优缺点

  • 优点:
    • 减少对象创建,节省内存。
    • 提高性能,尤其是在需要大量相似对象时。
  • 缺点:
    • 增加了系统复杂性:需要分离内部状态和外部状态。
    • 需要确保外部状态的正确性,否则可能导致数据问题。

五.享元模式的 C++ 实现

 以下代码实现了一个场景:在一片森林中种植多棵树,树的种类是共享的(享元对象),而每棵树的位置是外部状态。

#include <iostream>
#include <unordered_map>
#include <string>
#include <memory>
using namespace std;

// 抽象享元类
class TreeType {
protected:
    string name;       // 树的名称
    string color;      // 树的颜色
    string texture;    // 树的纹理
public:
    TreeType(const string& name, const string& color, const string& texture)
        : name(name), color(color), texture(texture) {}

    virtual void Display(int x, int y) const = 0; // 显示树的信息
    virtual ~TreeType() = default;
};

// 具体享元类
class ConcreteTreeType : public TreeType {
public:
    ConcreteTreeType(const string& name, const string& color, const string& texture)
        : TreeType(name, color, texture) {}

    void Display(int x, int y) const override {
        cout << "Tree [Type: " << name
             << ", Color: " << color
             << ", Texture: " << texture
             << "] at (" << x << ", " << y << ")" << endl;
    }
};

// 享元工厂类
class TreeFactory {
private:
    unordered_map<string, shared_ptr<TreeType>> treeTypes; // 存储享元对象
public:
    shared_ptr<TreeType> GetTreeType(const string& name, const string& color, const string& texture) {
        string key = name + "_" + color + "_" + texture;
        if (treeTypes.find(key) == treeTypes.end()) {
            // 如果对象不存在,则创建新的享元对象
            treeTypes[key] = make_shared<ConcreteTreeType>(name, color, texture);
            cout << "Created new TreeType: " << key << endl;
        }
        return treeTypes[key];
    }
};

// 客户端类:表示单棵树
class Tree {
private:
    shared_ptr<TreeType> type; // 树的种类(享元对象)
    int x, y;                 // 树的位置(外部状态)
public:
    Tree(int x, int y, shared_ptr<TreeType> type) : x(x), y(y), type(type) {}

    void Display() const {
        type->Display(x, y); // 调用享元对象的 Display 方法
    }
};

// 主函数
int main() {
    TreeFactory factory;

    // 创建森林中的树
    shared_ptr<Tree> tree1 = make_shared<Tree>(10, 20, factory.GetTreeType("Oak", "Green", "Smooth"));
    shared_ptr<Tree> tree2 = make_shared<Tree>(15, 25, factory.GetTreeType("Pine", "Dark Green", "Rough"));
    shared_ptr<Tree> tree3 = make_shared<Tree>(10, 20, factory.GetTreeType("Oak", "Green", "Smooth")); // 复用已有类型

    // 显示所有树
    tree1->Display();
    tree2->Display();
    tree3->Display();

    return 0;
}

六.享元模式的 JAVA 实现

import java.util.HashMap;
import java.util.Map;

// 抽象享元类
abstract class TreeType {
    protected String name;
    protected String color;
    protected String texture;

    public TreeType(String name, String color, String texture) {
        this.name = name;
        this.color = color;
        this.texture = texture;
    }

    public abstract void display(int x, int y);
}

// 具体享元类
class ConcreteTreeType extends TreeType {
    public ConcreteTreeType(String name, String color, String texture) {
        super(name, color, texture);
    }

    @Override
    public void display(int x, int y) {
        System.out.println("Tree [Type: " + name + ", Color: " + color + ", Texture: " + texture
                + "] at (" + x + ", " + y + ")");
    }
}

// 享元工厂类
class TreeFactory {
    private Map<String, TreeType> treeTypes = new HashMap<>();

    public TreeType getTreeType(String name, String color, String texture) {
        String key = name + "_" + color + "_" + texture;
        if (!treeTypes.containsKey(key)) {
            treeTypes.put(key, new ConcreteTreeType(name, color, texture));
            System.out.println("Created new TreeType: " + key);
        }
        return treeTypes.get(key);
    }
}

// 客户端类:单棵树
class Tree {
    private TreeType type; // 树的类型(享元对象)
    private int x, y;      // 树的位置(外部状态)

    public Tree(int x, int y, TreeType type) {
        this.x = x;
        this.y = y;
        this.type = type;
    }

    public void display() {
        type.display(x, y); // 调用享元对象的 display 方法
    }
}

// 主函数
public class FlyweightPatternDemo {
    public static void main(String[] args) {
        TreeFactory factory = new TreeFactory();

        // 创建森林中的树
        Tree tree1 = new Tree(10, 20, factory.getTreeType("Oak", "Green", "Smooth"));
        Tree tree2 = new Tree(15, 25, factory.getTreeType("Pine", "Dark Green", "Rough"));
        Tree tree3 = new Tree(10, 20, factory.getTreeType("Oak", "Green", "Smooth")); // 复用已有类型

        // 显示所有树
        tree1.display();
        tree2.display();
        tree3.display();
    }
}

七.代码解析

  • TreeType 和 ConcreteTreeType(享元类)
    • TreeType 是抽象基类,定义了树的名称、颜色、纹理等共享部分。
    • ConcreteTreeType 是具体实现,包含共享的树种信息。
  • TreeFactory(享元工厂类)
    • 工厂类维护一个哈希表(unordered_map),用于存储和复用享元对象。
    • 当请求一个 TreeType 时,如果已有共享对象,直接返回;否则创建新对象。
  • Tree(客户端类)
    • Tree 对象包含外部状态(位置坐标 x 和 y),同时持有一个指向 TreeType 的指针。
    • 外部状态由客户端管理,享元对象不直接维护。
  • 运行结果
    • 创建新的 TreeType 时,工厂输出 “Created new TreeType”。
    • 共享的 TreeType 只会创建一次,相同类型的树直接复用。

八.总结

 享元模式通过共享相同类型的对象,减少内存开销,非常适合需要大量重复对象的场景。然而,使用享元模式需要注意内部状态与外部状态的划分,同时管理外部状态的复杂性可能增加。
应用场景:

  • 游戏开发:重复的图形元素(如树木、建筑物)可以共享对象。
  • 文本编辑器:字符对象的字体、颜色等共享,而具体位置动态维护。
  • 网络连接池:数据库或网络连接共享对象,节约资源。

http://www.kler.cn/a/413222.html

相关文章:

  • Jenkins升级到最新版本后无法启动
  • 解决首次加载数据空指针异常
  • 攸信技术:运动文化激发企业活力,赋能体育行业新未来
  • 基础入门-Web应用架构类别源码类别镜像容器建站模版编译封装前后端分离
  • 远程视频验证如何改变商业安全
  • 李宏毅机器学习课程知识点摘要(14-18集)
  • 基于SSM的婴幼儿用品商城系统+LW示例参考
  • C#里怎么样快速使用LINQ实现查询?
  • k8s集群增加nfs-subdir-external-provisioner存储类
  • UWB数字钥匙安全测距和场景应用
  • SQL EXISTS 子句的深入解析
  • 电脑上的ip地址可以改吗?如何改变ip地址
  • Java图书管理系统(简易保姆级)
  • CTF之密码学(RSA加密)
  • PMP好考吗,有多大的价值?
  • Leetcode 每日一题 30.串联所有单词的子串
  • 《用Python实现3D动态旋转爱心模型》
  • 前端学习笔记之FileReader
  • 蓝牙定位的MATLAB仿真程序|基于信号强度的定位,平面、四个蓝牙基站(附源代码)
  • React的基础知识:Context
  • 【vue】导航守卫
  • 高级java每日一道面试题-2024年11月27日-JVM篇-JVM的永久代中会发生垃圾回收么?
  • 将jar包导入maven
  • 【git】取消一个已提交的文件或路径的追踪
  • Java线程的使用
  • 多线程 相关面试集锦