结构模式->享元模式
来源:互联网 发布:qq风险软件下载 编辑:程序博客网 时间:2024/05/18 00:06
减少内存浪费。 Sunny软件公司开发人员通过对围棋软件进行分析,发现在围棋棋盘中包含大量的黑子和白子,它们的形状、大小都一模一样,只是出现的位置不同而已。如果将每一个棋子都作为一个独立的对象存储在内存中,将导致该围棋软件在运行时所需内存空间较大,如何降低运行代价、提高系统性能是Sunny公司开发人员需要解决的一个问题。为了解决这个问题,Sunny公司开发人员决定使用享元模式来设计该围棋软件的棋子对象,那么享元模式是如何实现节约内存进而提高系统性能的呢?
享元模式通过共享技术实现相同或相似对象的重用,在逻辑上每一个出现的字符都有一个对象与之对应,然而在物理上它们却共享同一个享元对象,这个对象可以出现在一个字符串的不同地方,相同的字符对象都指向同一个实例,在享元模式中,存储这些共享实例对象的地方称为享元池(Flyweight Pool)。我们可以针对每一个不同的字符创建一个享元对象,将其放在享元池中,需要时再从享元池取出。如图14-2所示:
图14-2 字符享元对象示意图
享元模式以共享的方式高效地支持大量细粒度对象的重用,享元对象能做到共享的关键是区分了内部状态(Intrinsic State)和外部状态(Extrinsic State)。下面将对享元的内部状态和外部状态进行简单的介绍:
(1) 内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享。如字符的内容,不会随外部环境的变化而变化,无论在任何环境下字符“a”始终是“a”,都不会变成“b”。
(2) 外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态通常由客户端保存,并在享元对象被创建之后,需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。如字符的颜色,可以在不同的地方有不同的颜色,例如有的“a”是红色的,有的“a”是绿色的,字符的大小也是如此,有的“a”是五号字,有的“a”是四号字。而且字符的颜色和大小是两个独立的外部状态,它们可以独立变化,相互之间没有影响,客户端可以在使用时将外部状态注入享元对象中。
正因为区分了内部状态和外部状态,我们可以将具有相同内部状态的对象存储在享元池中,享元池中的对象是可以实现共享的,需要的时候就将对象从享元池中取出,实现对象的复用。通过向取出的对象注入不同的外部状态,可以得到一系列相似的对象,而这些对象在内存中实际上只存储一份。
享元模式定义如下:
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
[cpp] view plain copy print?//棋子颜色 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;} }; 棋盘的定义:[cpp] view plain copy print?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]; } }; 客户的使用方式如下:[cpp] view plain copy print?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。这里是关键。 棋子的新定义,只包含内在属性:[cpp] view plain copy print?//棋子颜色 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<<"绘制一颗黑棋\n"; } }; class WhitePiece: public Piece { public: WhitePiece(PieceColor color): Piece(color) {} ~WhitePiece() {} void Draw() { cout<<"绘制一颗白棋\n";} }; 相应棋盘的定义为:[cpp] view plain copy print?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图,以围棋为例。棋盘中含两个共享的对象,黑棋子和白棋子,所有棋子的外在属性都存放在单独的容器中。
- 结构模式->享元模式
- 享元模式-AbstractMap结构
- 结构型模式-享元
- 结构模式:FlyWeight享元模式
- 设计模式-结构型模式-享元
- 结构模式之享元模式注解
- 设计模式--结构型--享元模式
- 结构型模式-享元模式
- 享元模式-对象结构型模式
- 结构模式之享元模式
- 结构型模式--享元模式
- 结构型模式-Flyweight ( 享元模式 )
- 享元模式 - 结构型模式
- 结构型设计模式-享元模式
- 结构型模式-享元模式
- 结构型设计模式-享元模式
- 设计模式--结构模式--享元模式--Java
- (C++设计模式) ------享元模式-- 结构型模式
- 转发:什么是单播、多播和广播
- 原生JavaScript移动端百度图片搜索,找工作就这么简单
- 日志分割
- C#之构造函数
- 使用Timer类去调度任务——java
- 结构模式->享元模式
- [Gym-101341B](KMP)(水)Pursuing the Happiness
- Java虚拟机学习笔记(3)——垃圾收集器
- C/C++学习(2)关于strcpy、strcat函数使用
- 功耗分析
- c++实验5——数组分离
- SpringMVC中使用bean来接收form表单提交的参数时的注意点
- ng-src 和 ng-href的妙用
- Spring依赖注解的四种方式