享元模式(Flyweight),运用共享技术有效地支持大量细粒度的对象。
内部状态和外部状态:实际场景中实例对象除了几个参数外基本上都是相同的,有时就能够大幅度地减少需要实例化的类的数量。
处理外部状态:如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。
需求
制作多个类似网站,共享相同部分。
代码
业务类
c
#include <stdio.h>
#include <stdlib.h>
#include "uthash-master-src/src/uthash.h"
typedef struct User {
char *name;
} User;
User *InitUser(char *name) {
User *obj = (User *)malloc(sizeof(User));
obj->name = name;
return obj;
}
typedef struct Website {
char *type;
void (*use)(struct Website *, User *);
} Website;
// Website实例认为是共享的是内部状态,用户数据认为是外部状态
void WebsiteUse(Website *obj, User *user) {
printf("网站分类:%s 用户:%s\n", obj->type, user->name);
}
Website *InitWebsite(char *type) {
Website *obj = (Website *)malloc(sizeof(Website));
obj->type = type;
obj->use = WebsiteUse;
return obj;
}
typedef struct Hashtable {
char *type;
Website *web;
UT_hash_handle hh;
} Hashtable;
typedef struct WebsiteFactory {
Hashtable *hashtable;
Website *(*GetWebsiteCategory)(struct WebsiteFactory *, char *);
int (*GetWebsiteCount)(struct WebsiteFactory *);
} WebsiteFactory;
Website *WebsiteFactoryGetWebsiteCategory(WebsiteFactory *obj, char *type) {
Hashtable *found;
HASH_FIND_STR(obj->hashtable, type, found);
// 有相同品类的网站不直接返回,不创建新的实例
if(found != NULL) {
return found->web;
}
found = (Hashtable *)malloc(sizeof(Hashtable));
found->type = type;
found->web = InitWebsite(type);
HASH_ADD_STR(obj->hashtable, type, found);
return found->web;
}
int WebsiteFactoryGetWebsiteCount(WebsiteFactory *obj) {
return HASH_COUNT(obj->hashtable);
}
WebsiteFactory *InitWebsiteFactory() {
WebsiteFactory *obj = (WebsiteFactory *)malloc(sizeof(WebsiteFactory));
obj->hashtable = NULL;
obj->GetWebsiteCategory = WebsiteFactoryGetWebsiteCategory;
obj->GetWebsiteCount = WebsiteFactoryGetWebsiteCount;
return obj;
}
客户端
c
int main() {
WebsiteFactory *f = InitWebsiteFactory();
Website *fx = f->GetWebsiteCategory(f, "产品展示");
fx->use(fx, InitUser("小菜"));
Website *fy = f->GetWebsiteCategory(f, "产品展示");
fy->use(fy, InitUser("骄骄"));
Website *fl = f->GetWebsiteCategory(f, "博客");
fl->use(fl, InitUser("老顽童"));
Website *fm = f->GetWebsiteCategory(f, "博客");
fm->use(fm, InitUser("桃谷六仙"));
return 0;
}
尽管给四个不同用户使用网站,但实际上只有两个网站实例。
UML图

总结
- 享元模式使用场景?
- 如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;
- 对象的大多数状态是外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。
实际使用场景,我理解动态库(windows系统的dll库,linux的so库)就是享元模式思想的运用
- 享元模式让软件设计者在设计类时会思考哪些是外部状态,哪些是内部状态