(莫寒C++成长贴)C++ 桥接模式

C++ Bridge 桥接模式

Bridge模式产生的缘由

面向对象主要需要解决两个问题:

  1. 松耦合(Couppling)
  2. 高内聚(Cohesion)

总体目标是:面向对象系统追求尽可能地提高系统模块的内聚(Cohesion)、尽可能的降低模块间的耦合(Coupling)。

然而这也是面向对象设计过程中最为难把握的部分。大家肯定在OO系统的开发过程中遇到这样的问题:

  1. 客户给了你一个需求,于是使用一个类来实现(A);

  2. 客户需求变化,有两个算法实现功能,于是改变设计,我们通过一个抽象的基类,再定义两个具体类实现两个不同的算法(A1和A2);

  3. 客户又告诉我们说对于不同的操作系统,于是再抽象一个层次,作为一个抽象基类A0,在分别为每个操作系统派生具体类(A00和A01,其中A00表示原来的类A)实现不同操作系统上的客户需求,这样我们就有了一共4个类。

  4. 可能用户的需求又有变化,比如说又有了一种新的算法………

5 .我们陷入了一个需求变化的郁闷当中,也因此带来了类的迅速膨胀。

然而Bridge模式则正是解决了这样的一类问题。

Bridge模式的作用

作用:将抽象部份与它的实现部份分离,使它们都可以独立地变化。将抽象(Abstraction)与实现(Implementation)分离,使得二者可以独立地变化。桥接模式号称设计模式中最难理解的模式之一,关键就是这个抽象和实现的分离非常让人奇怪,大部分人刚看到这个定义的时候都会认为实现就是继承自抽象,那怎么可能将他们分离呢。

《大话设计模式》中就Bridge模式的相关解释:

手机品牌和软件是两个概念,不同的软件可以在不同的手机上,不同的手机可以有相同的软件,两者都具有很大的变动性。如果我们单独以手机品牌或手机软件为基类来进行继承扩展的话,无疑会使类的数目剧增并且耦合性很高,将两者抽象出来两个基类分别是PhoneBrand和PhoneSoft,那么在品牌类中聚合一个软件对象的基类将解决软件和手机扩展混乱的问题,这样两者的扩展就相对灵活,剪短了两者的必要联系,结构图如下:

image

Bridget模式的UML结构图如图1所示:

image

Bridge模式的构成:

  1. Abstraction::Operation():定义要实现的操作接口
  2. AbstractionImplement::Operation():实现抽象类Abstaction所定义操作的接口,由其具体派生类ConcreteImplemenA、ConcreteImplemenA或者其他派生类实现。
  3. 在Abstraction::Operation()中根据不同的指针多态调用AbstractionImplement::Operation()函数。

对于Bridge模式构成的理解:

Bridge模式用于将表示和实现解耦,两者可以独立的变化,在Abstraction类中维护一个AbstractionImplement类指针,需要采用不同的实现方式的时候只需要传入不同的AbstractionImplement派生类就可以了。

Bridge的实现方式其实和Builde十分的相近,可以这么说:本质上是一样的,只是封装的东西不一样罢了.两者的实现都有如下的共同点:

抽象出来一个基类,这个基类里面定义了共有的一些行为,形成接口函数(对接口编程而不是对实现编程),这个接口函数在Buildier中是BuildePart函数在Bridge中是Operation函数;

其次,聚合一个基类的指针,如Builder模式中Director类聚合了一个Builder基类的指针,而Brige模式中Abstraction类聚合了一个AbstractionImplement基类的指针(优先采用聚合而不是继承);

而在使用的时候,都把对这个类的使用封装在一个函数中,在Bridge中是封装在Director::Construct函数中,因为装配不同部分的过程是一致的,而在Bridge模式中则是封装在Abstraction::Operation函数中,在这个函数中调用对应的AbstractionImplement::Operation函数.就两个模式而言,Builder封装了不同的生成组成部分的方式,而Bridge封装了不同的实现方式.

桥接模式就将实现与抽象分离开来,使得RefinedAbstraction依赖于抽象的实现,这样实现了依赖倒转原则,而不管左边的抽象如何变化,只要实现方法不变,右边的具体实现就不需要修改,而右边的具体实现方法发生变化,只要接口不变,左边的抽象也不需要修改。

桥接模式代码模型代码示例:

PatternGlobals.h

/*
** (C) Copyright 2020 Tom Zhao. All Rights Reserved.
*/
#pragma once

#define CDP_USE_NAME_SPACE using namespace CppDesignPatternPractice;
#define CDP_BEGIN_NAME_SPACE namespace CppDesignPatternPractice {
#define CDP_END_NAME_SPACE };

BridgeExamOne.h

/*
** (C) Copyright 2020 Tom Zhao. All Rights Reserved.
*/
#pragma once
#ifndef _BRIDGE_EXAM_ONE_H
#define _BRIDGE_EXAM_ONE_H

#include "PatternGlobals.h"

CDP_BEGIN_NAME_SPACE

// Define the abstract class for phone software
class HandsetSoft 
{
public:
	virtual void run() = 0; // Define the abstract operations for phone's software.
};

// This is a game software on any phone.
class HandsetGame : public HandsetSoft
{
public:
	void run();
};
// This is a addresslist software on any phone.
class HandsetAddressList : public HandsetSoft 
{
public:
	void run();
};

// Definethe abstract class for phone brand.
class HandsetBrand 
{
protected:
	HandsetSoft *soft;	//Get the abstract object of HandsetSoft.
public:
	void setHandsetSoft(HandsetSoft * soft);
	virtual void run() = 0;
};

class HandsetBrandN : public HandsetBrand 
{
public:
	void run();
};

class HandsetBrandM : public HandsetBrand
{
public:
	void run();
};

class BridgeExamOne
{
public:
	~BridgeExamOne();

	BridgeExamOne(const BridgeExamOne&) = delete;
	BridgeExamOne operator=(const BridgeExamOne&) = delete;
	static BridgeExamOne& get_instance();

private:
	BridgeExamOne();
};

CDP_END_NAME_SPACE

#endif

BridgeExamOne.cpp

/*
** (C) Copyright 2020 Tom Zhao. All Rights Reserved.
*/
#include "stdafx.h"
#include "BridgeExamOne.h"
#include <iostream>
#include <string>
#include <QDebug>

using namespace std;

CDP_BEGIN_NAME_SPACE

void HandsetGame::run()
{
	cout << "Run the mobile game!" << endl;
	qDebug() << "Run the mobile game!";
}

void HandsetAddressList::run()
{
	cout << "Run the addresslist software!" << endl;
	qDebug() << "Run the addresslist software!";
}

void HandsetBrand::setHandsetSoft(HandsetSoft * soft)
{
	this->soft = soft;
}

void HandsetBrandN::run()
{
	soft->run();
}

void HandsetBrandM::run()
{
	soft->run();
}

BridgeExamOne::BridgeExamOne()
{
	HandsetBrand *hb;
	hb = new HandsetBrandM();

	hb->setHandsetSoft(new HandsetGame());
	hb->run();
	hb->setHandsetSoft(new HandsetAddressList());
	hb->run();
}

BridgeExamOne::~BridgeExamOne()
{
	cout << "BridgeExamOne destructor called!." << endl;
	qDebug() << "BridgeExamOne destructor called!.";
}

BridgeExamOne & BridgeExamOne::get_instance()
{
	// TODO: 在此处插入 return 语句
	static BridgeExamOne instance;
	return instance;
}

CDP_END_NAME_SPACE

Bridge模式的适用场景:

1.当一个对象有多个变化因素的时候,考虑依赖于抽象的实现,而不是具体的实现。如上面例子中手机品牌有2种变化因素,一个是品牌,一个是功能。

2.当多个变化因素在多个对象间共享时,考虑将这部分变化的部分抽象出来再聚合/合成进来,如上面例子中的通讯录和游戏,其实是可以共享的。

3.当我们考虑一个对象的多个变化因素可以动态变化的时候,考虑使用桥接模式,如上面例子中的手机品牌是变化的,手机的功能也是变化的,所以将他们分离出来,独立的变化。

小结:

1.设计中有超过一维的变化我们就可以用桥模式。如果只有一维在变化,那么我们用继承就可以圆满的解决问题。

抽象代码解析

Abstraction.h

/*
** (C) Copyright 2020 Tom Zhao. All Rights Reserved.
*/

#pragma once
#ifndef _ABSTRACTION_H_
#define _ABSTRACTION_H_

#include "PatternGlobals.h"

CDP_BEGIN_NAME_SPACE

class AbstractionImplement;

class Abstraction
{
public:
	virtual void Operation() = 0; // Define the interface to represent the operations supported by the class
	virtual ~Abstraction();
protected:
	Abstraction();
};

class RefinedAbstractionA :public Abstraction
{
public:
	RefinedAbstractionA(AbstractionImplement* imp);//Constructor
	virtual void Operation();//Implement the interface
	virtual ~RefinedAbstractionA();//Destructor
private:
	AbstractionImplement* _imp;//Private member
};

class RefinedAbstractionB :public Abstraction
{
public:
	RefinedAbstractionB(AbstractionImplement* imp);//Constructor
	virtual void Operation();//Implement the interface
	virtual ~RefinedAbstractionB();//Destructor
private:
	AbstractionImplement* _imp;//Private member
};
CDP_END_NAME_SPACE
#endif

Abstraction.cpp

/*
** (C) Copyright 2020 Tom Zhao. All Rights Reserved.
*/

#include "stdafx.h"
#include "Abstraction.h"
#include "AbstractionImplement.h"
#include <iostream>
#include <QDebug>

using namespace std;

CDP_BEGIN_NAME_SPACE

Abstraction::Abstraction()
{}

Abstraction::~Abstraction()
{}

RefinedAbstractionA::RefinedAbstractionA(AbstractionImplement* imp)
{
	this->_imp = imp;
}

RefinedAbstractionA::~RefinedAbstractionA()
{
	delete this->_imp;
	this->_imp = NULL;
}

void RefinedAbstractionA::Operation()
{
	cout << "RefinedAbstractionA::Operation" << endl;
	qDebug() << "RefinedAbstractionA::Operation";
	this->_imp->Operation();
}

RefinedAbstractionB::RefinedAbstractionB(AbstractionImplement* imp)
{
	this->_imp = imp;
}

RefinedAbstractionB::~RefinedAbstractionB()
{
	delete this->_imp;
	this->_imp = NULL;
}

void RefinedAbstractionB::Operation()
{
	cout << "RefinedAbstractionB::Operation" << endl;
	qDebug() << "RefinedAbstractionB::Operation";
	this->_imp->Operation();
}

CDP_END_NAME_SPACE

AbstractImplement.h

/*
** (C) Copyright 2020 Tom Zhao. All Rights Reserved.
*/
#pragma once
#ifndef _ABSTRACTIONIMPLEMENT_H_
#define _ABSTRACTIONIMPLEMENT_H_

#include "PatternGlobals.h"

CDP_BEGIN_NAME_SPACE

//Abstract base class that defines the implemented interface
class AbstractionImplement
{
public:
	virtual void Operation() = 0;//Define the operation interface
	virtual ~AbstractionImplement();
protected:
	AbstractionImplement();
};

// Inherited from AbstractionImplement, is one of the different implementations of AbstractionImplement
class ConcreteAbstractionImplementA :public AbstractionImplement
{
public:
	ConcreteAbstractionImplementA();
	void Operation();//Implement operation
	~ConcreteAbstractionImplementA();
protected:
};

// Inherited from AbstractionImplement, is one of the different implementations of AbstractionImplement
class ConcreteAbstractionImplementB :public AbstractionImplement
{
public:
	ConcreteAbstractionImplementB();
	void Operation();//Implement operation
	~ConcreteAbstractionImplementB();
protected:
};

CDP_END_NAME_SPACE
#endif

AbstractImplement.cpp

/*
** (C) Copyright 2020 Tom Zhao. All Rights Reserved.
*/

#include "stdafx.h"
#include "AbstractionImplement.h"
#include <iostream>
#include <QDebug>

using namespace std;

CDP_BEGIN_NAME_SPACE

AbstractionImplement::AbstractionImplement()
{}

AbstractionImplement::~AbstractionImplement()
{}

ConcreteAbstractionImplementA::ConcreteAbstractionImplementA()
{}

ConcreteAbstractionImplementA::~ConcreteAbstractionImplementA()
{}

void ConcreteAbstractionImplementA::Operation()
{
	cout << "ConcreteAbstractionImplementA Operation" << endl;
	qDebug() << "ConcreteAbstractionImplementA Operation";
}

ConcreteAbstractionImplementB::ConcreteAbstractionImplementB()
{}

ConcreteAbstractionImplementB::~ConcreteAbstractionImplementB()
{}

void ConcreteAbstractionImplementB::Operation()
{
	cout << "ConcreteAbstractionImplementB Operation" << endl;
	qDebug() << "ConcreteAbstractionImplementB Operation";
}

CDP_END_NAME_SPACE

BridgeExamTwo.h

/*
** (C) Copyright 2020 Tom Zhao. All Rights Reserved.
*/

#pragma once
#ifndef _BRIDGE_EXAM_TWO_H
#define _BRIDGE_EXAM_TWO_H

#include "PatternGlobals.h"

CDP_BEGIN_NAME_SPACE
// Using signaltone pattern to create the test.
class BridgeExamTwo
{
public:
	~BridgeExamTwo();

	BridgeExamTwo(const BridgeExamTwo&) = delete;
	BridgeExamTwo operator=(const BridgeExamTwo&) = delete;
	static BridgeExamTwo& get_instance();

private:
	BridgeExamTwo();
};


CDP_END_NAME_SPACE

#endif

BridgeExamTwo.cpp

/*
** (C) Copyright 2020 Tom Zhao. All Rights Reserved.
*/
#include "stdafx.h"
#include "BridgeExamTwo.h"
#include <iostream>
#include <string>
#include <QDebug>
#include "Abstraction.h"
#include "AbstractionImplement.h"

using namespace std;

CDP_BEGIN_NAME_SPACE

BridgeExamTwo::BridgeExamTwo()
{
	/* 将抽象部分与它的实现部分分离,使得它们可以独立地变化
	1、抽象Abstraction与实现AbstractionImplement分离;
	2、抽象部分Abstraction可以变化,如new RefinedAbstractionA(imp)、new RefinedAbstractionB(imp2);
	3、实现部分AbstractionImplement也可以变化,如new ConcreteAbstractionImplementA()、new ConcreteAbstractionImplementB();
	*/

	AbstractionImplement *imp = new ConcreteAbstractionImplementA();
	Abstraction* abs = new RefinedAbstractionA(imp);
	abs->Operation();
	
	qDebug() << "-----------------------------------------";

	AbstractionImplement * imp1 = new ConcreteAbstractionImplementB();
	Abstraction *abs1 = new RefinedAbstractionA(imp1);
	abs1->Operation();

	qDebug() << "-----------------------------------------";

	AbstractionImplement* imp2 = new ConcreteAbstractionImplementA();        //实现部分ConcreteAbstractionImplementA
	Abstraction* abs2 = new RefinedAbstractionB(imp2);                        //抽象部分RefinedAbstractionB
	abs2->Operation();

	qDebug() << "-----------------------------------------";

	AbstractionImplement* imp3 = new ConcreteAbstractionImplementB();        //实现部分ConcreteAbstractionImplementB
	Abstraction* abs3 = new RefinedAbstractionB(imp3);                        //抽象部分RefinedAbstractionB
	abs3->Operation();

}

BridgeExamTwo::~BridgeExamTwo()
{
	qDebug() << "BridgeExamOne destructor called!.";
}

BridgeExamTwo & BridgeExamTwo::get_instance()
{
	// TODO: 在此处插入 return 语句
	static BridgeExamTwo instance;
	return instance;
}


CDP_END_NAME_SPACE

桥接模式的运用

将抽象部分与它的实现部分分离,使得它们可以独立地变化

  • 抽象Abstraction与实现AbstractionImplement分离;
  • 抽象部分Abstraction可以变化,如new RefinedAbstractionA(imp)、new RefinedAbstractionB(imp2);
  • 实现部分AbstractionImplement也可以变化,如new ConcreteAbstractionImplementA()、new ConcreteAbstractionImplementB();

参考文献版权声明:

————————————————
版权声明:本文为CSDN博主「老樊Lu码」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fanyun_01/article/details/51766505

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值