【设计模式学习笔记十二】【结构型模式】【享元模式(FlyWeight)】

来源:互联网 发布:ubuntu owncloud 编辑:程序博客网 时间:2024/04/30 02:12

本文是学习刘伟技术博客和《设计模式-可复用面向对象软件的基础》笔记,博客链接:http://blog.csdn.net/lovelion/article/details/17517213

主要是对博客和书本做提炼和记录,更多是对设计模式的基础框架学习,细节将略去,侧重对每个设计模式框架的理解。

我应该理解和掌握的:

1)能够画出这个设计模式的架构框图;

2)能够根据架构框图写出对应的伪代码;

3)这个模式的应用场景,主要优缺点。

1.享元模式

当一个软件系统中产生大量的相同或相似对象时,将会导致运行代价过高,消耗大量内存。比如围棋棋子的设计,假如我们没下一步棋都去new一个对象出来,将会占用大量的内存空间。面对这种情况,享元模式能够很好的解决这个问题,通过共享技术实现相同或相似对象的重用,存放这些共享实例对象的地方称为享元池。我们针对每一个不同的棋子创建一个对象,将他放在享元池中,需要时再从里面取出。

(1)定义

享元模式:运用共享技术有效地支持大量细粒度的对象。系统只使用少量的对象,而这些对象都很相似,状态变化小,可以实现多次复用。又称轻量级模式。

1) 注意

需要注意的是,享元对象能够做到共享的关键是区分了内部状态和外部状态。

a) 内部状态是存储在享元对象内部并且不会随环境改变的状态,内部状态可以共享。如棋子的大小,形状。

b) 外部状态是随环境改变而改变的、不可以共享的状态。如棋子放在棋盘的位置。

c) 区分这两个状态,把具有内部状态的对象存在享元池中,需要时再取;在把取出的享元对象注入不同外部的外部状态,可以得到一系列相似的对象。

2)享元模式结构图


3)参与者

a) Flyweight(抽象享元类):描述一个接口,通过这个接口flyweight可以接受并作用于外部状态;

b) ConcreteFlyweight(具体享元类):实现flyweight接口,并为内部状态增加存储空间;ComcreteFlyweight对象必须是可以共享的,它所存储的状态必须是内部的;即,他必须独立于ConcreteFlyweight对象的场景;

c) UnsharedConcreteFlyweight(非共享具体享元类):并非所有Flyweight子类都需要被共享。不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类时可以直接实例化创建。

d) FlyweightFactory(享元工厂类):用于创建并管理Flyweight对象;当用户请求一个flyweight对象时,FlyweightFactory对象提供一个已经创建的实例或者创建一个(如果不存在的话)。

d) Client(客户端):维持一个对flyweight的引用,计算或存储一个(或多个外部状态)。

e) Coordinartes(外部状态):与外部环境相关,即棋子放置的位置,随时变化,由客户端计算或存贮;把这个外部状态注入到ConcreteFlyweight.上面的结构图没有,请看下面这个结构图:


4)看图写代码

#include<iostream>#include<map>#include<string>using namespace std;/* ** FileName     : DecoratorPattern ** Author       : lin005 ** Date         : 2015/01/29 ** Description  : More information, please go to http://blog.csdn.net/amd123456789 *///外部状态,注入给共享元对象,不同坐标得到一系列相似的棋子class Coordinates{public:    Coordinates():x(0),y(0){}    Coordinates(int a, int b):x(a),y(b){}    void setX(int a){x = a;}    void setY(int b){y = b;}    int getX(){return x;}    int getY(){return y;}private:    int x;    int y;};//Flyweight:抽象共享元类class IgoChessman{public:    virtual string getColor() = 0;    virtual void display(Coordinates pos){};};//根据颜色生成不同的棋子class ConcreteIgoChessman:public IgoChessman{public:    ConcreteIgoChessman(string c):color(c){}    string getColor()    {        return color;    }    void display(Coordinates pos)    {        cout<<"the color is "<<getColor()<<";the pos is x = "<<pos.getX()<<"; y = "<<pos.getY()<<endl;    }private:    string color;};//工厂类维护一个实例列表(也就是享元池),保存所有的共享实例。class IgoChessmanFactory{public:    static IgoChessmanFactory* getInstance();    IgoChessman* getIgoChessman(string color);private:    static IgoChessmanFactory* _instance;    //存放对象,也就是共享元池    map<string, IgoChessman*> m_map;};IgoChessmanFactory* IgoChessmanFactory::_instance = NULL;IgoChessmanFactory* IgoChessmanFactory::getInstance(){    if(_instance == NULL)    {        _instance = new IgoChessmanFactory();    }    return _instance;}IgoChessman* IgoChessmanFactory::getIgoChessman(string color){    //如果找不到棋子,则生成一个,如果有,直接取出    map<string,IgoChessman*>::iterator it = m_map.find(color);    IgoChessman *ic = NULL;    if(it->second == NULL)    {        ic = new ConcreteIgoChessman(color);        m_map.insert(pair<string, IgoChessman*>(color, ic));    }    else    {        ic = m_map.find(color)->second;    }        return ic;};//客户端测试int main(int argc, const char * argv[]) {    //声明白色,黑色,红色的抽象棋子    IgoChessman *b1,*b2,*b3,*wr;    //创建黑色的棋子    b1 = IgoChessmanFactory::getInstance()->getIgoChessman("black");    b2 = IgoChessmanFactory::getInstance()->getIgoChessman("black");    b3 = IgoChessmanFactory::getInstance()->getIgoChessman("black");        //每种棋子注入不同的坐标,也就是外部状态    b1->display(Coordinates(1,2));    b2->display(Coordinates(60,2));    b3->display(Coordinates(1,55));        //这里输出的时指针地址,每个指针的地址都不一样,故指针也会消耗内存,这里只是做验证用    cout<<&(b1)<<" "<<&(b2)<<" "<<&(b3)<<endl;    //这里输出的时指针所指向内容的地址,全都一样,指向享元池的对象,这是我们要的结果    cout<<&(*b1)<<" "<<&(*b2)<<" "<<&(*b3)<<endl;        //创建白色的棋子    wr = IgoChessmanFactory::getInstance()->getIgoChessman("white");    //注入显示位置    wr->display(Coordinates(100,2));    //查看地址是否一样        cout<<&wr<<" "<<&(*wr)<<endl;    wr = IgoChessmanFactory::getInstance()->getIgoChessman("white");    wr->display(Coordinates(1,200));    cout<<&wr<<" "<<&(*wr)<<endl;        //创建红色的棋子,指针地址一样,红色棋子的地址一样    wr = IgoChessmanFactory::getInstance()->getIgoChessman("red");    wr->display(Coordinates(10,200));    cout<<&wr<<" "<<&(*wr)<<endl;        wr = IgoChessmanFactory::getInstance()->getIgoChessman("red");    wr->display(Coordinates(100,200));    cout<<&wr<<" "<<&(*wr)<<endl;    return 0;}

(2)总结

1)优点

a) 极大减少内存中对象的数量,避免相似对象的开销。使得相同相似对象在内存中只保留一份,从而可以节约系统资源,提高系统性能。

b) 享元的外部状态相对独立,不会影响内部状态,从而使得享元对象在不同的环境中共享。

2)缺点

a) 需要分离出外部状态和内部状态,是系统、逻辑编的复杂。

b) 使享元对象的部分状态外部化,可以将这些状态传入对象中。

(3)适用场景

1)一个应用程序使用了大量对象。

2)完全由于使用大量对象,造成很大的存储开销。

3)对象的大多数状态都可以变为外部状态。

4)如果删除对象的外部状态,那么可以用相对较少的共享对象取代多组对象。

5)在享元模式中需要维护一个存储享元对象的享元池,这需要耗费一定的系统资源,因此需要再多次重复使用享元对象的情况下才使用享元模式。

0 0
原创粉丝点击