设计模式——享元模式

       设计模式是人们从长期的软件开发实战中总结出来的一些经验之谈,为软件领域中的开发人员提供了一种使用专家设计经验的有效途径,通常是多个设计模式结合使用。设计模式中运用了面向对象编程语言的重要特性:封装、继承、多态,只有通过大量的编程实践才能真正领悟设计模式的精髓。在参加校招前我翻看了部分设计模式相关的书,对于每个设计模式模式,用C++写了个小例子,加深一下理解。主要参考《大话设计模式》和《设计模式》两本书以及借鉴其他博客园博文的优秀部分。本文介绍享元模式的实现和一些使用场景。希望对大家加深对设计模式的理解有一点帮助。

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

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

//棋子颜色
enum PieceColor {BLACK, WHITE};
 
//棋子位置
struct PiecePos
{
        intx;
        inty;
        PiecePos(inta, int b): x(a), y(b) {}
};
 
//棋子定义
class Piece
{
protected:
        PieceColorm_color; //颜色
        PiecePosm_pos;     //位置
public:
        Piece(PieceColorcolor, PiecePos pos): m_color(color), m_pos(pos) {}
        ~Piece(){}
        virtualvoid Draw() {}
};
 
class BlackPiece: public Piece
{
public:
        BlackPiece(PieceColorcolor, PiecePos pos): Piece(color, pos) {}
        ~BlackPiece(){}
        voidDraw() { cout<<"绘制一颗黑棋"<
    
    
     
     m_vecPiece; //棋盘上已有的棋子
        stringm_blackName; //黑方名称
        stringm_whiteName; //白方名称
public:
        PieceBoard(stringblack, string white): m_blackName(black), m_whiteName(white){}
        ~PieceBoard(){ Clear(); }
        voidSetPiece(PieceColor color, PiecePos pos) //一步棋,在棋盘上放一颗棋子
        {
               Piece* piece = NULL;
               if(color== BLACK) //黑方下的
               {      
                       piece= new BlackPiece(color, pos); //获取一颗黑棋
                       cout<
     
     
      
      <<"在位置("<
      
      
       
       <<','<
       
       
        
        <<")";
                       piece->Draw();//在棋盘上绘制出棋子
               }
               else
               {      
                       piece= new WhitePiece(color, pos);
                       cout<
        
        
          <<"在位置("< 
         
           <<','< 
          
            <<")"; piece->Draw(); } m_vecPiece.push_back(piece); //加入容器中 } voidClear() //释放内存 { intsize = m_vecPiece.size(); for(inti = 0; i < size; i++) deletem_vecPiece[i]; } }; //客户的使用方式如下 int main() { PieceBoardpieceBoard("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
{
        intx;
        inty;
        PiecePos(inta, int b): x(a), y(b) {}
};
 
//棋子定义
class Piece
{
protected:
        PieceColorm_color; //颜色
public:
        Piece(PieceColorcolor): m_color(color) {}
        ~Piece(){}
        virtualvoid Draw() {}
};
 
class BlackPiece: public Piece
{
public:
        BlackPiece(PieceColorcolor): Piece(color) {}
        ~BlackPiece(){}
        voidDraw() { cout<<"绘制一颗黑棋\n";}
};
 
class WhitePiece: public Piece
{
public:
        WhitePiece(PieceColorcolor): Piece(color) {}
        ~WhitePiece(){}
        voidDraw() { cout<<"绘制一颗白棋\n";}
};
 
// 相应棋盘的定义为:
class PieceBoard
{
private:
        vector
    
    
     
     m_vecPos; //存放棋子的位置
        Piece*m_blackPiece;       //黑棋棋子
        Piece*m_whitePiece;       //白棋棋子
        stringm_blackName;
        stringm_whiteName;
public:
        PieceBoard(stringblack, string white): m_blackName(black), m_whiteName(white)
        {
               m_blackPiece= NULL;
               m_whitePiece= NULL;
        }
        ~PieceBoard(){ delete m_blackPiece; delete m_whitePiece;}
        voidSetPiece(PieceColor color, PiecePos pos)
        {
               if(color== BLACK)
               {
                       if(m_blackPiece== NULL)  //只有一颗黑棋
                               m_blackPiece= new BlackPiece(color);
                       cout<
     
     
      
      <<"在位置("<
      
      
       
       <<','<
       
       
        
        <<")";
                       m_blackPiece->Draw();
               }
               else
               {
                       if(m_whitePiece== NULL)
                               m_whitePiece= new WhitePiece(color);
                       cout<
        
        
          <<"在位置("< 
         
           <<','< 
          
            <<")"; m_whitePiece->Draw(); } m_vecPos.push_back(pos); } }; 
           
          
        
       
       
      
      
     
     
    
    

 

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

        

         看到这里,相信大家已经对享元模式有了深入的理解。那小伙伴们还能举出几个享元模式的使用场景吗?细心的小伙伴可能会说word、WPS……

         对,没错,word中就用到了大量的设计模式,其中就包括我们本文所讲的享元模式。那具体用在了哪里呢?可能有小伙伴会问。大家随便找一本书,统计一下任意一个字在书中出现的总次数,一定很大吧(如果你找的书很薄就没办法咯……)。其实,这些文字的内部属性是可以共享的,我们只要修改它们的外部属性就可以了,这样可以为我们节省大量的对象空间。大家都知道,汉字占两个字节,使用的是Unicode编码(这里用Unicode编码格式为例,当然还有其他编码格式,比如big5、gdb、gb2312、utf8……)格式。那么我们平时在编辑word时,这两个字节是不是可以用享元模式来节省一下呢。我们只要保存汉字的位置、字体、颜色等信息,也就是我们上面所说的外部属性,相同汉字的编码是可以共享的,那么编码就是我们上面所说的内部共享属性。

         好啦,享元模式就介绍到这里了,希望你已经掌握了这个模式并且可以熟练的使用它,那么本博文的效果就达到了。如果你还觉得意犹未尽,那么可以去看看博主的其他设计模式的介绍哦。

 

          关于设计模式,我推荐大家看看以下博客写的博文,关于设计模式的讲解将的特别好,我从中学习了很多,我的设计模式中的部分博文就是从这里借鉴过来的。

    博客链接:http://blog.csdn.net/wuzhekai1985

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值