peter_luy
8/15/2017 - 11:15 AM

桥接模式

桥接模式

#桥接模式
##一、UML图

##二、概念

桥接模式(Bridge):将抽象部分与它的实现部分分离,使他们都可以独立地变化。

##三、说明

为什么叫“桥接模式”?

如上所示的UML图中,有一个聚合线,像一座桥,所以叫“桥接模式”。

概念解释:

什么叫抽象与它的实现分离,这并不是说,让抽象类与其派生类分离,因为这没有任何意义。实现指的是抽象类与它的派生类用来实现自己的对象。

为什么要优先使用合成/聚合原则?
(1)对象的继承关系是在编译时就定义好了的,所以无法再运行时改变从父类继承的实现。子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

(2)优先使用合成/聚合原则有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

盲目使用继承会造成麻烦,本质原因是什么?
继承是一种强耦合的结构。父类变,子类就必须要变。所以,我们在用继承时,一定要在是“is-a”的关系时再考虑使用,而不是任何时候都去使用。

##四、C++实现

(1)Bridge.h

//Implementor:此处为手机软件抽象类
class HandsetSoft
{
public:
virtual void Run()=0;
};

//ConcreteImplementorA,此处为手机游戏
class HandsetGame:public HandsetSoft
{
void Run()
{
std::cout<<"运行手机游戏"<<std::endl;
}
};

//ConcreteImplementorB,此处为手机通讯录
class HandsetAddressList:public HandsetSoft
{
void Run()
{
std::cout<<"运行手机通讯录"<<std::endl;
}
};

//Abstraction,此处为手机品牌抽象类
class HandsetBrand
{
protected:
HandsetSoft* soft;
public:
void SetHandsetSoft(HandsetSoft* soft)
{
this->soft=soft;
}
virtual void Run()=0;
};

//RefineAbstraction,此处为手机品N
class HandsetBrandN:public HandsetBrand
{
public:
void Run()
{
soft->Run();
}
};

//RefineAbstraction,此处为手机品M
class HandsetBrandM:public HandsetBrand
{
public:
void Run()
{
soft->Run();
}
};

(2).Client.cpp

//Client,客户端
void main()
{
//手机品牌N
std::cout<<"手机品牌N:"<<std::endl;
HandsetBrand* ab=new HandsetBrandN();
ab->SetHandsetSoft(new HandsetGame());
ab->Run();
ab->SetHandsetSoft(new HandsetAddressList());
ab->Run();
delete ab;
//手机品牌M
std::cout<<std::endl<<"手机品牌M:"<<std::endl;
ab=new HandsetBrandM();
ab->SetHandsetSoft(new HandsetGame());
ab->Run();
ab->SetHandsetSoft(new HandsetAddressList());
ab->Run();
delete ab;
ab=NULL;
std::cout<<std::endl;
system("pause");
}

(3).运行截图


软件领域中的设计模式为开发人员提供了一种使用专家设计经验的有效途径。设计模式中运用了面向对象编程语言的重要特性:封装、继承、多态,真正领悟设计模式的精髓是可能一个漫长的过程,需要大量实践经验的积累。最近看设计模式的书,对于每个模式,用C写了个小例子,加深一下理解。主要参考《大话设计模式》和《设计模式:可复用面向对象软件的基础》两本书。本文介绍桥接模式的实现。
[DP]书上定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。考虑装操作系统,有多种配置的计算机,同样也有多款操作系统。如何运用桥接模式呢?可以将操作系统和计算机分别抽象出来,让它们各自发展,减少它们的耦合度。当然了,两者之间有标准的接口。这样设计,不论是对于计算机,还是操作系统都是非常有利的。下面给出这种设计的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);
}


关于桥接模式:

-实现系统可能有多角度分类,每一种分类都可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。
-聚合其实是一种弱拥有的关系,A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。

我的一点想法:
初一看,桥接模式和抽象工厂有些类似,其实不然。抽象工厂中几个工厂和产品,都是一样的,比如HIK、DH、UNV都是工厂,他们应该是并列的,没有从属关系,而NVR、IPC等产品几个工厂都有,但又不尽相同,所以,在抽象工厂中,有工厂的抽象和产品的抽象,同时,每个工厂同时又多个产品(产品族)。而桥接模式,从上面的例子可见,手机品牌和手机软件(电脑品牌和操作系统)存在弱拥有的关系,而不是并列的关系,任何一个手机都拥有手机软件(精髓),所以,手机品牌是一个抽象类,手机软件也是一个抽象类,其下分别派生出多个手机品牌和手机软件,手机品牌改变或者手机软件调整都是内部的事情,与客户端无关。要注意的是,客户端在调用的过程中,先要去设置从属中属(也就是手机和电脑)类中的从类的对象,例子中软件类的对象一定是手机类的成员。