• 设计模式之享元模式


    2018-09-21  10:07:30

    享元模式

      享元模式(Flyweight),运用共享技术有效的支持大量细粒度的对象。

    享元模式UML类图

    ·  

    FlyWeightFactory:用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。它的作用主要是为了创建并管理共享元对象,确保合理的共享Flyweight。

    Flyweight:所有具体享元类的公共基类,通过这个基类,Flyweight可以接收并作用于外部状态。

    SharedConcreteFlyweight(ConcreteFlyweight):实现了抽象享元类,其实例称为享元对象,在具体享元类里为内部状态提供了存储空间,通常可以结合单例模式来设计具体享元类,为每个享元类提供唯一的享元对象。大部分时候需要使用共享元对象来降低内存的损耗,但是并不是所有情况下都需要共享元,这就是UnSharedConcreteFlyweight类存在的必要性。

    UnSharedConcreteFlyweight:这个类是指那些不需要共享的对象,虽然通过共享模式的设计(Flyweight),使得对象的共享成为可能,但是它并不会强制共享。因为在客户端使用的时候必然会有不需要共享的对象。它的存在就是为了解决那些不需要共享对象的问题。

      共享元模式是和工厂模式是有些相似的,区别在于一方面共享元模式提供了对象的动态加入功能,另一方面共享元模式下客户端代码不是只能使用有共享元工厂创建的产品,它可以直接使用UnSharedConcreteFlyweight的对象。另外享元模式的根本目的是为了共享对象,减少代码实例的个数,同时也提高了代码的复用性,它在使用的时候,必然是对象之间具有某种相似性才可以,而表现出的不同之处则在于外部状态的不同。

    内部状态与外部状态

      在享元对象内部并且不会随环境的改变而改变的共享部分,可以称为是享元对象的内部状态,而随环境改变而改变的、不可以共享的状态就是外部状态了,当然享元是可以没有内部状态的,而外部状态最好由客户代码或者第三方类托管。事实上,享元模式可以避免大量非常相似类的开销。在程序设计找那个,有时需要生成大量细粒度的类实例来表示数据。如果发现这些实例除了几个参数外基本上都是相同的,有时就能大幅度的减少需要实例化的类的数量。如果能把那些参数移到类实例的外部,在方法调用时将它们(外部状态)传递进来,就可以通过共享大幅度的减少单个实例的数目。

    享元模式的优缺点

    优点:

      1.通过共享对象节约了内存资源降低了性能消耗(有些情况下,对象的数量太多,会导致运行时的资源与性能损耗)

      2.享元模式的内部状态稳定不变,外部状态则随环境而改变且不会影响内部状态,而外部状态的不同又是的享元对象得出不同的运算的结果,这使得共享对象成为了一种可能

    缺点:

      1.享元模式需要维护一个记录了系统已有的所有享元的列表,这本身也需要消耗资源,在每一次使用享元使都需要进行一次查找,这降低了程序的运行效率。

      2.享元模式使得系统更加的复杂,因为你需要抽离出享元的内部状态和外部状态,享元在不同环境下的外部状态都不相同,这使得程序的逻辑复杂度提升。

    适用场景:

      1.在对象(相同或相似的对象)足够多的时候,而这些对象造成了很大的存储开销时应该考虑使用享元模式,还有就是对象的大多数状态可以抽离为外部状态,如果删除对象的外部状态,那么可以用相对较小的共享对象取代很多组对象,此时也可以考虑使用共享模式。

      2.享元必须是被大量使用的才有使用享元模式的价值,因为,在使用享元模式的时候需要维护一个所有已经存在的享元的key-value数据结构,这本身也是需要消耗资源的。

    代码示例

      问题模型:围棋系统,在一个围棋系统中,有黑子、白子、还有棋盘、装棋子棋盒。其中黑子181个,白子180个,总共361个棋子,在下棋的过程中,每个棋子都会有自己的一个坐标。棋盘总共有384格,如果你为每个实例化一个棋子对象,那么将由384个棋子对象。这里我们可以使用享元模式。接下来分析内部和外部状态。对棋子、棋盘、棋盒,它们都有固定的颜色,这个是不会改变的,所以颜色是内部状态。标准双人模式下,棋盒只有两个,棋盘一个,所以它们没有共享的意义(但是它们相对于界面来说,依然有个显示坐标的问题,所以外部变量对他们依然成立)。对棋子来说,每个对象的坐标就是外部模式,根据坐标的不同,棋子可以显示在棋盘上不同的位置。

    1.抽象享元基类(Flyweight)

    #ifndef FLYWEIGHT_H_
    #define FLYWEIGHT_H_
    
    #include <iostream>
    #include <string>
    
    class Flyweight
    {
    public:
        virtual void display(const int iX,const int iY) = 0;
        Flyweight() = default;
        virtual ~Flyweight() = default;
    protected:
        std::string m_strColor;
    };
    #endif
    Flyweight

    2.共享类(SharedFlyweight)

    #ifndef SHAREDFLYWEIGHTWHITE_H_
    #define SHAREDFLYWEIGHTWHITE_H_
    #include "Flyweight.h"
    
    class SharedConcreteFlyweightWhite : public Flyweight
    {
    public:
        void display(const int iX,const int iY) override;
        SharedConcreteFlyweightWhite()
        {
        m_strColor = "White";
        }
        ~SharedConcreteFlyweightWhite() = default;
    };
    #endif
    
    #include "SharedFlyweightWhite.h"
    
    void SharedConcreteFlyweightWhite::display(const int iX,const int iY)
    {
        std::cout << " I am a " << m_strColor << "chess pieces,my coordinate is (" << iX << "," << iY << ")." << std::endl;
    }
    
    #ifndef SHAREDFLYWEIGHTBLACK_H_
    #define SHAREDFLYWEIGHTBLACK_H_
    
    #include "Flyweight.h"
    
    class SharedConcreteFlyweightBlack : public Flyweight
    {
    public:
       void display(const int iX,const int iY) override;
       SharedConcreteFlyweightBlack()
       {
        m_strColor = "Black";
       }
       ~SharedConcreteFlyweightBlack() = default;
    };
    
    #endif
    
    #include "SharedFlyweightBlack.h"
    
    void SharedConcreteFlyweightBlack::display(const int iX,const int iY)
    {
        std::cout << "I am a black Chess,my coordinate is (" << iX << "," << iY << ")" << std::endl;
    }
    SharedFlyweight

    3.非共享的享元类(UnsharedFlyweight)

    #ifndef UNSHAREDCONCRETEFLYWEIGHTCHESSBOX_H_
    #define UNSHAREDCONCRETEFLYWEIGHTCHESSBOX_H_
    
    #include "Flyweight.h"
    
    class UnsharedConcreteFlyweightChessbox : public Flyweight
    {
    public:
        void display(const int iX,const int iY) override;
        UnsharedConcreteFlyweightChessbox()
        {
        m_strColor = "Yellow";
        }
        ~UnsharedConcreteFlyweightChessbox() = default;
    };
    #endif 
    
    #include "UnsharedConcreteFlyweightChessBox.h"
    
    void UnsharedConcreteFlyweightChessbox::display(const int iX,const int iY)
    {
        std::cout << "I am a " << m_strColor << " chessbox,my coordinate is (" << iX << "," << iY << ")" <<std::endl;
    }
    UnsharedFlyweight

    4.Flyweight(享元模式的核心部件)

    #ifndef FLYWEIGHTFACTORY_H_
    #define FLYWEIGHTFACTORY_H_
    
    #include "Flyweight.h"
    #include "SharedFlyweightWhite.h"
    #include "SharedFlyweightBlack.h"
    const std::string BLACKCHESS = "Black";
    const std::string WHITECHESS = "White";
    #include <map>
    class FlyweightFactory
    {
    private:
        std::map<std::string,Flyweight*> m_mapFlyweight;
    public:
        Flyweight* getFlyweight(const std::string strKey);
        int getFlyweightCount()
        {
        return m_mapFlyweight.size();
        }
    };
    #endif
    
    #include "FlyweightFactory.h"
    
    // strKey is defined class name or surname of class 
    Flyweight* FlyweightFactory::getFlyweight(const std::string strKey)
    {
        //if find return FlyWeight object whose key is equal,otherwise new object and insert into map
        if(m_mapFlyweight.end() != m_mapFlyweight.find(strKey))
        return m_mapFlyweight[strKey];
        if(strKey == BLACKCHESS)
        {
            auto pointer = new SharedConcreteFlyweightBlack;
        m_mapFlyweight[strKey] = pointer;
            return pointer;
        }
        else if(strKey == WHITECHESS)
        {
            auto pointer  = new SharedConcreteFlyweightWhite;
            m_mapFlyweight[strKey] = pointer;
            return pointer;
        }
        else
        {
        std::cout << "The key is Error!" << std::endl;
          return nullptr;
        }
    }
    Flyweight

    5.Client

    #include "FlyweightFactory.h"
    #include "UnsharedConcreteFlyweightCheckerboard.h"
    #include "UnsharedConcreteFlyweightChessBox.h"
    
    using namespace std;
    
    int main(int argc,char *argv[])
    {
        FlyweightFactory objFactory;
        auto objBlack = objFactory.getFlyweight(BLACKCHESS);
        if(nullptr != objBlack)
        objBlack->display(3,5);
        auto objBlack1 = objFactory.getFlyweight(BLACKCHESS);
        if(nullptr != objBlack1)
        objBlack1->display(1,4);
        std::cout << "count "<< objFactory.getFlyweightCount() << std::endl;
       
        auto objWhite = objFactory.getFlyweight(WHITECHESS);
        if(nullptr != objWhite)
        objWhite->display(9,9);
        std::cout << "count:" << objFactory.getFlyweightCount() << std::endl;
        auto objWhite1 = objFactory.getFlyweight(WHITECHESS);
        if(nullptr != objWhite1)
        objWhite1->display(8,8);
        std::cout << "count: "<< objFactory.getFlyweightCount() << std::endl;
        UnsharedConcreteFlyweightChessbox  unshChessbox;
        unshChessbox.display(1,2);
        std::cout <<"count:" << objFactory.getFlyweightCount() << std::endl;
        return(1);
    }
    Client
  • 相关阅读:
    Bootstrap
    格式化字符串
    闭包函数与装饰器
    正则表达式
    jQuery
    分布式-锁-1.1 多线程锁无法满足的场景
    effective python 读书笔记-第22条: 尽量用辅助类来维护程序的状态,而不要用字典
    effective python 读书笔记:第21条-用只能以关键字形式指定的参数来确保代码明晰
    effective python 读书笔记:第20条-用None和文档字符串来描述具有动态默认值的参数
    git如何将上游(upstream)新建分支(origin没有)导入到origin中?
  • 原文地址:https://www.cnblogs.com/ToBeExpert/p/9686960.html
Copyright © 2020-2023  润新知