C++设计模式整理009-享元模式和桥接模式

本文探讨了享元模式在节省内存和大量细粒度对象管理中的应用,通过围棋棋子例子展示如何优化内存使用。同时,介绍了桥接模式如何分离抽象和实现,以提高灵活性和扩展性,以操作系统和计算机配置为例进行说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

13. 享元模式

13.1 实例2

14. 桥接模式

14.1 实例2


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

参考资料:https://www.cnblogs.com/chengjundu/p/8473564.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

公众号:程序喵星人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值