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

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

概念

将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。

场景描述

    假如现在需要一个手机装配系统,从手机的角度,我们可以把手机分为苹果,诺基亚,华为,OPPO等等。从手机的操作系统又分为IOS,安卓,塞班等。而手机系统又有版本一说,可能老款的手机对新版本的系统没办法支持。所以要实现这么一个手机类型多样化,操作系统多样化的系统。我们该怎么实现呢?

一步一步进化到桥接模式

    桥接模式具体是什么,我们先不不要知道。假如现在我们直接实现上述场景,我们会怎么实现呢?通过类图来说一下第一个版本的设计。

C++设计模式——桥接模式_第1张图片

    由于手机分为多种类型的,那么将手机抽象出来,在Phone中定义手机的公共接口,各种类型的手机通过继承来实现自己的接口。对特定的手机安装他们特定的操作系统。总体设计如上述类图一般。

这种实现的缺点:

  • 很明显,当现在又要加一种手机的时候,手机的操作系统无法复用,我们每实现一个手机,就要在实现类中去实现它的操作系统。

  • 如果现在要升级一个手机的操作系统,那么就必须修改实现类中的操作系统源码。这很显然是不合适的。

  • 最主要的是上述类图中,我们要给一个手机添加操作系统的时候,为什么要通过继承来实现?手机和操作系统的关系应该是has-a的关系而不是is-a。

综上几点,该方案虽然最后可以勉强实现,但是是有问题的。

    既然上述设计中通过继承来实现给手机添加操作系统是有问题的。那么我们换一种实现方式。类图如下。

C++设计模式——桥接模式_第2张图片

    该设计中是将这个需求考虑的比较清楚,不仅把多种多样的手机抽象化,而且将多种多样的操作系统也抽象化了。特定的手机需要特定的操作系统,通过组合来实现,如此一来,操作系统不管如何变,在特定的手机类的实现中是不需要改变的。总体来说,比第一种设计好太多了。但是依旧存在如下问题:

  • 如果手机需要更换升级操作系统,那是不是需要修改所依赖的操作系统呢?

涉及到依赖的时候,我们不妨想想设计模式六大原则——依赖倒置原则,如果两个类存在依赖,那么要尽可能的去依赖其抽象,而不是其具体实现。所以在该实现中,是违背了依赖倒置原则。

    针对上一种实现的缺点,我们将实现再升级一下。设计类图如下图所示:

C++设计模式——桥接模式_第3张图片

    该设计中,苹果手机已经不是依赖于IOS的具体实现,而是依赖于IOS的抽象。同样的诺基亚手机也是一样的实现。 那这样实现是不是就完美了呢?诚然不是的。假如有一天,诺基亚把苹果公司收购了,要把苹果手机的系统都更换成塞班系统的时候,我们所依赖的IOS的抽象是不是就需要修改了呢?所以该设计中虽然是把上一种设计升级了,但是依旧在一定的程度上违反了依赖倒置原则。

    再针对上一种模式的缺点,我们再次设计类图如下。

C++设计模式——桥接模式_第4张图片

    如此实现,就完全克服了我们之前实现中所说的缺点。而这也是桥接模式的具体实现方式,将抽象和实现分离,这里的抽象和实现并不是C++继承中的抽象实现。这里的抽象指的是变化不大的接口,而实现指的是易变化的类(道行不深,个人简介)。这里的将抽象和实现分离,是将具有聚合/关联关系的两个类进行分离解耦,使得一个类的变化对另外一个类无影响。这就是我所理解的桥接模式。

代码实现

//OS.h
#ifndef OS_H_
#define OS_H_

#include 
using namespace std;

//抽象的操作系统类
class OS
{
public:
    virtual std::string GetOS() = 0;
};

//IOS类的实现
class IOS : public OS
{
public:
    virtual std::string GetOS()
    {
        return "IOS Operator System";
    }
};

//塞班类的实现
class SaiBian : public OS
{
public:
    virtual std::string GetOS()
    {
        return "SaiBian Operator System";
    }
};

//IOS某个版本的实现
class IOSSubSystem1 : public IOS
{
public:
    virtual std::string GetOS()
    {
        return "IOS 5.1.1 Operator System";
    }
};

//IOS某个版本的实现
class IOSSubSystem2 : public IOS
{
public:
    virtual std::string GetOS()
    {
        return "IOS 10.1.1 Operator System";
    }
};

//塞班系统某个类的实现
class SaiBianSubSystem1 : public SaiBian
{
public:
    virtual std::string GetOS()
    {
        return "SaiBian 1.1.0 Operator System";
    }
};

//塞班系统某个类的实现
class SaiBianSubSystem2 : public SaiBian
{
public:
    virtual std::string GetOS()
    {
        return "SaiBian 1.1.1 Operator System";
    }
};

#endif
//phone.h
#ifndef PHONE_H_
#define PHONE_H_

#include "OS.h"

#ifndef DELETE_OBJECT
#define DELETE_OBJECT(p) {if(NULL != (p)){delete (p); (p) = NULL;}}
#endif

//抽象的手机类
class Phone
{
public:
    virtual void SetOS() = 0;
};

//苹果手机类,依赖抽象的操作系统
class iPhone : public Phone
{
public:
    iPhone(OS* os)
    {
        m_pOS = os;
    }
    ~iPhone()
    {
        DELETE_OBJECT(m_pOS);
    }
    virtual void SetOS()
    {
        cout << "Set The OS: " << m_pOS->GetOS().c_str() << endl;
    }
private:
    OS* m_pOS;
};

//诺基亚手机类,以来抽象的操作系统
class Nokia : public Phone
{
public:
    Nokia(OS* os)
    {
        m_pOS = os;
    }
    ~Nokia()
    {
        DELETE_OBJECT(m_pOS);
    }

    virtual void SetOS()
    {
        cout << "Set The OS: " << m_pOS->GetOS().c_str() << endl;
    }
private:
    OS* m_pOS;
};
#endif
//client
#include <iostream>

#include "OS.h"
#include "Phone.h"

using namespace std;

#ifndef DELETE_OBJECT
#define DELETE_OBJECT(p) {if(NULL != (p)){delete (p); (p) = NULL;}}
#endif

int main()
{
    OS* pIOS1 = new IOSSubSystem1(); //创建一个操作系统
    Phone* iPhone4 = new iPhone(pIOS1);//应用到该手机上
    iPhone4->SetOS();

    OS* pIOS2 = new IOSSubSystem2();//创建一个操作系统
    Phone* iPhone6 = new iPhone(pIOS2);//应用到该手机上
    iPhone6->SetOS();

    OS* pSaiBian1 = new SaiBianSubSystem1();//创建一个操作系统
    Phone* Nokia1 = new Nokia(pSaiBian1);//应用到该手机上
    Nokia1->SetOS();

    OS* pSaiBian2 = new SaiBianSubSystem2();//创建一个操作系统
    Phone* Nokia2 = new Nokia(pSaiBian2);//应用到该手机上
    Nokia2->SetOS();

    DELETE_OBJECT(iPhone4);
    DELETE_OBJECT(iPhone6);
    DELETE_OBJECT(Nokia1);
    DELETE_OBJECT(Nokia2);
    return 0;
}

优缺点

优点:

  • 将抽象与实现分离,提高了系统的可扩展性。

  • 不管是在两个维度还是更高维度上变化,都只需要在各自维度的变化上修改即可,不需要修改原有系统。

缺点:

  • 维度变化过多过杂,很难将单纯变化的两个维度分离开来。在设计上有一定难度。

适用场景

  • 适用于有两个维度以上的变化,并且都需要扩展时。

  • 适用于一个易于变化的对象需要在多个类中共享时。

你可能感兴趣的:(设计模式)