简介

享元模式(Flyweight Pattern),是一种结构型设计模式。主要用于减少创建对象的数量,以减少内存占用和提高性能。它摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所共有的相同状态,让你能在有限的内存容量中载入更多对象。

当程序需要生成数量巨大的相似对象时,可能对内存有大量损耗,而对象中包含可抽取且能在多个对象间共享的重复状态,您可以采取享元模式。

内部状态 vs. 外部状态
内部状态是存储在享元对象内部,一般在构造时确定或通过setter设置,并且不会随环境改变而改变的状态,因此内部状态可以共享。
外部状态是随环境改变而改变、不可以共享的状态。外部状态在需要使用时通过客户端传入享元对象。外部状态必须由客户端保存。

作用

  1. 有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
  2. 如果程序中有很多相似对象,可减少对象的创建,降低系统的内存,使效率提高。

实现步骤

  1. 创建享元角色抽象接口,用于具体享元角色实现。
  2. 创建具体享元角色,实现抽象方法。具体享元角色就是一般类,该类可以支持外部状态数据。
  3. 创建享元工厂,里面建一个储存对象共享池,对已经实例化的对象直接取出返回。

UML

Java代码

享元抽象接口

// Flyweight.java 享元角色抽象接口
public interface Flyweight {
void operate(String state);
}

具体享元角色

// ConcreteFlyweight.java 具体享元角色,实现抽象接口,用于共享状态,一个类被创建以后就不用重复创建了
public class ConcreteFlyweight implements Flyweight {
private String name;
private String type; public ConcreteFlyweight(String name) {
// 内部状态,即不会随着环境的改变而改变的可共享部分
// 这里的name也是对象保存的key
this.name = name;
this.type = "piano";
System.out.println("ConcreteFlyweight::ConcreteFlyweight(name) [创建具体享元" + name + "]");
} // 这里state属于外部状态,由外部调用时传入
// 也可以把非共享的对象传入进来
@Override
public void operate(String state) {
System.out.println(
String.format("%s::operate() [%s %s %s]", this.getClass().getName(), this.getName(),
this.getType(), state));
} public String getName() {
return this.name;
} public String getType() {
return this.type;
}
}
// UnsharedConcreteFlyweight.java 无需共享的角色,每次都是新实例
public class UnsharedConcreteFlyweight implements Flyweight {
private String name;
private String type = "guitar"; public UnsharedConcreteFlyweight(String name) {
this.name = name;
System.out.println("UnsharedConcreteFlyweight::UnsharedConcreteFlyweight(name) [创建非共享对象" + name + "]");
} // 这里state属于外部状态,在调用时外部传入。
@Override
public void operate(String state) {
System.out.println(
String.format("%s::operate() [%s %s %s]", this.getClass().getName(), this.getName(),
this.getType(), state));
} public String getName() {
return this.name;
} public String getType() {
return this.type;
}
}

享元工厂类

// FlyweightFactory.java 享元工厂,储存一个对象共享池,已经生成过的对象直接取出
public class FlyweightFactory {
public static Map<String, Flyweight> pool = new HashMap<String, Flyweight>(); // 这里的name可以认为是内部状态,在构造时确定,具有唯一性。
public static Flyweight getFactory(String name) {
Flyweight flyweight = pool.get(name);
if (flyweight == null) {
// 如果对象不存在则创建新的对象放入到池子里,如果已经存在则复用前面的对象
flyweight = new ConcreteFlyweight(name);
pool.put(name, flyweight);
} else {
System.out.println("FlyweightFactory::getFactory(name) [成功获取具体享元" + name + "]");
}
return flyweight;
}
}

测试调用

    /**
* 享元模式就是将已经声明过的实例或数据保存在内存里,需要使用时则取出来,无需再次实例化和声明。
* 通过共享多个对象所共有的相同状态,以达到节省开销的目的。
* 享元模式分为内部状态和外部状态,内部状态基于享元对象共享,外部状态则外部传入或使用非享元类。
*/ // 假设有钢琴和吉他,钢琴使用者很多需要共享实例,而吉他每次创建新实例 // 2个一样名称的为共享对象,只创建1个实例,后面的返回缓存实例
Flyweight factory1 = FlyweightFactory.getFactory("piano1");
// piano1已经声明过了,同名则共享前面的实例
Flyweight factory2 = FlyweightFactory.getFactory("piano1");
Flyweight factory3 = FlyweightFactory.getFactory("piano2");
Flyweight factory4 = FlyweightFactory.getFactory("piano2"); factory1.operate("factory1");
factory2.operate("factory2");
factory3.operate("factory3");
factory4.operate("factory4"); // 查看一共多少个对象
for (Map.Entry<String, Flyweight> entry : FlyweightFactory.pool.entrySet()) {
System.out.println("享元对象:" + entry.getKey());
// entry.getValue().operate(null);
} // 无需共享的,名字一样也是多个对象
Flyweight factory5 = new UnsharedConcreteFlyweight("guitar1");
Flyweight factory6 = new UnsharedConcreteFlyweight("guitar1");
factory5.operate("factory5");
factory6.operate("factory6");

C代码

头文件

// func.h 公共头文件
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h> // 享元角色抽象接口
typedef struct Flyweight
{
char name[50];
char kind[50];
void (*operate)(struct Flyweight *, char *);
} Flyweight; // 享元工厂,储存一个对象共享池,已经生成过的对象直接取出
typedef struct FlyweightFactory
{
char name[50];
int (*get_pool_size)();
Flyweight **(*get_pool)();
Flyweight *(*get_factory)(char *name);
} FlyweightFactory;
FlyweightFactory *flyweight_factory_constructor(char *name); // 具体享元角色,实现抽象接口,用于共享状态,一个类被创建以后就不用重复创建了
typedef struct ConcreteFlyweight
{
// 内部状态,即不会随着环境的改变而改变的可共享部分
// 这里的name也是对象保存的key
char name[50];
char kind[50];
void (*operate)(struct ConcreteFlyweight *, char *);
} ConcreteFlyweight;
ConcreteFlyweight *concrete_flyweight_constructor(char *name);
ConcreteFlyweight *concrete_flyweight_init(char *name); // 无需共享实例的角色,用于处理外部非共享状态
// 当不需要共享时用这样的类
typedef struct UnsharedConcreteFlyweight
{
char name[50];
char kind[50];
void (*operate)(struct UnsharedConcreteFlyweight *, char *);
} UnsharedConcreteFlyweight;
UnsharedConcreteFlyweight *unshared_concrete_flyweight_constructor(char *name);
UnsharedConcreteFlyweight *unshared_concrete_flyweight_init(char *name);

享元抽象接口

// flyweight.c 享元角色抽象接口
#include "func.h" // 享元角色抽象基础struct,相关定义在head

具体享元角色

// concrete_flyweight.c 具体享元角色,实现抽象接口,用于共享状态,一个类被创建以后就不用重复创建了
#include "func.h" /* 具体享元角色,实现抽象接口,用于共享状态,一个类被创建以后就不用重复创建了 */ // 享元对象实例化函数,对象实例化后共享对象
// state属于外部状态,由外部调用时传入,也可以把非共享的对象传入进来
void concrete_flyweight_operate(ConcreteFlyweight *flyweight, char *state)
{
printf("\r\n ConcreteFlyweight::operate() [name=%s kind=%s state=%s]", flyweight->name, flyweight->kind, state);
} ConcreteFlyweight *concrete_flyweight_constructor(char *name)
{
printf("\r\n ConcreteFlyweight::concrete_flyweight_constructor() 创建具体享元对象[name=%s]", name);
Flyweight *flyweight = (Flyweight *)malloc(sizeof(Flyweight));
strncpy(flyweight->name, name, 50);
strncpy(flyweight->kind, "piano", 50);
ConcreteFlyweight *concrete_flyweight = (ConcreteFlyweight *)flyweight;
concrete_flyweight->operate = &concrete_flyweight_operate;
return concrete_flyweight;
}
// unshared_concrete_flyweight.c 无需共享的角色,每次都是新实例
#include "func.h" /* 无需共享实例的角色,用于处理外部非共享状态 */ // 非共享对象的外部状态,这里state属于外部状态,在调用时外部传入。
void unshared_flyweight_operate(UnsharedConcreteFlyweight *flyweight, char *state)
{
printf("\r\n UnsharedConcreteFlyweight::operate() [name=%s kind=%s state=%s]", flyweight->name, flyweight->kind, state);
} // 无需共享的角色,每次都是新实例
UnsharedConcreteFlyweight *unshared_concrete_flyweight_constructor(char *name)
{
printf("\r\n UnsharedConcreteFlyweight::unshared_concrete_flyweight_constructor() 创建非共享对象[name=%s]", name);
Flyweight *flyweight = (Flyweight *)malloc(sizeof(Flyweight));
strncpy(flyweight->name, name, 50);
strncpy(flyweight->kind, "guitar", 50);
UnsharedConcreteFlyweight *unshared_flyweight = (UnsharedConcreteFlyweight *)flyweight;
unshared_flyweight->operate = &unshared_flyweight_operate;
return unshared_flyweight;
}

享元工厂类

// flyweight_factory.c 享元工厂,储存一个对象共享池,已经生成过的对象直接取出
#include "func.h" /* 享元工厂,储存一个对象共享池,已经生成过的对象直接取出 */ // 全局用来记录Flyweight的对象数组
static Flyweight **flyweight_factory_member_pool;
// 全局用来记录Flyweight的名称数组
static char **flyweight_factory_name_pool;
// 全局记录flyweight_factory的数量
static int flyweight_factory_pool_size = 0; // 这里的name可以认为是内部状态,在构造时确定,具有唯一性。
Flyweight *get_factory(char *name)
{ // 定义公共map用作共享池子,全局共用
if (flyweight_factory_member_pool == NULL)
{
flyweight_factory_member_pool = (Flyweight **)calloc(100, sizeof(Flyweight));
}
if (flyweight_factory_name_pool == NULL)
{
flyweight_factory_name_pool = (char **)calloc(100, sizeof(char));
} Flyweight **flyweight_pool = flyweight_factory_member_pool;
char **name_pool = flyweight_factory_name_pool;
int length = flyweight_factory_pool_size; int flyweight_index = -1;
for (int i = 0; i < length; i++)
{
if (name == name_pool[i])
{
flyweight_index = i;
break;
}
} Flyweight *flyweight; // 如果已经存在则复用前面的对象
if (flyweight_index >= 0)
{
flyweight = flyweight_pool[flyweight_index];
printf("\r\n FlyweightFactory::get_factory() 成功获取具体享元[name=%s]", name);
}
else
{
// 不存在则创建新的对象放入到池子里
flyweight = (Flyweight *)concrete_flyweight_constructor(name);
flyweight_pool[length] = flyweight;
name_pool[length] = name;
flyweight_factory_pool_size += 1;
printf("\r\n FlyweightFactory::get_factory() 成功创建具体享元[name=%s]", name);
}
return flyweight;
} Flyweight **get_flyweight_pool()
{
return flyweight_factory_member_pool;
} int get_flyweight_pool_size()
{
return flyweight_factory_pool_size;
} FlyweightFactory *flyweight_factory_constructor(char *name)
{
FlyweightFactory *factory = (FlyweightFactory *)malloc(sizeof(FlyweightFactory));
strncpy(factory->name, name, 50);
factory->get_factory = &get_factory;
factory->get_pool = &get_flyweight_pool;
factory->get_pool_size = &get_flyweight_pool_size;
return factory;
}

测试调用

   /**
* 享元模式就是将已经声明过的实例或数据保存在内存里,需要使用时则取出来,无需再次实例化和声明。
* 通过共享多个对象所共有的相同状态,以达到节省开销的目的。
* 享元模式分为内部状态和外部状态,内部状态基于享元对象共享,外部状态则外部传入或使用非享元类。
*/ FlyweightFactory *flyweight_factory = flyweight_factory_constructor("flyweight_factory");
// 假设有钢琴和吉他,钢琴使用者很多需要共享实例,而吉他每次创建新实例
// // 2个一样名称的为共享对象,只创建1个实例,后面的返回缓存实例
Flyweight *factory1 = flyweight_factory->get_factory("piano1");
Flyweight *factory2 = flyweight_factory->get_factory("piano1");
// 转换类型测试
ConcreteFlyweight *factory3 = (ConcreteFlyweight *)flyweight_factory->get_factory("piano2");
Flyweight *factory4 = flyweight_factory->get_factory("piano2"); factory1->operate(factory1, "factory1");
factory2->operate(factory2, "factory2");
factory3->operate(factory3, "factory3");
factory4->operate(factory4, "factory4"); // 打印全部共享对象
Flyweight **flyweight_pool = flyweight_factory->get_pool();
int pool_size = flyweight_factory->get_pool_size();
for (int i = 0; i < pool_size; i++)
{
printf("\r\n 享元对象:%d %s", i, flyweight_pool[i]->name);
} // 无需共享的对象,name虽然一样,是不同的实例
Flyweight *factory5 = (Flyweight *)unshared_concrete_flyweight_constructor("guitar1");
UnsharedConcreteFlyweight *factory6 = unshared_concrete_flyweight_constructor("guitar1");
factory5->operate(factory5, "factory5");
factory6->operate(factory6, "factory6");

更多语言版本

不同语言实现设计模式:https://github.com/microwind/design-pattern

【享元设计模式详解】C/Java/JS/Go/Python/TS不同语言实现的更多相关文章

  1. [ 转载 ] Java开发中的23种设计模式详解(转)

    Java开发中的23种设计模式详解(转)   设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类 ...

  2. android java 设计模式详解 Demo

    android java 设计模式详解 最近看了一篇设计模式的文章,深得体会,在此基础我将每种设计模式的案例都写成Demo的形式,方便读者研究学习, 首先先将文章分享给大家: 设计模式(Design ...

  3. Java温故而知新(5)设计模式详解(23种)

    一.设计模式的理解 刚开始“不懂”为什么要把很简单的东西搞得那么复杂.后来随着软件开发经验的增加才开始明白我所看到的“复杂”恰恰就是设计模式的精髓所在,我所理解的“简单”就是一把钥匙开一把锁的模式,目 ...

  4. JAVA设计模式简介及六种常见设计模式详解

    一.什么是设计模式                                                                                           ...

  5. JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法

    JAVA进阶之旅(一)--增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法 学完我们的java之旅,其实收获还是很多的,但是依然还有很 ...

  6. java高新技术-基本数据类型拆装箱及享元设计模式

    享元设计模式 public static void main(String[] args) { Integer iObj = 3; //自动装箱 System.out.println(iObj + 1 ...

  7. Javascript设计模式详解

    Javascript常用的设计模式详解 阅读目录 一:理解工厂模式 二:理解单体模式 三:理解模块模式 四:理解代理模式 五:理解职责链模式 六:命令模式的理解: 七:模板方法模式 八:理解javas ...

  8. Javascript常用的设计模式详解

    Javascript常用的设计模式详解 阅读目录 一:理解工厂模式 二:理解单体模式 三:理解模块模式 四:理解代理模式 五:理解职责链模式 六:命令模式的理解: 七:模板方法模式 八:理解javas ...

  9. javascript设计模式详解之策略模式

    接上篇命令模式来继续看下js设计模式中另一种常用的模式,策略模式.策略模式也是js开发中常用的一种实例,不要被这么略显深邃的名字给迷惑了.接下来我们慢慢看一下. 一.基本概念与使用场景: 基本概念:定 ...

  10. 设计模式_享元设计模式(flyweight)

    享元模式以共享的方式高效地支持大量的细粒度对象.很多很小的对象,这些对象有很多共同的东西,那就变成一个对象,还有些不同的东西,作为外部的属性,用方法的参数传入 public class Flyweig ...

随机推荐

  1. 安装 vue devtools 时,npm i 和 npm run build 报错问题

    1.如果 npm i 报错,运行如下命令: npm i --legacy-peer-deps 2.如果 npm run build 报错,运行如下命令: npm install -g yarn yar ...

  2. SpringBoot - Lombok使用详解1(基本介绍、安装配置、var和val)

    我们创建 POJO 类时,经常需要先写属性,然后手写或者自动生成 get 和 set 方法,然后还要重写 toString 方法....一系列操作下来,这个 POJO 类便会产生太多的样板式代码. 这 ...

  3. PyCharm2018 不使用IPython 不成功--一直显示连接控制台

    每次关闭PyCharm时,都提示有后台任务在运行,一开始不知道怎么回事. 另外,发现底部的,python 控制台,启动pycharm时总有错误信息. 后来发现底部状态栏,显示1个 进程在运行. 2个地 ...

  4. MSVC-用于其他IDE的手工环境配置,手工提取

    ​ 最近因为在使用Code::Blocks编程,遇到了MSVC编译的库,不愿意换VS,所以手工配置了MSVC路径.CB是有点老了,不像现在新的IDE都是自动搜索的,而且我又不会批处理orz. 这里面可 ...

  5. linux 部署轻量级堡垒机Next Terminal

    对比 类似的开源堡垒机有 Teleport,Jumpserver,Next Terminal等等. Teleport 安全性最好,较轻量,但是对被管理资产不透明,纳管前需要双向认证,在资产设备上需额外 ...

  6. C# 将实体转xml/xml转实体

    xml转实体 /// <summary> /// 把xml转换成实体 /// </summary> /// <typeparam name="T"&g ...

  7. getopt函数使用说明

    一.查询linux命令手册: #include<unistd.h> #include<getopt.h> /*所在头文件 */ int getopt(intargc, char ...

  8. liunx服务器搭建jenkins环境

    服务器搭建jenkins 持续集成环境(1)-Jenkins安装 1)安装JDK Jenkins需要依赖JDK,所以先安装JDK1.8 yum install java-1.8.0-openjdk* ...

  9. .DS_Store泄露利用过程

    .DS_Store文件泄漏利用工具: ds_store_exp.DS_Store是Mac下Finder用来保存如何展示 文件/文件夹 的数据文件,每个文件夹下对应一个. 如果开发/设计人员将.DS_S ...

  10. 三艾云 Kubernetes 集群最佳实践

    三艾云 Kubernetes 集群最佳实践 三艾云 Kubernetes 集群最佳实践 容器是 Cloud Native 的基石,它们之间的关系不言而喻.了解容器对于学习 Cloud Native 也 ...