简单易懂23种设计模式——享元模式【含C++代码实例】

   在做面向对象的软件开发时我们往往想达到更高的代码可复用性和更合理的软件颗粒度。
  根据《设计模式——可复用面向对象软件的基础》所说:“你必须找到相关的对象,以适当的颗粒度将他们回归类,再定义类的接口和继承层次,建立对象之间的基本关系。你的设计应该对手头的问题有针对性,同时对将来的问题和需求也要有足够的通用性。
  内行的设计者知道:不是解决任何问题都要从头做起。他们更愿意复用以前使用的解决方案。这些重复的模式方案解决特定的问题,使得面向对象的设计更灵活、优雅,最终复用性更好。它们帮助设计者将新的设计建立在以往的工作基础上,复用以往的成功设计方案。一个熟悉这些设计模式的设计者不需要再去发现它们,而能够立即将他们应用于设计问题中。
  本系列文章主要参考文献为——设计模式,可复用面向对象软件的基础(Design Patterns Elements of Reusable Object-Oriented SoftWare Erich.),内部代码基本用C++语言编写。
  汇总链接:23种设计模式C++实现——概要(索引汇总)

摘要

   本章主要说明享元模式,该设计模式主要意图是:共享相同元素,不必为相同的元素new大量的实例,在需要时调取该对象即可,
   享元模式本文中会介绍两种,一种是单纯享元,一种是复合享元,这两种享元模式的区别是,单纯享元的特点是所有元素都是共享的,而复合享元角色对象是不共享的,但是一个复合享元内部对象可以划分为多个共享享元表示;
   具体实现下边我们就通过以下的小栗子来说明什么是享元模式。

主要参与者

该设计模式的参与者有4个,分别是:

  1. Flyweight 抽象享元接口
  2. ConcreteFlyWeight 抽象享元接口单纯享元的实现,该对象时可共享的,切储存状态必须是内部的
  3. ComplexFlyWeight 抽象享元接口复合享元的实现,该对象不可共享,但是其内部的享元元素可共享
  4. FlyWeightFactory 创建管理享元对象
  5. Client 用户

优点

  1. 空间上节省开销
  2. 更方便的垃圾回收和引用计数,当一个享元元素不在使用时可以回收它的存储空间

具体实现代码

   单纯享元例子以共享的工具箱为例,工具箱内可以放置多个型号的扳手,当需要用大扳手时我们会买一个放在工具箱内,需要中号或小号的扳手时再买小号、中号的扳手放在工具箱内,如果朋友需要大号的扳手则可以直接从工具箱中拿出使用,但他需要用中号或小号扳手时,就需要先去五金店买一把中号或小号扳手放在工具箱内。这样其他人在使用扳手时直接从工具箱内拿出来使用就可以。
   复合享元以私人工具箱为例,工具箱内可以放置多个型号的扳手,但是由于扳手总是丢失,所以每个人都有私人工具箱且不可外借,这个扳手不管你是装配还是维修都可以使用,如果没有合适的扳手需要自己去五金店买。

抽象享元的接口(Wrench)

/****************************************************************
 Author :   BingLee
 Date   :   2019-08-20
 Info   :	complexflyweight.h
 https://blog.csdn.net/Bing_Lee (C)All rights reserved.
******************************************************************/
#ifndef COMPLEXFLYWEIGHT_H
#define COMPLEXFLYWEIGHT_H

#include <stdio.h>
#include <string>
#include <map>
#include <list>
#include <QMap>

#define USE_QMAP
using namespace std;
//抽象享元的接口(Wrench)
class Wrench
{
public:
    virtual void Operation(string str = 0) = 0;
};

单纯享元的实现(ConcreteWrench)

//单纯享元的实现(ConcreteWrench)
class ConcreteWrench : public Wrench
{
public:
    ConcreteWrench(string size){
        m_wrenchSize = size;
    }
    void Operation(string user){
        printf("Wrench Size = %s \n", m_wrenchSize.c_str());
        printf("User = %s \n", user.c_str());
    }
protected:
    string m_wrenchSize;
};

复合享元的实现(PersonalWrenchBox)

class PersonalWrenchBox : public Wrench
{
public:
    void Add(string size, Wrench *flyweight){
#ifndef USE_QMAP
        // m_wrenchBox.insert(pair<string, Wrench*> (size, flyweight));
        // 切换如下方式赋值
        m_wrenchBox[size] = flyweight;
#else
        m_wrenchBox.insert(size, flyweight);
#endif
    }
    void Operation(string user){
#ifndef USE_QMAP
        map<string, Wrench*>::iterator iter;
        for(iter = m_wrenchBox.begin(); iter != m_wrenchBox.end(); iter++)
        {
            iter->second->Operation(user);
        }
#else
        QMap<string, Wrench*>::iterator iter;
        for(iter = m_wrenchBox.begin(); iter != m_wrenchBox.end(); iter++)
        {
            iter.value()->Operation(user);
        }
#endif
    }
private:
#ifndef USE_QMAP
    map<string, Wrench*> m_wrenchBox;
#else
    QMap<string, Wrench*> m_wrenchBox;
#endif
};

享元管理对象(ToolShop)

//五金店,可以在这里买到各种工具
class ToolShope
{
public:
    Wrench *Factory(list<string> compositeState)
    {
        PersonalWrenchBox *complexFlyWeight = new PersonalWrenchBox();
        foreach (string size , compositeState)
        {
            complexFlyWeight->Add(size, this->Factory(size));
        }
        return complexFlyWeight;
    }
    Wrench *Factory(string size){
        //先从缓存中查找对象
        Wrench *fly = m_shareWrenchBox[size];
        if(fly == NULL){
            //如果对象不存在则创建一个新的Flyweight对象
            fly = new ConcreteWrench(size);
            //把这个新的Flyweight对象添加到缓存中
#ifndef USE_QMAP
			// 这里发现使用insert时会新建一个对象,有兴趣的同学可以研究下具体是什么原因。
            // m_shareWrenchBox.insert(pair<string, Wrench*> (size, fly));
            // 验证 @嗨氏令牌 和 @小乐子531 所说内容,切换为如下方法结果符合预期。
            m_shareWrenchBox[size] = fly;
#else
            m_shareWrenchBox.insert(size, fly);
#endif
        }
        return fly;
    }
private:
#ifndef USE_QMAP
    map<string, Wrench*> m_shareWrenchBox;
#else
    QMap<string, Wrench*> m_shareWrenchBox;
#endif

};
#endif // COMPLEXFLYWEIGHT_H

用户(Client)

/****************************************************************
 Author :   BingLee
 Date   :   2019-08-20
 Info   :	complexflyweight_client.cpp
 https://blog.csdn.net/Bing_Lee (C)All rights reserved.
******************************************************************/
#include <QCoreApplication>
#include "complexflyweight.h"
#include <list>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    //单纯享元
    ToolShope *flyweightFactory1 = new ToolShope;
    Wrench *smallWrench = flyweightFactory1->Factory("Small");
    smallWrench->Operation("Mike");
    Wrench *mediumWrench = flyweightFactory1->Factory("Medium");
    mediumWrench->Operation("Sara");
    Wrench *largeWrench = flyweightFactory1->Factory("Large");
    largeWrench->Operation("Steven");
    Wrench *smallWrench1 = flyweightFactory1->Factory("Small");
    smallWrench1->Operation("Jane");
    printf("-------------------------------\n");
    printf("Simple FlyWeight Obj is same?\n");
    // 增加指针地址打印
    printf("smallWrench address: {%p}\n", smallWrench);
    printf("mediumWrench address: {%p}\n", mediumWrench);
    printf("largeWrench address: {%p}\n", largeWrench);
    printf("smallWrench1 address: {%p}\n", smallWrench1);
    if(smallWrench == smallWrench1)
        printf("Is same.\n");
    else
        printf("Is different.\n");
    printf("-------------------------------\n");

    //复合享元
    list<string> compositeState;
    compositeState.push_back("Small");
    compositeState.push_back("Medium");
    compositeState.push_back("Large");
    compositeState.push_back("Small");
    compositeState.push_back("Medium");
    ToolShope *complexFlyWeightFactory = new ToolShope();
    Wrench *complexfly1 = complexFlyWeightFactory->Factory(compositeState);
    Wrench *complexfly2 = complexFlyWeightFactory->Factory(compositeState);
    complexfly1->Operation("Mike");
    complexfly2->Operation("Sara");
    printf("-------------------------------\n");
    printf("Complex FlyWeight Obj is same?\n");
    if(complexfly1 == complexfly2)
        printf("Is same.\n");
    else
        printf("Is different.\n");
    printf("-------------------------------\n");
    return a.exec();
}

输出结果:

修改后结果
补充内存地址打印。

Wrench Size = Small 
User = Mike 
Wrench Size = Medium 
User = Sara 
Wrench Size = Large 
User = Steven 
Wrench Size = Small 
User = Jane 
-------------------------------
Simple FlyWeight Obj is same?
smallWrench address: {0000017ab0271a00}
mediumWrench address: {0000017ab0271ae0}
largeWrench address: {0000017ab0271b40}
smallWrench1 address: {0000017ab0271a00}
Is same.
-------------------------------
Wrench Size = Large
User = Mike
Wrench Size = Medium
User = Mike
Wrench Size = Small
User = Mike
Wrench Size = Large
User = Sara
Wrench Size = Medium
User = Sara
Wrench Size = Small
User = Sara
-------------------------------
Complex FlyWeight Obj is same?
Is different.
-------------------------------

补充说明

   该模式的思想是对于相同的对象共享使用,单纯享元是开放式共享,所有对象均可以分享使用;复合享元是区分式共享,比如说同一小组或好朋友可以共享,陌生人不共享,不同小组之间不共享;这样解释不知道是否可以理解到—“对象不可共享,但是其内部的享元元素可共享”的内涵。
   代码使用Qt编写但是本地的C++库好似有些问题,STL map类使用有些问题,所以改用qmap实现,文中代码也写了map的函数可根据宏定义来选择;如果需要专栏所有设计模式的源代码请留言留下你的联系方式,若能点赞、关注就更棒了!!!

本篇博客中的代码均已通过编译,如有Bug,请提出宝贵意见~

注:文章内函数可以把除Client对象放在一个头文件中,Client文件放在主函数中执行。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

机器人梦想家

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

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

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

打赏作者

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

抵扣说明:

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

余额充值