简介

享元形式(Flyweight Pattern),是一种结构型规划形式。主要用于削减创立目标的数量,以削减内存占用和进步功能。它摒弃了在每个目标中保存一切数据的方式,经过同享多个目标所共有的相同状况,让你能在有限的内存容量中载入更多目标。

当程序需求生成数量巨大的类似目标时,或许对内存有大量损耗,而目标中包含可抽取且能在多个目标间同享的重复状况,您能够采取享元形式。

内部状况 vs. 外部状况 内部状况是存储在享元目标内部,一般在结构时确认或经过setter设置,而且不会随环境改动而改动的状况,因此内部状况能够同享。 外部状况是随环境改动而改动、不能够同享的状况。外部状况在需求运用时经过客户端传入享元目标。外部状况必须由客户端保存。

效果

  1. 有相同的业务恳求,直接回来在内存中已有的目标,避免重新创立。
  2. 假如程序中有许多类似目标,可削减目标的创立,降低体系的内存,使功率进步。

完成过程

  1. 创立享元人物笼统接口,用于详细享元人物完成。
  2. 创立详细享元人物,完成笼统方法。详细享元人物就是一般类,该类能够支持外部状况数据。
  3. 创立享元工厂,里边建一个贮存目标同享池,对现已实例化的目标直接取出回来。

UML

【享元设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

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");

更多言语版别

不同言语完成规划形式:github.com/microwind/d…