Jungle有两个手机,分别是M手机和N手机,M手机上有游戏Game1,N手机上有Game2。每次Jungle想玩Game1时,就使用M手机,想玩Game2时,就玩N手机。要是某天Jungle外出,心情大好,两个游戏都想玩,那Jungle还得带上两个手机???麻不麻烦?
如果新出一个游戏Game3,那Jungle是不是要再买一个手机呢?
同样都是游戏软件,为什么不把所有游戏都装到一个手机上呢?
如果系统中的某个类存在两个独立变化的维度,通过桥接模式可以将这两个维度分离开来,使两者独立扩展。如同上述实例,Jungle想用手机玩游戏,手机和游戏是两个独立变化的维度,增加一个游戏对手机没有影响,增加一个手机对游戏也没有影响。手机上可以安装游戏,而游戏必须在手机上玩,从这个角度而言,手机和游戏之间存在较强的耦合。
但两者可以很好的解耦,且解耦后扩展灵活:所有游戏安装在一个手机上,新出一个游戏,新安装就ok!买了新手机,同样可以装上所有游戏。这就是桥接模式:
桥接模式:
将抽象部分与它的实现部分解耦,使得两者都能够独立变化。
桥接模式将两个独立变化的维度设计成两个独立的继承等级结构(而不会将两者耦合在一起形成多层继承结构),在抽象层将二者建立起一个抽象关联,该关联关系类似一座桥,将两个独立的等级结构连接起来,故曰“桥接模式”。
桥接模式UML图如下图。由图可知,桥接模式包含以下角色:
简言之,在Abstraction类中维护一个Implementor类指针,需要采用不同的实现方式的时候只需要传入不同的Implementor派生类就可以了。
以引言中的故事为例,Jungle学习了桥接模式后大受启发,想实现如下操作:
新手机上能够迅速在新手机上安装(setup)并玩(play)游戏
新增加一个游戏时Jungle能够在已有手机上安装并play
在这个实例里,手机是抽象类Abstraction,具有玩游戏这样的实现类接口Implementor,不同的手机品牌扩充抽象类RefinedAbstraction,多个不同的游戏则是具体实现类ConcreteImplementor。
//实现类接口
class Game
{
public:
Game(){}
virtual void play() = 0;
private:
};
//具体实现类GameA
class GameA:public Game
{
public:
GameA(){}
void play(){
printf("Jungle玩游戏A\n");
}
};
//具体实现类GameB
class GameB :public Game
{
public:
GameB(){}
void play(){
printf("Jungle玩游戏B\n");
}
};
实现类Game中声明了play的接口,不过它是一个虚方法,其实现在具体实现类GameA和GameB中定义。
//抽象类Phone
class Phone
{
public:
Phone(){
}
//安装游戏
virtual void setupGame(Game *igame) = 0;
virtual void play() = 0;
private:
Game *game;
};
//扩充抽象类PhoneA
class PhoneA:public Phone
{
public:
PhoneA(){
}
//安装游戏
void setupGame(Game *igame){
this->game = igame;
}
void play(){
this->game->play();
}
private:
Game *game;
};
//扩充抽象类PhoneB
class PhoneB :public Phone
{
public:
PhoneB(){
}
//安装游戏
void setupGame(Game *igame){
this->game = igame;
}
void play(){
this->game->play();
}
private:
Game *game;
};
抽象类Phone中也声明了两个虚方法,并且定义了一个实现类的对象,使抽象和实现具有关联关系。而对象的实例化则放在客户端使用时进行。
#include
#include "BridgePattern.h"
int main()
{
Game *game;
Phone *phone;
//Jungle买了PhoneA品牌的手机,想玩游戏A
phone = new PhoneA();
game = new GameA();
phone->setupGame(game);
phone->play();
printf("++++++++++++++++++++++++++++++++++\n");
//Jungle想在这个手机上玩游戏B
game = new GameB();
phone->setupGame(game);
phone->play();
system("pause");
return 0;
}
优点:
缺点:
适用场景:
设计模式系列文章:
设计模式——设计模式概述
设计模式(二)——UML类图介绍
设计模式(三)——面向对象设计原则
设计模式(四)——简单工厂模式
设计模式(五)——工厂方法模式
设计模式(六)——抽象工厂模式
设计模式(七)——建造者模式
设计模式(八)——原型模式
设计模式(九)——单例模式
设计模式(十)——适配器模式
源码和资料地址:https://github.com/FengJungle/DesignPattern
欢迎关注知乎专栏:Jungle是一个用Qt的工业Robot
欢迎关注Jungle的微信公众号:Jungle笔记