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

本文是学习刘伟技术博客和《设计模式-可复用面向对象软件的基础》笔记,博客链接: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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值