设计模式专题之C语言-享元模式

1.简介

享元模式(Flyweight Pattern)是一种用于减少对象数量从而减少内存使用的设计模式。它的核心思想是尽可能地重用已创建的对象,而不是每次需要时都创建新的实例。这种模式尤其适用于需要大量相似对象的场景,通过共享这些对象的公共状态来节省资源。

2.通俗讲解

想象一个电子游戏,其中有许多不同类型的树木。每种树木可能有不同的颜色和形状,但它们的生长环境(比如土壤类型、阳光照射量等)对于所有树木来说都是相同的。如果我们为每棵树创建一个完整的对象,那么将会消耗大量的内存资源。而实际上,我们可以利用享元模式来优化这个问题。

3.实战

实现步骤

  1. 定义接口:定义一个抽象类或接口,表示所有共享对象的行为。
  2. 创建享元工厂:负责管理享元对象池,并根据需要返回享元对象。
  3. 具体享元类:实现抽象类或接口,每个类代表一个具体的享元对象。
  4. 客户端代码:调用享元工厂获取享元对象并使用它们。

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 方法用来显示树木的信息。
  • OakTreePineTree 分别实现了 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.结果分析

创建树木实例

  1. main() 函数中,我们初始化了一个 FlyweightFactory 对象 factory
  2. 我们使用 flyweight_factory_get() 函数三次来尝试获取不同的树木实例:
    • 第一次请求一个绿色圆形的橡树 ("green", "round").
    • 第二次请求一个棕色高大的松树 ("brown", "tall").
    • 第三次再次请求一个绿色圆形的橡树 ("green", "round").

共享实例

  • 第一次和第三次请求的是同一种类型的树木,所以第二次请求会返回与第一次相同的实例,从而实现共享。

输出

当执行 display 方法时,程序将输出每棵树的具体信息,包括颜色、形状以及土壤类型。

这里可以看到,第三次输出与第一次完全相同,这是因为两次请求的是同一棵绿色圆形的橡树。这表明我们成功实现了享元模式,相同的树木实例被共享了。

4.总结

享元模式是一种旨在减少对象数量从而降低内存使用量的设计模式。它通过共享对象的公共状态来实现这一目标,特别适合于需要大量相似对象的场景。

关键点

  • 对象复用:通过复用已存在的对象,而不是每次都创建新对象。
  • 内外状态分离:
    • 内在状态:对象的不变部分,可以被多个对象共享。
    • 外在状态:对象的可变部分,通常由客户端在使用对象时提供。
    • 享元工厂:管理享元对象的创建和存储,确保相同的对象只被创建一次。

适用场景

  • 当系统中存在大量相似对象时。
  • 当创建和销毁对象的成本较高时。
  • 当对象的大部分状态可以外部化时。

优点

  • 减少了对象的数量,降低了内存消耗。
  • 提高了系统的性能,因为减少了对象的创建和销毁操作。

缺点

  • 增加了系统的复杂度,因为需要维护一个工厂来管理共享对象。
  • 需要仔细设计内外状态的分离,以确保正确性和效率。

实现注意事项

  • 要确保共享对象的正确性,特别是当对象的状态可能会被多个线程访问时。
  • 考虑到对象的生命周期,适时地清理不再使用的享元对象以避免内存泄漏。
  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

甜航一直在

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值