1.简介
享元模式(Flyweight Pattern)是一种用于减少对象数量从而减少内存使用的设计模式。它的核心思想是尽可能地重用已创建的对象,而不是每次需要时都创建新的实例。这种模式尤其适用于需要大量相似对象的场景,通过共享这些对象的公共状态来节省资源。
2.通俗讲解
想象一个电子游戏,其中有许多不同类型的树木。每种树木可能有不同的颜色和形状,但它们的生长环境(比如土壤类型、阳光照射量等)对于所有树木来说都是相同的。如果我们为每棵树创建一个完整的对象,那么将会消耗大量的内存资源。而实际上,我们可以利用享元模式来优化这个问题。
3.实战
实现步骤
- 定义接口:定义一个抽象类或接口,表示所有共享对象的行为。
- 创建享元工厂:负责管理享元对象池,并根据需要返回享元对象。
- 具体享元类:实现抽象类或接口,每个类代表一个具体的享元对象。
- 客户端代码:调用享元工厂获取享元对象并使用它们。
3.1.代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义一个结构体来表示树木的内部状态
typedef struct {
char color[10];
char shape[10];
} IntrinsicState;
// 树木的抽象接口
typedef struct Tree {
IntrinsicState *state;
void (*display)(struct Tree *tree, const char *soilType);
} Tree;
// 具体的树木类型
typedef struct OakTree {
Tree base;
} OakTree;
// 具体的树木类型
typedef struct PineTree {
Tree base;
} PineTree;
// 构造函数
void oak_tree_init(OakTree *tree, IntrinsicState *state) {
tree->base.state = state;
tree->base.display = oak_tree_display;
}
void pine_tree_init(PineTree *tree, IntrinsicState *state) {
tree->base.state = state;
tree->base.display = pine_tree_display;
}
// 显示方法
void oak_tree_display(Tree *tree, const char *soilType) {
printf("Oak tree: color=%s, shape=%s, soilType=%s\n", tree->state->color, tree->state->shape, soilType);
}
void pine_tree_display(Tree *tree, const char *soilType) {
printf("Pine tree: color=%s, shape=%s, soilType=%s\n", tree->state->color, tree->state->shape, soilType);
}
// 享元工厂
typedef struct {
Tree *trees;
int count;
} FlyweightFactory;
void flyweight_factory_init(FlyweightFactory *factory) {
factory->trees = NULL;
factory->count = 0;
}
Tree *flyweight_factory_get(FlyweightFactory *factory, const char *color, const char *shape) {
IntrinsicState state = { .color = strdup(color), .shape = strdup(shape) };
for (int i = 0; i < factory->count; ++i) {
if (!strcmp(factory->trees[i].state->color, state.color) && !strcmp(factory->trees[i].state->shape, state.shape)) {
return &factory->trees[i];
}
}
// 如果不存在,则创建一个新的树木
Tree *newTree = (Tree *)malloc(sizeof(Tree));
OakTree *oak = (OakTree *)malloc(sizeof(OakTree));
oak_tree_init(oak, &state);
newTree = &oak->base;
factory->trees = realloc(factory->trees, (factory->count + 1) * sizeof(Tree));
factory->trees[factory->count++] = *newTree;
return newTree;
}
int main() {
FlyweightFactory factory;
flyweight_factory_init(&factory);
Tree *tree1 = flyweight_factory_get(&factory, "green", "round");
Tree *tree2 = flyweight_factory_get(&factory, "brown", "tall");
Tree *tree3 = flyweight_factory_get(&factory, "green", "round"); // 应该与tree1共享
tree1->display(tree1, "sand");
tree2->display(tree2, "clay");
tree3->display(tree3, "sand");
free(factory.trees);
return 0;
}
3.2.代码解析
- 我们定义了一个
IntrinsicState
结构体来存储树木的固有属性,如颜色和形状。 Tree
是一个抽象接口,它有一个display
方法用来显示树木的信息。OakTree
和PineTree
分别实现了Tree
接口。FlyweightFactory
负责管理所有树木的实例,并确保具有相同固有状态的树木共享同一个实例。
3.3.代码运行
Oak tree: color=green, shape=round, soilType=sand
Pine tree: color=brown, shape=tall, soilType=clay
Oak tree: color=green, shape=round, soilType=sand
3.4.结果分析
创建树木实例
- 在
main()
函数中,我们初始化了一个FlyweightFactory
对象factory
。 - 我们使用
flyweight_factory_get()
函数三次来尝试获取不同的树木实例:- 第一次请求一个绿色圆形的橡树 (
"green"
,"round"
). - 第二次请求一个棕色高大的松树 (
"brown"
,"tall"
). - 第三次再次请求一个绿色圆形的橡树 (
"green"
,"round"
).
- 第一次请求一个绿色圆形的橡树 (
共享实例
- 第一次和第三次请求的是同一种类型的树木,所以第二次请求会返回与第一次相同的实例,从而实现共享。
输出
当执行 display
方法时,程序将输出每棵树的具体信息,包括颜色、形状以及土壤类型。
这里可以看到,第三次输出与第一次完全相同,这是因为两次请求的是同一棵绿色圆形的橡树。这表明我们成功实现了享元模式,相同的树木实例被共享了。
4.总结
享元模式是一种旨在减少对象数量从而降低内存使用量的设计模式。它通过共享对象的公共状态来实现这一目标,特别适合于需要大量相似对象的场景。
关键点
- 对象复用:通过复用已存在的对象,而不是每次都创建新对象。
- 内外状态分离:
- 内在状态:对象的不变部分,可以被多个对象共享。
- 外在状态:对象的可变部分,通常由客户端在使用对象时提供。
- 享元工厂:管理享元对象的创建和存储,确保相同的对象只被创建一次。
适用场景
- 当系统中存在大量相似对象时。
- 当创建和销毁对象的成本较高时。
- 当对象的大部分状态可以外部化时。
优点
- 减少了对象的数量,降低了内存消耗。
- 提高了系统的性能,因为减少了对象的创建和销毁操作。
缺点
- 增加了系统的复杂度,因为需要维护一个工厂来管理共享对象。
- 需要仔细设计内外状态的分离,以确保正确性和效率。
实现注意事项
- 要确保共享对象的正确性,特别是当对象的状态可能会被多个线程访问时。
- 考虑到对象的生命周期,适时地清理不再使用的享元对象以避免内存泄漏。