(莫寒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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章