peter_luy
8/31/2017 - 12:17 PM

享元模式

享元模式

#享元模式 ##一、UML图
关键词:通过共享减少单个实例的数目,网站。

二、概念

享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象。

flyweight的意思:轻量级。

三、说明

角色:

(1)Flyweight类:它是所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。

(2)ConcreteFlyweight类:继承Flyweight超类或实现Flyweight接口,并为内部状态增加存储空间。

(3)UnsharedConcreteFlyweight:是指那些不需要共享的Flyweight子类。因为Flyweight接口共享成为可能,但是它并不强制共享。

(4)FlyweightFactory:是一个享元工厂,用来创建并管理Flyweight对象。它主要是用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。

用享元模式的好处?

享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够大幅度的减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。

也就是说,享元模式Flyweight执行时所需的状态是有内部的也可能有外部的,内部状态存储于ConcreteFlyweight对象之中,而外部对象则应该考虑由客户端对象存储或计算,当调用Flyweight对象的操作时,将该状态传递给它。

什么时候用享元模式?

如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。

四、C++实现

(1)Flyweight.h

//用户类
class User
{
private:
std::string name;
public:
User(std::string name)
{
this->name=name;
}
std::string GetName()
{
return this->name;
}
};

//Flyweight类,此处为抽象网站类
class WebSite
{
public:
virtual void Use(User user)=0;
};

//ConcreteFlyweight类,此处为具体网站类
class ConcreteWebSite:public WebSite
{
private:
std::string name;
public:
ConcreteWebSite(std::string name)
{
this->name=name;
}
void Use(User user)
{
std::cout<<"网站分类:"<<name<<" 用户:"+user.GetName()<<std::endl;
}
};

//FlyweightFactory类,此处为网站工程类
class WebSiteFactory
{
private:
std::map<std::string,WebSite> flyweights;
public:
WebSiteFactory(){}
~WebSiteFactory()
{
std::map<std::string,WebSite
>::iterator it;
for(it=flyweights.begin();it!=flyweights.end();it)
delete it->second;
}
WebSite* GetWebSiteCategory(std::string key)
{
std::map<std::string,WebSite*>::iterator it;
for(it=flyweights.begin();it!=flyweights.end();it)
if(it->first==key)
return it->second;
WebSite* website=new ConcreteWebSite(key);
flyweights.insert(std::pair<std::string,WebSite*>(key,website));
return website;
}
int GetWebSiteCount()
{
return flyweights.size();
}

};
(2).client.cpp

//Client,客户端
void main()
{
WebSiteFactory f;
WebSite* fx=f.GetWebSiteCategory("产品展示");
fx->Use(User("小菜"));
WebSite* fy=f.GetWebSiteCategory("产品展示");
fy->Use(User("大鸟"));
WebSite* fz=f.GetWebSiteCategory("产品展示");
fz->Use(User("娇娇"));
WebSite* fl=f.GetWebSiteCategory("博客");
fl->Use(User("老顽童"));
WebSite* fm=f.GetWebSiteCategory("博客");
fm->Use(User("桃谷六仙"));
WebSite* fn=f.GetWebSiteCategory("博客");
fn->Use(User("南海鳄神"));
std::cout<<"得到网站分类总数:"<<f.GetWebSiteCount()<<std::endl;
system("pause");
}

(3)运行截图


举个围棋的例子,围棋的棋盘共有361格,即可放361个棋子。现在要实现一个围棋程序,该怎么办呢?首先要考虑的是棋子棋盘的实现,可以定义一个棋子的类,成员变量包括棋子的颜色、形状、位置等信息,另外再定义一个棋盘的类,成员变量中有个容器,用于存放棋子的对象。下面给出代码表示: 棋子的定义,当然棋子的属性除了颜色和位置,还有其他的,这里略去。这两个属性足以说明问题。

//棋子颜色
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;}
};
棋盘的定义:
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];
}
};
客户端的使用方式:
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 m_vecPos。这里是关键。 棋子的新定义,只包含内在属性:

//棋子颜色
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";}
};
相应棋盘的定义为:
class PieceBoard
{
private:
vector 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图,以围棋为例。棋盘中含两个共享的对象,黑棋子和白棋子,所有棋子的外在属性都存放在单独的容器中。