资讯详情

S15. 享元模式

享元模式

享元模式是一种结构设计模式, 它允许你在消耗少量内存的情况下支持大量对象。

简单理解: 一类成员很多,创建这个对象消耗资源,需要在实际场景中反复创建和销毁。消耗的内存更大。

如果此时设计了一个对象池,以缓存某个对象,软件在使用时应用,不需要时回收。可以重复使用对象,并多次创建和销毁对象。

意义

从以上解释可以得出结论,元享受模式主要限制了在资源有限的情况下创建大量对象的行为。通过对现有对象的初始化,达到与创建对象相同的效果。

应用场景

目前开发板只有三个Led灯,其中一个被用作Power指示灯,另外两个可用作软件调试或功能灯,设计Led避免使用资源管理Led发生冲突。(消耗的内存不方便演示,这里使用Led资源消耗模拟内存消耗)

Led资源

分析

板子Led资源有限,其中一个被指定为Power灯不能占用,只剩下两个Led可用。软件可分为UnShare Led 和 Share Led,假设两个Shared Led分别为Led 1和Led2。

如果不对Led资源管理可能发生以下情况:

  • 使用各种模块Led目前还不清楚Led是否使用。两个模块很可能同时使用。Led 1,导致Led 1显示紊乱,不符合预期。
  • 被用作Power指示灯的Led其他模块不允许使用,但在实际情况下,一些模块可能会被误用Power 指示灯。

因为Led操作相同,但具体资源不同。根据现有的享元模式,可以引用享元模式Led创造几个资源Led对象放入对象工厂,每个模块都可以从对象工厂申请。

类图

结合上述分析,结合享元模式构建类图:

Led管理类图
  • CLed: Led功能通用接口类。
  • CShareLed、CUnsharedLed: 具体Led实现功能类。
  • CledFactory: 管理当前Led工厂资源类模块通过工厂获取Led资源。
  • Client: 客户代码。

源码实现

  1. 编译环境: Linux环境
  2. 语言: C 语言
  3. 编译命令: make

Flyweight/ ├── led.cc ├── led_factory.cc ├── led_factory.h ├── led.h ├── main.cc └── Makefile
  • led: Led实现通用功能。
  • led_factory: led实现资源管理工厂。
  • Makefile: 编译工具。
  • main: 客户代码。

class CLed { public:     CLed () {}     virtual ~CLed() {}     void SetStatus(ELedStatus status);     ELedStatus  GetStatus();     void SetUsedMode(EUsedMode mode);     EUsedMode  GetUsedMode();     void SetPermission(EPermission permission);     EPermission  GetPermission();     void SetFrequency(int freq);     void SetIndex(int value);     void UpdateLed(EPermission permission, EUsedMode mode, int freq);     virtual void Start();     virtual void Stop();     virtual void Relase();  private:     int         mIndex;     int         mFreq;     ELedStatus  mStatus;     EUsedMode   mUsedMode;     EPermission mPermission; };

这类主要实现Led一般功能不涉及具体功能Led管脚等硬件信息。

class CLedFactory { public:     static CLedFactory* GetInstance();      CLed* GetLed(EPermission permission, EUsedMode mode = LED_MODE_DEFAULT, int freq = 10);  private:     std::map<int, CLed *> mLedTable;///Led享元对象     CLedFactory() {}                ///< 单例模式     ~CLedFactory() {} };

主要用于缓存Led目前只缓存了三个对象: Unshare Led、Share Led(Led1、Led2)。通过客户端GetLed接口未使用Led资源。

CLed* CLedFactory::GetLed(EPermission permission, EUsedMode mode, int freq) {     int i, sum = 0, location = -1;      switch (permission)     {         case LED_UNSHARED:         {             // 返回Power Led对象             if (CUnsharedLed::GetInstance()->GetStatus() == LED_IDLE) {                 CUnsharedLed::GetInstance()->SetStatus(LED_BUSY);                 return CUnsharedLed::GetInstance();             } else {                 return NULL;             }         }         break;          case LED_SHARED:         {             sum = mLedTable.size();             // loop: 遍历所有LED状态,返回空闲的LED             for (i = 0; i < sum; i  ) {                 if ( mLedTable[i]->GetStatus() == LED_IDLE)                 {                     location = i;                     goto RET;       // 找到空闲的LED, 返回当前下标                 }             }              // 当前存在的LED对象都处于busy状态,             // 若Led如果资源未达到上限,将创造新的资源Led对象。             if (i == sum && sum < MAX_SHARED_LED) {                 FACTORY_LOGD("Create Shared Led %d!\n", i);                 mLedTable.insert(std::pair<int, CLed*>(i, new CSharedLed()));                 location = i;             }         }         break;          default:         break;     }  RET:     if (location >= 0) {         mLedTable[location]->UpdateLed(permission, mode, freq);         return mLedTablelocation];
    } else {
        return NULL;
    }
}
  • 在用户申请Led资源时,先从对象缓存池遍历是否存在可用Led对象,返回给用户。
  • 若对象池缓存的Led对象都为busy,判断对象池Led对象个数是否等于Led资源个数。若小于,继续创建Led对象返回给用户,并加入对象池。
  • 若对象池中Led对象都busy,且对象池中Led对象个数等于Led资源个数。返回用户无可用的Led对象。

int main(int argc, char *argv[])
{
    CLedFactory *theLedFactory = CLedFactory::GetInstance();
    if (!theLedFactory) {
        MAIN_LOGE("Get Led Factory failed!\n");
        return -1;
    }

    // ------------------- UnShare Led Test ------------------------
    MAIN_LOG("-> UnShare led test 1st!\n");
    CLed *thePowerLed1 = theLedFactory->GetLed(LED_UNSHARED);
    if (thePowerLed1) {
        thePowerLed1->Start();
    } else {
        MAIN_LOGE("Get Power Led failed!\n");
    }

    thePowerLed1->Relase(); // 当前使用释放,才可被再次使用

    MAIN_LOG("\n-> UnShare led test 2nd!\n");
    CLed *thePowerLed2 = theLedFactory->GetLed(LED_UNSHARED);
    if (thePowerLed2) {
        thePowerLed2->Start();
    } else {
        MAIN_LOGE("Get Power Led failed!\n");
    }
    // -------------------      Test End    ------------------------

    // -------------------  Share Led Test  ------------------------
    MAIN_LOG("\n-> Share led test 1st!\n");
    CLed *theLed1 = theLedFactory->GetLed(LED_SHARED, LED_MODE_HORSE);
    if (theLed1) {
        theLed1->Start();
    } else {
        MAIN_LOGE("Get Led failed!\n");
    }

    MAIN_LOG("\n-> Share led test 2nd!\n");
    CLed *theLed2 = theLedFactory->GetLed(LED_SHARED, LED_MODE_BREATH);
    if (theLed2) {
        theLed2->Start();
    } else {
        MAIN_LOGE("Get Led failed!\n");
    }

    // Share Led 仅有两个,若不释放占用的Led。Led3就拿不到资源
    MAIN_LOG("\n-> Share led test 3rd!\n");
    CLed *theLed3 = theLedFactory->GetLed(LED_SHARED, LED_MODE_BREATH);
    if (theLed3) {
        theLed3->Start();
    } else {
        MAIN_LOGE("Get Led failed!\n");
    }
    // -------------------      Test End    ------------------------

    return 0;
}
  • 对Unshare Led测试: 在thePowerLed1被释放后才可被thePowerLed2申请使用。
  • 对Share Led测试: 仅存在两个Led对象,都被占用时,再申请会返回失败。

测试效果

$ ./exe 
-> UnShare led test 1st!
Power Led (pin10) ON. 
Power Led (pin10) OFF.
Relase power Led (pin10).

-> UnShare led test 2nd!
Power Led (pin10) ON. 

-> Share led test 1st!
Led 0 (pin20) Start. Mode: Horse Mode 

-> Share led test 2nd!
Led 1 (pin21) Start. Mode: Breath Mode 

-> Share led test 3rd!
74 Main E: Get Led failed!

总结

  • 的实现方式主要是,创建一定个数的对象放到对象池缓存。当用户需要使用时,从对象池申请;当用户不再使用时,回收至对象池。

  • 在《设计模式》中指出,可使用在类变量过多,反复创建/销毁会消耗资源的场景下。但是在笔者思考后发现,也可以用于对共享资源的管理上,于是有了本文。

  • 在其他大佬总结中,很少看到代码中有回收动作。感觉是一个对象可以同时被多个模块使用,只是在使用的时候初始化一下对象内部状态。那么这种做法,难道不会影响上个使用此对象还在运行的模块吗?至少对于本篇Led的使用会有影响,例如上个模块还在呼吸灯模式,下个模块转换成跑马灯模式,就会影响到呼吸灯(当然跑马灯需要多个Led,这里仅是指一种模式)。

标签: 4394s15ac接近传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台