c++设计模式之桥接模式
作用:将抽象部份与它的实现部份分离,使它们都可以独立地变化。将抽象(Abstraction)与实现(Implementation)分离,使得二者可以独立地变化。
桥接模式就将实现与抽象分离开来,使得RefinedAbstraction依赖于抽象的实现,这样实现了依赖倒转原则,而不管左边的抽象如何变化,只要实现方法不变,右边的具体实现就不需要修改,而右边的具体实现方法发生变化,只要接口不变,左边的抽象也不需要修改。
#include <iostream>
using namespace std;
/*
* 桥接模式
*/
/*
* 实现基类
*/
class AbstractionImp {
public:
virtual void operation() = 0;
virtual ~AbstractionImp()
{
}
};
class ConcreteAbstractionImpA : public AbstractionImp {
public:
void operation()
{
cout << " ConcreteAstractionImpA" << endl;
}
~ConcreteAbstractionImpA()
{
cout << "ConcreteAbstractionImpA's ~" << endl;
}
};
class ConcreteAbstractionImpB : public AbstractionImp {
public:
void operation()
{
cout << " ConcreteAstractionImpB" << endl;
}
~ConcreteAbstractionImpB()
{
cout << "ConcreteAbstractionImpB's ~" << endl;
}
};
/*
* 抽象基类
*/
class Abstraction {
private:
AbstractionImp *aImp;
public:
Abstraction( AbstractionImp *aImp )
{
this->aImp = aImp;
}
virtual void operation() = 0;
virtual ~Abstraction()
{
}
void inter()
{
aImp->operation();
}
};
class RefindAbstraction1 : public Abstraction {
public:
RefindAbstraction1( AbstractionImp * aImp ) : Abstraction( aImp )
{
}
void operation()
{
cout << "RefindAbstraction1 is on";
inter();
}
~RefindAbstraction1()
{
cout << "RefindAbstraction1's ~" << endl;
}
};
class RefindAbstraction2 : public Abstraction {
public:
RefindAbstraction2( AbstractionImp * aImp ) : Abstraction( aImp )
{
}
void operation()
{
cout << "RefindAbstraction2 is on";
inter();
}
~RefindAbstraction2()
{
cout << "RefindAbstraction2's ~" << endl;
}
};
int main( void )
{
ConcreteAbstractionImpA *cAIa = new ConcreteAbstractionImpA();
Abstraction *Ra1 = new RefindAbstraction1( cAIa );
Ra1->operation();
delete cAIa;
delete Ra1;
return(0);
}
例子:
手机品牌和软件是两个概念,不同的软件可以在不同的手机上,不同的手机可以有相同的软件,两者都具有很大的变动性。如果我们单独以手机品牌或手机软件为基类来进行继承扩展的话,无疑会使类的数目剧增并且耦合性很高
常用的场景
1.当一个对象有多个变化因素的时候,考虑依赖于抽象的实现,而不是具体的实现。如上面例子中手机品牌有2种变化因素,一个是品牌,一个是功能。
2.当多个变化因素在多个对象间共享时,考虑将这部分变化的部分抽象出来再聚合/合成进来,如上面例子中的通讯录和游戏,其实是可以共享的。
3.当我们考虑一个对象的多个变化因素可以动态变化的时候,考虑使用桥接模式,如上面例子中的手机品牌是变化的,手机的功能也是变化的,所以将他们分离出来,独立的变化。
优点
1.将实现抽离出来,再实现抽象,使得对象的具体实现依赖于抽象,满足了依赖倒转原则。
2.将可以共享的变化部分,抽离出来,减少了代码的重复信息。
3.对象的具体实现可以更加灵活,可以满足多个因素变化的要求。
缺点
1.客户必须知道选择哪一种类型的实现。
考虑装操作
系统,
有多种配置的计算机,
同样也有多款操作系统。
如何运用桥接模式呢?可以将操作系统和
计算机分别抽象出来,让它们各自发展,减少它们的耦合度。当然了,两者之间有标准的接口。
这样设计,不论是对于计算机,还是操作系统都是非常有利的。
考虑装操作系统,有多种配置的计算机,同样也有多款操作系统。如何运用桥接模式呢?可以将操作系统和计算机分别抽象出来,让它们各自发展,减少它们的耦合度。当然了,两者之间有标准的接口。这样设计,不论是对于计算机,还是操作系统都是非常有利的。
#include <iostream>
using namespace std;
/*
* 实现基类
*/
class Os {
public:
virtual void install_imp() = 0;
virtual ~Os()
{
};
};
/*
* 具体实现类
*/
class LinuxOS : public Os {
public:
void install_imp()
{
cout << "安装linux系统!!" << endl;
}
};
class windowsOS : public Os {
public:
void install_imp()
{
cout << "安装windows系统!!" << endl;
}
};
class unixOS : public Os {
public:
void install_imp()
{
cout << "安装unix系统!!" << endl;
}
};
/*
* 抽象基类
*/
class computer {
private:
Os * os;
string name;
public:
computer( Os *os, string name )
{
this->os = os;
this->name = name;
}
virtual void install() = 0;
void inter()
{
cout << name << ":";
os->install_imp();
}
};
/*
* 具体抽象类
*/
class Applecomputer : public computer {
public:
Applecomputer( Os *os ) : computer( os, "Applecomputer" )
{
}
void install()
{
inter();
}
};
class dellcomputer : public computer {
public:
dellcomputer( Os *os ) : computer( os, "dellcomputer" )
{
}
void install()
{
inter();
}
};
class lenovocomputer : public computer {
public:
lenovocomputer( Os *os ) : computer( os, "lenovocomputer" )
{
}
void install()
{
inter();
}
};
int main( void )
{
Os * po1 = new windowsOS();
Os * po2 = new LinuxOS();
computer * pc1 = new lenovocomputer( po1 );
computer * pc2 = new dellcomputer( po2 );
pc1->install();
pc2->install();
delete po1;
delete po2;
delete pc1;
delete pc2;
return(0);
}
备注:
由于实现的方式有多种,桥接模式的核心就是把这些实现独立出来,让他们各自变化。
将抽象部分与它的实现部分分离:实现系统可能有多角度(维度)分类,每一种分类都可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。
在发现需要多角度去分类实现对象,而只用继承会造成大量的类增加,不能满足开放-封闭原则时,就要考虑用Bridge桥接模式了。
合成/聚合复用原则:尽量使用合成/聚合,精良不要使用类继承。
优先使用对象的合成/聚合将有助于保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。