目录
13. 享元模式
享元模式:运用共享技术有效地支持大量细粒度的对象。在有大量对象时,把其中共同的部分抽象出来,如果有相同的业务请求,直接返回内存中已有的对象,避免重新创建。
以下情况可以考虑使用享元模式:
系统中有大量的对象,这些对象消耗大量的内存,且这些对象的状态可以被外部化。
对于享元模式,需要将对象的信息分为两个部分:内部状态和外部状态。内部状态是指被共享出来的信息,储存在享元对象内部且不随环境变化而改变;外部状态是不可以共享的,它随环境改变而改变,是由客户端控制的。
/*
* 关键代码:将内部状态作为标识,进行共享。
*/
#include <iostream>
#include <map>
#include <memory>
using namespace std;
//抽象享元类,提供享元类外部接口。
class AbstractConsumer
{
public:
virtual ~AbstractConsumer(){}
virtual void setArticle(const string&) = 0;
virtual const string& article() = 0;
};
//具体的享元类
class Consumer : public AbstractConsumer
{
public:
Consumer(const string& strName) : m_user(strName){}
~Consumer()
{
cout << " ~Consumer()" << endl;
}
void setArticle(const string& info) override
{
m_article = info;
}
const string& article() override
{
return m_article;
}
private:
string m_user;
string m_article;
};
//享元工厂类
class Trusteeship
{
public:
~Trusteeship()
{
m_consumerMap.clear();
}
void hosting(const string& user, const string& article)
{
if(m_consumerMap.count(user))
{
cout << "A customer named " << user.data() << " already exists" << endl;
Consumer* consumer = m_consumerMap.at(user).get();
consumer->setArticle(article);
}
else
{
shared_ptr<Consumer> consumer(new Consumer(user));
consumer.get()->setArticle(article);
m_consumerMap.insert(pair<string,shared_ptr<Consumer>>(user, consumer));
}
}
void display()
{
map<string, shared_ptr<Consumer>>::iterator iter = m_consumerMap.begin();
for(; iter != m_consumerMap.end(); iter++)
{
cout << iter->first.data() << " : "<< iter->second.get()->article().data() << endl;
}
}
private:
map<string, shared_ptr<Consumer>> m_consumerMap;
};
int main()
{
Trusteeship* ts = new Trusteeship;
ts->hosting("zhangsan", "computer");
ts->hosting("lisi", "phone");
ts->hosting("wangwu", "watch");
ts->display();
ts->hosting("zhangsan", "TT");
ts->hosting("lisi", "TT");
ts->hosting("wangwu", "TT");
ts->display();
delete ts;
ts = nullptr;
return 0;
}
13.1 实例2
举个围棋的例子,围棋的棋盘共有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<PiecePos> 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<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);
}
};
参考资料:https://blog.youkuaiyun.com/wuzhekai1985/article/details/6670298
参考资料:https://www.cnblogs.com/chengjundu/p/8473564.html
14. 桥接模式
桥接模式:将抽象部分与实现部分分离,使它们都可以独立变换。
以下情形考虑使用桥接模式:
1. 当一个对象有多个变化因素的时候,考虑依赖于抽象的实现,而不是具体的实现。
2. 当多个变化因素在多个对象间共享时,考虑将这部分变化的部分抽象出来再聚合/合成进来。
3. 当一个对象的多个变化因素可以动态变化的时候。
优点:
1. 将实现抽离出来,再实现抽象,使得对象的具体实现依赖于抽象,满足了依赖倒转原则。
2. 更好的可扩展性。
3. 可动态的切换实现。桥接模式实现了抽象和实现的分离,在实现桥接模式时,就可以实现动态的选择具体的实现。
/*
* 关键代码:将现实独立出来,抽象类依赖现实类。
* 以下示例中,将各类App、各类手机独立开来,实现各种App和各种手机的自由桥接。
*/
#include <iostream>
using namespace std;
//抽象App类,提供接口
class App
{
public:
virtual ~App(){ cout << "~App()" << endl; }
virtual void run() = 0;
};
//具体的App实现类
class GameApp:public App
{
public:
void run()
{
cout << "GameApp Running" << endl;
}
};
//具体的App实现类
class TranslateApp:public App
{
public:
void run()
{
cout << "TranslateApp Running" << endl;
}
};
//抽象手机类,提供接口
class MobilePhone
{
public:
virtual ~MobilePhone(){ cout << "~MobilePhone()" << endl;}
virtual void appRun(App* app) = 0; //实现App与手机的桥接
};
//具体的手机实现类
class XiaoMi:public MobilePhone
{
public:
void appRun(App* app)
{
cout << "XiaoMi: ";
app->run();
}
};
//具体的手机实现类
class HuaWei:public MobilePhone
{
public:
void appRun(App* app)
{
cout << "HuaWei: ";
app->run();
}
};
int main()
{
App* gameApp = new GameApp;
App* translateApp = new TranslateApp;
MobilePhone* mi = new XiaoMi;
MobilePhone* hua = new HuaWei;
mi->appRun(gameApp);
mi->appRun(translateApp);
hua->appRun(gameApp);
hua->appRun(translateApp);
delete hua;
hua = nullptr;
delete mi;
mi = nullptr;
delete gameApp;
gameApp = nullptr;
delete translateApp;
translateApp = nullptr;
return 0;
}
14.1 实例2
将抽象部分与它的实现部分分离,使它们都可以独立地变化。考虑装操作系统,有多种配置的计算机,同样也有多款操作系统。如何运用桥接模式呢?可以将操作系统和计算机分别抽象出来,让它们各自发展,减少它们的耦合度。当然了,两者之间有标准的接口。这样设计,不论是对于计算机,还是操作系统都是非常有利的。下面给出这种设计的UML图,其实就是桥接模式的UML图。
给出C++的一种实现:
//操作系统
class OS
{
public:
virtual void InstallOS_Imp() {}
};
class WindowOS: public OS
{
public:
void InstallOS_Imp() { cout<<"安装Window操作系统"<<endl; }
};
class LinuxOS: public OS
{
public:
void InstallOS_Imp() { cout<<"安装Linux操作系统"<<endl; }
};
class UnixOS: public OS
{
public:
void InstallOS_Imp() { cout<<"安装Unix操作系统"<<endl; }
};
//计算机
class Computer
{
public:
virtual void InstallOS(OS *os) {}
};
class DellComputer: public Computer
{
public:
void InstallOS(OS *os) { os->InstallOS_Imp(); }
};
class AppleComputer: public Computer
{
public:
void InstallOS(OS *os) { os->InstallOS_Imp(); }
};
class HPComputer: public Computer
{
public:
void InstallOS(OS *os) { os->InstallOS_Imp(); }
};
// 客户使用方式:
int main()
{
OS *os1 = new WindowOS();
OS *os2 = new LinuxOS();
Computer *computer1 = new AppleComputer();
computer1->InstallOS(os1);
computer1->InstallOS(os2);
}
参考资料:https://blog.youkuaiyun.com/wuzhekai1985/article/details/6670473