簡介
享元模式(Flyweight Pattern),是一種結(jié)構(gòu)型設(shè)計模式。主要用于減少創(chuàng)建對象的數(shù)量,以減少內(nèi)存占用和提高性能。它摒棄了在每個對象中保存所有數(shù)據(jù)的方式,通過共享多個對象所共有的相同狀態(tài),讓你能在有限的內(nèi)存容量中載入更多對象。
當(dāng)程序需要生成數(shù)量巨大的相似對象時,可能對內(nèi)存有大量損耗,而對象中包含可抽取且能在多個對象間共享的重復(fù)狀態(tài),您可以采取享元模式。
內(nèi)部狀態(tài) vs. 外部狀態(tài)
內(nèi)部狀態(tài)是存儲在享元對象內(nèi)部,一般在構(gòu)造時確定或通過setter設(shè)置,并且不會隨環(huán)境改變而改變的狀態(tài),因此內(nèi)部狀態(tài)可以共享。
外部狀態(tài)是隨環(huán)境改變而改變、不可以共享的狀態(tài)。外部狀態(tài)在需要使用時通過客戶端傳入享元對象。外部狀態(tài)必須由客戶端保存。
?
作用
- 有相同的業(yè)務(wù)請求,直接返回在內(nèi)存中已有的對象,避免重新創(chuàng)建。
- 如果程序中有很多相似對象,可減少對象的創(chuàng)建,降低系統(tǒng)的內(nèi)存,使效率提高。
?
實現(xiàn)步驟
- 創(chuàng)建享元角色抽象接口,用于具體享元角色實現(xiàn)。
- 創(chuàng)建具體享元角色,實現(xiàn)抽象方法。具體享元角色就是一般類,該類可以支持外部狀態(tài)數(shù)據(jù)。
- 創(chuàng)建享元工廠,里面建一個儲存對象共享池,對已經(jīng)實例化的對象直接取出返回。
?
UML
?
?
Java代碼
?
享元抽象接口
// Flyweight.java 享元角色抽象接口 public interface Flyweight { void operate(String state); }
?
?
具體享元角色
?
// ConcreteFlyweight.java 具體享元角色,實現(xiàn)抽象接口,用于共享狀態(tài),一個類被創(chuàng)建以后就不用重復(fù)創(chuàng)建了 public class ConcreteFlyweight implements Flyweight { private String name; private String type; public ConcreteFlyweight(String name) { // 內(nèi)部狀態(tài),即不會隨著環(huán)境的改變而改變的可共享部分 // 這里的name也是對象保存的key this.name = name; this.type = "piano"; System.out.println("ConcreteFlyweight::ConcreteFlyweight(name) [創(chuàng)建具體享元" + name + "]"); } // 這里state屬于外部狀態(tài),由外部調(diào)用時傳入 // 也可以把非共享的對象傳入進來 @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) [創(chuàng)建非共享對象" + name + "]"); } // 這里state屬于外部狀態(tài),在調(diào)用時外部傳入。 @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 享元工廠,儲存一個對象共享池,已經(jīng)生成過的對象直接取出 public class FlyweightFactory { public static Map<String, Flyweight> pool = new HashMap<String, Flyweight>(); // 這里的name可以認為是內(nèi)部狀態(tài),在構(gòu)造時確定,具有唯一性。 public static Flyweight getFactory(String name) { Flyweight flyweight = pool.get(name); if (flyweight == null) { // 如果對象不存在則創(chuàng)建新的對象放入到池子里,如果已經(jīng)存在則復(fù)用前面的對象 flyweight = new ConcreteFlyweight(name); pool.put(name, flyweight); } else { System.out.println("FlyweightFactory::getFactory(name) [成功獲取具體享元" + name + "]"); } return flyweight; } }
?
測試調(diào)用
?
/** * 享元模式就是將已經(jīng)聲明過的實例或數(shù)據(jù)保存在內(nèi)存里,需要使用時則取出來,無需再次實例化和聲明。 * 通過共享多個對象所共有的相同狀態(tài),以達到節(jié)省開銷的目的。 * 享元模式分為內(nèi)部狀態(tài)和外部狀態(tài),內(nèi)部狀態(tài)基于享元對象共享,外部狀態(tài)則外部傳入或使用非享元類。 */ // 假設(shè)有鋼琴和吉他,鋼琴使用者很多需要共享實例,而吉他每次創(chuàng)建新實例 // 2個一樣名稱的為共享對象,只創(chuàng)建1個實例,后面的返回緩存實例 Flyweight factory1 = FlyweightFactory.getFactory("piano1"); // piano1已經(jīng)聲明過了,同名則共享前面的實例 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; // 享元工廠,儲存一個對象共享池,已經(jīng)生成過的對象直接取出 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); // 具體享元角色,實現(xiàn)抽象接口,用于共享狀態(tài),一個類被創(chuàng)建以后就不用重復(fù)創(chuàng)建了 typedef struct ConcreteFlyweight { // 內(nèi)部狀態(tài),即不會隨著環(huán)境的改變而改變的可共享部分 // 這里的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); // 無需共享實例的角色,用于處理外部非共享狀態(tài) // 當(dāng)不需要共享時用這樣的類 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" // 享元角色抽象基礎(chǔ)struct,相關(guān)定義在head
?
具體享元角色
?
// concrete_flyweight.c 具體享元角色,實現(xiàn)抽象接口,用于共享狀態(tài),一個類被創(chuàng)建以后就不用重復(fù)創(chuàng)建了 #include "func.h" /* 具體享元角色,實現(xiàn)抽象接口,用于共享狀態(tài),一個類被創(chuàng)建以后就不用重復(fù)創(chuàng)建了 */ // 享元對象實例化函數(shù),對象實例化后共享對象 // state屬于外部狀態(tài),由外部調(diào)用時傳入,也可以把非共享的對象傳入進來 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() 創(chuàng)建具體享元對象[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" /* 無需共享實例的角色,用于處理外部非共享狀態(tài) */ // 非共享對象的外部狀態(tài),這里state屬于外部狀態(tài),在調(diào)用時外部傳入。 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() 創(chuàng)建非共享對象[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 享元工廠,儲存一個對象共享池,已經(jīng)生成過的對象直接取出 #include "func.h" /* 享元工廠,儲存一個對象共享池,已經(jīng)生成過的對象直接取出 */ // 全局用來記錄Flyweight的對象數(shù)組 static Flyweight **flyweight_factory_member_pool; // 全局用來記錄Flyweight的名稱數(shù)組 static char **flyweight_factory_name_pool; // 全局記錄flyweight_factory的數(shù)量 static int flyweight_factory_pool_size = 0; // 這里的name可以認為是內(nèi)部狀態(tài),在構(gòu)造時確定,具有唯一性。 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; // 如果已經(jīng)存在則復(fù)用前面的對象 if (flyweight_index >= 0) { flyweight = flyweight_pool[flyweight_index]; printf("\r\n FlyweightFactory::get_factory() 成功獲取具體享元[name=%s]", name); } else { // 不存在則創(chuàng)建新的對象放入到池子里 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() 成功創(chuàng)建具體享元[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; }
?
?
測試調(diào)用
?
/** * 享元模式就是將已經(jīng)聲明過的實例或數(shù)據(jù)保存在內(nèi)存里,需要使用時則取出來,無需再次實例化和聲明。 * 通過共享多個對象所共有的相同狀態(tài),以達到節(jié)省開銷的目的。 * 享元模式分為內(nèi)部狀態(tài)和外部狀態(tài),內(nèi)部狀態(tài)基于享元對象共享,外部狀態(tài)則外部傳入或使用非享元類。 */ FlyweightFactory *flyweight_factory = flyweight_factory_constructor("flyweight_factory"); // 假設(shè)有鋼琴和吉他,鋼琴使用者很多需要共享實例,而吉他每次創(chuàng)建新實例 // // 2個一樣名稱的為共享對象,只創(chuàng)建1個實例,后面的返回緩存實例 Flyweight *factory1 = flyweight_factory->get_factory("piano1"); Flyweight *factory2 = flyweight_factory->get_factory("piano1"); // 轉(zhuǎn)換類型測試 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");
?文章來源:http://www.zghlxwxcb.cn/news/detail-409729.html
更多語言版本
不同語言實現(xiàn)設(shè)計模式:https://github.com/microwind/design-pattern文章來源地址http://www.zghlxwxcb.cn/news/detail-409729.html
到了這里,關(guān)于【享元設(shè)計模式詳解】C/Java/JS/Go/Python/TS不同語言實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!