本文是学习刘伟技术博客和《设计模式-可复用面向对象软件的基础》笔记,博客链接: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)在享元模式中需要维护一个存储享元对象的享元池,这需要耗费一定的系统资源,因此需要再多次重复使用享元对象的情况下才使用享元模式。