zoukankan      html  css  js  c++  java
  • [设计模式] 11 享元模式 Flyweight

    转 http://blog.csdn.net/wuzhekai1985/article/details/6670298

    问题
    在面向对象系统的设计何实现中,创建对象是最为常见的操作。这里面就有一个问题:
    如果一个应用程序使用了太多的对象,就会造成很大的存储开销。特别是对于大量轻量级(细
    粒度)的对象,比如在文档编辑器的设计过程中,我们如果为没有字母创建一个对象的话,
    系统可能会因为大量的对象而造成存储开销的浪费。例如一个字母“a”在文档中出现了
    100000 次,而实际上我们可以让这一万个字母“a”共享一个对象,当然因为在不同的位置
    可能字母“a”有不同的显示效果(例如字体和大小等设置不同),在这种情况我们可以为将
    对象的状态分为“外部状态”和“内部状态”,将可以被共享(不会变化)的状态作为内部
    状态存储在对象中,而外部对象(例如上面提到的字体、大小等)我们可以在适当的时候将
    外部对象最为参数传递给对象(例如在显示的时候,将字体、大小等信息传递给对象)。

    可以从图 2-1 中看出,Flyweight 模式中有一个类似 Factory 模式的对象构造工厂
    FlyweightFactory,当客户程序员(Client)需要一个对象时候就会向 FlyweightFactory 发出
    请求对象的消息 GetFlyweight()消息,FlyweightFactory 拥有一个管理、存储对象的“仓
    库”(或者叫对象池,vector 实现),GetFlyweight()消息会遍历对象池中的对象,如果已
    经存在则直接返回给 Client,否则创建一个新的对象返回给 Client。当然可能也有不想被共
    享的对象(例如结构图中的 UnshareConcreteFlyweight),但不在本模式的讲解范围,故在实
    现中不给出。

           举个围棋的例子,围棋的棋盘共有361格,即可放361个棋子。现在要实现一个围棋程序,该怎么办呢?首先要考虑的是棋子棋盘的实现,可以定义一个棋子的类,成员变量包括棋子的颜色、形状、位置等信息,另外再定义一个棋盘的类,成员变量中有个容器,用于存放棋子的对象。下面给出代码表示:

            棋子的定义,当然棋子的属性除了颜色和位置,还有其他的,这里略去。这两个属性足以说明问题。

    //棋子颜色
    enum PieceColor {BLACK, WHITE};
    //棋子位置
    struct PiecePos
    {
        int x;
        int y;
        PiecePos(int a, int b): x(a), y(b) {}
    };
    //棋子定义
    class Piece
    {
    protected:
        PieceColor m_color; //颜色
        PiecePos m_pos;     //位置
    public:
        Piece(PieceColor color, PiecePos pos): m_color(color), m_pos(pos) {}
        ~Piece() {}
        virtual void Draw() {}
    };
    class BlackPiece: public Piece
    {
    public:
        BlackPiece(PieceColor color, PiecePos pos): Piece(color, pos) {}
        ~BlackPiece() {}
        void Draw() { cout<<"绘制一颗黑棋"<<endl;}
    };
    class WhitePiece: public Piece
    {
    public:
        WhitePiece(PieceColor color, PiecePos pos): Piece(color, pos) {}
        ~WhitePiece() {}
        void Draw() { cout<<"绘制一颗白棋"<<endl;}
    };

         棋盘的定义:

    class PieceBoard
    {
    private:
        vector<Piece*> m_vecPiece; //棋盘上已有的棋子
        string m_blackName; //黑方名称
        string m_whiteName; //白方名称
    public:
        PieceBoard(string black, string white): m_blackName(black), m_whiteName(white){}
        ~PieceBoard() { Clear(); }
        void SetPiece(PieceColor color, PiecePos pos) //一步棋,在棋盘上放一颗棋子
        {
            Piece * piece = NULL;
            if(color == BLACK) //黑方下的
            {    
                piece = new BlackPiece(color, pos); //获取一颗黑棋
                cout<<m_blackName<<"在位置("<<pos.x<<','<<pos.y<<")";
                piece->Draw(); //在棋盘上绘制出棋子
            }
            else
            {    
                piece = new WhitePiece(color, pos);
                cout<<m_whiteName<<"在位置("<<pos.x<<','<<pos.y<<")";
                piece->Draw();
            }
            m_vecPiece.push_back(piece);  //加入容器中
        }
        void Clear() //释放内存
        {
            int size = m_vecPiece.size();
            for(int i = 0; i < size; i++)
                delete m_vecPiece[i];
        }
    };

    客户的使用方式如下:

    int main()
    {
        PieceBoard pieceBoard("A","B");
        pieceBoard.SetPiece(BLACK, PiecePos(4, 4));
        pieceBoard.SetPiece(WHITE, PiecePos(4, 16));
        pieceBoard.SetPiece(BLACK, PiecePos(16, 4));
        pieceBoard.SetPiece(WHITE, PiecePos(16, 16));
    }

           可以发现,棋盘的容器中存放了已下的棋子,而每个棋子包含棋子的所有属性。一盘棋往往需要含上百颗棋子,采用上面这种实现,占用的空间太大了。如何改进呢?用享元模式。其定义为:运用共享技术有效地支持大量细粒度的对象。

            在围棋中,棋子就是大量细粒度的对象。其属性有内在的,比如颜色、形状等,也有外在的,比如在棋盘上的位置。内在的属性是可以共享的,区分在于外在属性。因此,可以这样设计,只需定义两个棋子的对象,一颗黑棋和一颗白棋,这两个对象含棋子的内在属性;棋子的外在属性,即在棋盘上的位置可以提取出来,存放在单独的容器中。相比之前的方案,现在容器中仅仅存放了位置属性,而原来则是棋子对象。显然,现在的方案大大减少了对于空间的需求。

           关注PieceBoard 的容器,之前是vector<Piece*> m_vecPiece,现在是vector<PiecePos> m_vecPos。这里是关键。

          改进后的 棋子的新定义,只包含内在属性:

    //棋子颜色
    enum PieceColor {BLACK, WHITE};
    //棋子位置
    struct PiecePos
    {
        int x;
        int y;
        PiecePos(int a, int b): x(a), y(b) {}
    };
    //棋子定义
    class Piece
    {
    protected:
        PieceColor m_color; //颜色
    public:
        Piece(PieceColor color): m_color(color) {}
        ~Piece() {}
        virtual void Draw() {}
    };
    class BlackPiece: public Piece
    {
    public:
        BlackPiece(PieceColor color): Piece(color) {}
        ~BlackPiece() {}
        void Draw() { cout<<"绘制一颗黑棋
    "; }
    };
    class WhitePiece: public Piece
    {
    public:
        WhitePiece(PieceColor color): Piece(color) {}
        ~WhitePiece() {}
        void Draw() { cout<<"绘制一颗白棋
    ";}
    };

    相应棋盘的定义为:

    class PieceBoard
    {
    private:
        vector<PiecePos> m_vecPos; //存放棋子的位置
        Piece *m_blackPiece;       //黑棋棋子 
        Piece *m_whitePiece;       //白棋棋子
        string m_blackName;
        string m_whiteName;
    public:
        PieceBoard(string black, string white): m_blackName(black), m_whiteName(white)
        {
            m_blackPiece = NULL;
            m_whitePiece = NULL;
        }
        ~PieceBoard() { delete m_blackPiece; delete m_whitePiece;}
        void SetPiece(PieceColor color, PiecePos pos)
        {
            if(color == BLACK)
            {
                if(m_blackPiece == NULL)  //只有一颗黑棋
                    m_blackPiece = new BlackPiece(color);    
                cout<<m_blackName<<"在位置("<<pos.x<<','<<pos.y<<")";
                m_blackPiece->Draw();
            }
            else
            {
                if(m_whitePiece == NULL)
                    m_whitePiece = new WhitePiece(color);
                cout<<m_whiteName<<"在位置("<<pos.x<<','<<pos.y<<")";
                m_whitePiece->Draw();
            }
            m_vecPos.push_back(pos);
        }
    };

           客户的使用方式一样,这里不重复给出,现在给出享元模式的UML图,以围棋为例。棋盘中含两个共享的对象,黑棋子和白棋子,所有棋子的外在属性都存放在单独的容器中。

    UML类图:

  • 相关阅读:
    js递归遍历
    .NET Core 图片操作在 Linux/Docker 下的坑
    远程桌面连接出现CredSSP的解决方法
    端口被占用解决方案
    当遇到“无法启动 IIS Express Web 服务器。”时的解决方案
    SQL Server索引设计
    python自动化之UI自动化框架搭建四--完成(关键字驱动)
    python自动化之UI自动化框架搭建三(关键字驱动)
    python自动化之UI自动化框架搭建二(关键字驱动)
    python自动化之ui自动化框架搭建一(关键字驱动)
  • 原文地址:https://www.cnblogs.com/diegodu/p/4450886.html
Copyright © 2011-2022 走看看