C++设计模式之桥接模式

17 篇文章 0 订阅
14 篇文章 0 订阅

C++ 设计模式之桥接模式

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

一、缘由

假设现在需要大、中、小、三种型号的画笔来绘制7种不同的颜色,如果使用蜡笔,就需要准备 3*7 = 21支蜡笔。而如果使用毛笔,则只需要3支毛笔和7中颜料。显然使用毛笔的形式要比蜡笔简单许多,这其中缘由在于在蜡笔这个对象中型号和颜料两个不同维度的因素耦合太强,而毛笔的型号和颜色属于松耦合的关系。 在毛笔实例中,型号大小和颜色是分离的,可以独立变化的。 这就是桥接模式的基本思想:将不同维度的变化职责抽取出来形成各自的继承等级结构,从而能够让其分别独立变化。

二、实现

我们给出桥接模式的UML类图如下:

可以看到,在桥接模式中,存着左右两棵继承树,AbstractionImplementor,两棵继承树可以独立发展变化,互不干扰。
有这样的一个情景,在不同的操作系统上,音频解码的算法都是一致的,而播放声音的方法却不一致。我们要实现一个播放器能够在不同的操作系统上播放不同格式的音乐,这里有两个变化的维度:音频格式和操作系统。在学习桥接模式之前,我们的解决方案如下:

显然因为我们将音频解码播放音频的职责设计成了强耦合,于是我们的系统结构变得复杂了,这就是违反单一职责的后果。于是我们使用了桥接模式,将操作系统播放音频的职责抽取出来,成为一棵新的继承树,让播放音频和音频解码两个职责独立变化。这样播放器的音频播放部分被抽离了,也就是抽象和实现分离。使用了桥接模式的UML类图如下:

可以看到,使用了桥接模式之后的解决方案十分清晰明了。桥接模式的好处由于可见一斑。

三、代码

下面给出桥接模式的示例代码


#include <string>
#include <iostream>

using std::string;

class OperatinSystem {
public:
    OperatinSystem (){};
    virtual ~OperatinSystem (){};
    virtual void doPlay() = 0;
};

class Linux : public OperatinSystem {
public:
    Linux  (){};
    virtual ~Linux(){};
    virtual void doPlay(){
        std::cout << " Linux" << std::endl;
    }
};

class Unix : public OperatinSystem {
public:
    Unix(){};
    virtual ~Unix(){};
    virtual void doPlay(){
        std::cout << " Unix" << std::endl;
    }
};

class Windows : public OperatinSystem {
public:
    Windows  (){};
    virtual ~Windows(){};
    virtual void doPlay(){
        std::cout << " Windows" << std::endl;
    }
};

class Player {
protected:
    OperatinSystem *OS;
public:
    Player (): OS(NULL){};
    virtual void setOS(OperatinSystem *os){OS = os;}
    virtual ~Player (){};

    virtual void play() = 0;
};

class MP3Player : public Player{
public:
    MP3Player(){};
    virtual ~MP3Player(){};
    virtual void play(){
        std::cout << "Play MP3 under ";
        OS->doPlay();
    }
};

class WAVPlayer : public Player{
public:
    WAVPlayer(){};
    virtual ~WAVPlayer(){};
    virtual void play(){
        std::cout << "Play WAV under ";
        OS->doPlay();
    }
};

class WMAPlayer : public Player{
public:
    WMAPlayer(){};
    virtual ~WMAPlayer(){};
    virtual void play(){
        std::cout << "Play WMA under ";
        OS->doPlay();
    }
};

int main(void)
{
    Player *mp3Player = new MP3Player;
    mp3Player->setOS(new Linux);
    mp3Player->play();
    mp3Player->setOS(new Windows);
    mp3Player->play();
    mp3Player->setOS(new Unix);
    mp3Player->play();

    Player *wmalayer = new WMAPlayer;
    wmalayer->setOS(new Linux);
    wmalayer->play();
    wmalayer->setOS(new Windows);
    wmalayer->play();
    wmalayer->setOS(new Unix);
    wmalayer->play();

    Player *wavPlayer = new WAVPlayer;
    wavPlayer->setOS(new Linux);
    wavPlayer->play();
    wavPlayer->setOS(new Windows);
    wavPlayer->play();
    wavPlayer->setOS(new Unix);
    wavPlayer->play();
}

运行结果:

Play MP3 under  Linux
Play MP3 under  Windows
Play MP3 under  Unix
Play WMA under  Linux
Play WMA under  Windows
Play WMA under  Unix
Play WAV under  Linux
Play WAV under  Windows
Play WAV under  Unix

四、总结

桥接模式的优点 :

  • 分离了抽象和实现。桥接模式使用了聚合代替了原本类间的绑定关系(继承具有侵入性),实现松耦合。

  • 变化的因素之间相互隔离,独立变化,方便拓展。桥接模式在增加系统的新实现的时候,不需要修改已有类,符合开闭原则。

桥接模式的适用场景:

  • 当继承树过于复杂时,应该考虑使用桥接模式。
  • 当一个类中存在着多个变化的维度的时候,应该考虑桥接模式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值