本篇文章记录享元模式。
享元(Flyweight)模式也称蝇量模式,是一种结构型模式,解决的是面向对象程序设计的性能问题。所谓享元就是被共享的单元或被共享的对象,其英文名 Flyweight 是“轻量的性能问题。其设计思想是:当需要某个对象时,尽量共用已经创建出的同类对象,从而避免频繁使用new创建同类或者相似的对象;在同类对象数量非常多的情况下,可以达到节省内存占用以及提升程序运行效率的目的。蝇量这个词也同样表示通过减少不必要的对象创建以减小系统运行时的负荷。

从一个典型的范例开始

在这里插入图片描述
围棋有19*19个格子,一共361个交叉点,本例子中只绘制棋子,代码如下:

    enum class Color
    {
        Black,
        White
    };

    struct Position
    {
        Position(int x, int y) : x(x),y(y)
        {

        }
        int x;
        int y;
    };

    class ChessPiece
    {
    public:
        ChessPiece(Color color, Position position)
            : m_color(color)
            ,m_position(position)
        {
        }

        void draw()
        {
            cout << "位置:" << m_position.x << "  " << m_position.y<< "   颜色:" << (m_color == Color::Black ?  "黑色" : "白色") << endl;
        }

    private:
        Color m_color;
        Position m_position;
    };

    void test()
    {
        ChessPiece* p1 = new ChessPiece(Color::Black, Position(1, 1));
        p1->draw();
        ChessPiece* p2 = new ChessPiece(Color::Black, Position(2, 2));
        p2->draw();
        ChessPiece* p3 = new ChessPiece(Color::White, Position(3, 3));
        p3->draw();
        ChessPiece* p4 = new ChessPiece(Color::White, Position(4, 4));
        p4->draw();
        /*
        位置:1  1   颜色:黑色
        位置:2  2   颜色:黑色
        位置:3  3   颜色:白色
        位置:4  4   颜色:白色
        */
}	

随着棋局不断进行,不难想象,每落下一颗棋子,就要创建一个ChessPiece 对象,程序中将会创建越来越多的ChessPiece对象,而这些对象之间除了颜色和位置不同,其余的都相同。这样看起来,只需要创建一颗代表白色和黑色的棋子就行,借助这两颗棋子来表示其他棋子,这就是享元模式的设计思想,代表黑色棋子和白色棋子的对象称为享元对象。
下面使用享元模式改造上面的代码:

    enum class Color
    {
        Black,
        White
    };

    struct Position
    {
        Position(int x, int y) : x(x), y(y)
        {

        }
        int x;
        int y;
    };

    class ChessPiece
    {
    public:
        virtual ~ChessPiece() // 虚析构
        {

        }
        virtual void draw(Position pos) = 0;
    };

    class BlackPiece : public ChessPiece
    {
    public:
        virtual void draw(Position pos) override
        {
            cout << "BlackPiece" << pos.x << "," << pos.y << endl;
        }
    };
    class WhitePiece : public ChessPiece
    {
    public:
        virtual auto draw(Position pos) -> void override
        {
            cout << "WhitePiece" << pos.x << "," << pos.y << endl;
        }
    };

    class PieceFactory
    {
    public:
        virtual ~PieceFactory()
        {
            for (auto it = m_map.begin(); it != m_map.end(); ++it)
            {
                delete it->second;
            }
        }

        // 获取蝇量,根据颜色值来获取
        ChessPiece* getFlyWeight(Color color)
        {
            std::map<Color, ChessPiece*>::iterator it = m_map.find(color);
            if (it != m_map.end())
            {
                return it->second;
            }
            else
            {
                ChessPiece* tmp = (color == Color::Black ? static_cast<ChessPiece*>(new BlackPiece()) : static_cast<ChessPiece*>( new WhitePiece()));
                m_map[color] = tmp;  // 将新创建的对象存储到享元池中
                return tmp;          // 返回新创建的对象
            }
        }
    private:
        std::map<Color, ChessPiece*> m_map; // 享元池
    };

    void test()
    {
        PieceFactory* factory = new PieceFactory();
        ChessPiece* p1 = factory->getFlyWeight(Color::Black);
        p1->draw(Position(1, 1));
        ChessPiece* p2 = factory->getFlyWeight(Color::White);
        p2->draw(Position(2, 2));
        /*
            BlackPiece1,1
            WhitePiece2,2
         */
        delete factory;
    }

上边的代码使用Map来保存黑色和白色棋子,这个map称为享元池。

引入享元(Flyweight)模式

该模式避免了程序中出现大量相同或者相似的对象,通过共享对象的方式实现相似对象的重用。
定义: 运用共享技术有效地支持大量细粒度的对象(的复用)。
**内部状态:**存储在享元对象内部,一直不会发生改变的状态。这种状态可以被共享。
**外部状态:**随着外部环境和各种动作因素的改变而发生改变的状态,这种状态不可以被共享。
**享元模式的目的:**减少对象数量,节省内存,提高程序运行效率。
在这里插入图片描述
享元模式的三种角色:
1 抽象享元类:ChesePiece类,通常是一个接口或者抽象类。
2 具体享元类:抽象享元类的子类,用这些类创建的对象就是享元对象,有时候也可以考虑以单件类实现享元对象。
3 享元工厂类:用创建并管理享元对象,该类中存在一个享元池存放享元对象。

享元使用场景

1 程序中存在大量相同或者相似对象造成内存大量消耗。
2 对象的大部分状态都是或者都可以转变为外部状态,通过参数传入到对象中。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐