C++设计模式——桥接模式
来源:互联网 发布:淘宝客服 编辑:程序博客网 时间:2024/06/05 15:24
C++设计模式——桥接模式
概念
将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
场景描述
假如现在需要一个手机装配系统,从手机的角度,我们可以把手机分为苹果,诺基亚,华为,OPPO等等。从手机的操作系统又分为IOS,安卓,塞班等。而手机系统又有版本一说,可能老款的手机对新版本的系统没办法支持。所以要实现这么一个手机类型多样化,操作系统多样化的系统。我们该怎么实现呢?
一步一步进化到桥接模式
桥接模式具体是什么,我们先不不要知道。假如现在我们直接实现上述场景,我们会怎么实现呢?通过类图来说一下第一个版本的设计。
由于手机分为多种类型的,那么将手机抽象出来,在Phone中定义手机的公共接口,各种类型的手机通过继承来实现自己的接口。对特定的手机安装他们特定的操作系统。总体设计如上述类图一般。
这种实现的缺点:
很明显,当现在又要加一种手机的时候,手机的操作系统无法复用,我们每实现一个手机,就要在实现类中去实现它的操作系统。
如果现在要升级一个手机的操作系统,那么就必须修改实现类中的操作系统源码。这很显然是不合适的。
最主要的是上述类图中,我们要给一个手机添加操作系统的时候,为什么要通过继承来实现?手机和操作系统的关系应该是has-a的关系而不是is-a。
综上几点,该方案虽然最后可以勉强实现,但是是有问题的。
既然上述设计中通过继承来实现给手机添加操作系统是有问题的。那么我们换一种实现方式。类图如下。
该设计中是将这个需求考虑的比较清楚,不仅把多种多样的手机抽象化,而且将多种多样的操作系统也抽象化了。特定的手机需要特定的操作系统,通过组合来实现,如此一来,操作系统不管如何变,在特定的手机类的实现中是不需要改变的。总体来说,比第一种设计好太多了。但是依旧存在如下问题:
- 如果手机需要更换升级操作系统,那是不是需要修改所依赖的操作系统呢?
涉及到依赖的时候,我们不妨想想设计模式六大原则——依赖倒置原则,如果两个类存在依赖,那么要尽可能的去依赖其抽象,而不是其具体实现。所以在该实现中,是违背了依赖倒置原则。
针对上一种实现的缺点,我们将实现再升级一下。设计类图如下图所示:
该设计中,苹果手机已经不是依赖于IOS的具体实现,而是依赖于IOS的抽象。同样的诺基亚手机也是一样的实现。 那这样实现是不是就完美了呢?诚然不是的。假如有一天,诺基亚把苹果公司收购了,要把苹果手机的系统都更换成塞班系统的时候,我们所依赖的IOS的抽象是不是就需要修改了呢?所以该设计中虽然是把上一种设计升级了,但是依旧在一定的程度上违反了依赖倒置原则。
再针对上一种模式的缺点,我们再次设计类图如下。
如此实现,就完全克服了我们之前实现中所说的缺点。而这也是桥接模式的具体实现方式,将抽象和实现分离,这里的抽象和实现并不是C++继承中的抽象实现。这里的抽象指的是变化不大的接口,而实现指的是易变化的类(道行不深,个人简介)。这里的将抽象和实现分离,是将具有聚合/关联关系的两个类进行分离解耦,使得一个类的变化对另外一个类无影响。这就是我所理解的桥接模式。
代码实现
//OS.h#ifndef OS_H_#define OS_H_#include <iostream>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;}}#endifint 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;}
优缺点
优点:
将抽象与实现分离,提高了系统的可扩展性。
不管是在两个维度还是更高维度上变化,都只需要在各自维度的变化上修改即可,不需要修改原有系统。
缺点:
- 维度变化过多过杂,很难将单纯变化的两个维度分离开来。在设计上有一定难度。
适用场景
适用于有两个维度以上的变化,并且都需要扩展时。
适用于一个易于变化的对象需要在多个类中共享时。
- 设计模式——桥接模式(C++)
- 设计模式_桥接模式(C++)
- C#--设计模式之桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 【设计模式】——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- 设计模式——桥接模式
- [android] SharedPreferences
- 使用JDBCTemplate实现与Spring结合,方法公用 ——接口(BaseDao)
- SDUT 3929 校赛 D 魔戒
- Docker推送到我的仓库
- 文章标题
- C++设计模式——桥接模式
- maven 3 自动创建目录骨架
- CI多环境配置与hooks做权限出现的问题
- 更快的AtomicInteger
- 使用JDBCTemplate实现与Spring结合,方法公用 ——共用实现类(BaseImpl)
- Zookeeper单机伪集群配置
- udp通信
- hadoop put 强制覆盖文件
- Git基础-取得项目的Git仓库