C++设计模式编程中使用Bridge桥接模式的完全攻略_C 语言

桥接模式将抽象(Abstraction)与实现(Implementation)分离,使得二者可以独立地变化。

桥接模式典型的结构图为:

在桥接模式的结构图中可以看到,系统被分为两个相对独立的部分,左边是抽象部分,右边是实现部分,这两个部分可以互相独立地进行修改:例如上面问题中的客户需求变化,当用户需求需要从 Abstraction 派生一个具体子类时候,并不需要像上面通过继承方式实现时候需要添加子类 A1 和 A2 了。另外当上面问题中由于算法添加也只用改变右边实现(添加一个具体化子类),而右边不用在变化,也不用添加具体子类了。

一切都变得 elegant!

桥接模式号称设计模式中最难理解的模式之一,关键就是这个抽象和实现的分离非常让人奇怪,大部分人刚看到这个定义的时候都会认为实现就是继承自抽象,那怎么可能将他们分离呢。

《大话设计模式》中就Bridge模式的解释:

手机品牌和软件是两个概念,不同的软件可以在不同的手机上,不同的手机可以有相同的软件,两者都具有很大的变动性。如果我们单独以手机品牌或手机软件为基类来进行继承扩展的话,无疑会使类的数目剧增并且耦合性很高,(如果更改品牌或增加软件都会增加很多的变动)两种方式的结构如下:

所以将两者抽象出来两个基类分别是PhoneBrand和PhoneSoft,那么在品牌类中聚合一个软件对象的基类将解决软件和手机扩展混乱的问题,这样两者的扩展就相对灵活,剪短了两者的必要联系,结构图如下:

这样扩展品牌和软件就相对灵活独立,达到解耦的目的!

抽象基类及接口:

1、Abstraction::Operation():定义要实现的操作接口

2、AbstractionImplement::Operation():实现抽象类Abstaction所定义操作的接口,由其具体派生类ConcreteImplemenA、ConcreteImplemenA或者其他派生类实现。

3、在Abstraction::Operation()中根据不同的指针多态调用AbstractionImplement::Operation()函数。

理解:
Bridge用于将表示和实现解耦,两者可以独立的变化.在Abstraction类中维护一个AbstractionImplement类指针,需要采用不同的实现方式的时候只需要传入不同的AbstractionImplement派生类就可以了.

Bridge的实现方式其实和Builde十分的相近,可以这么说:本质上是一样的,只是封装的东西不一样罢了.两者的实现都有如下的共同点:

抽象出来一个基类,这个基类里面定义了共有的一些行为,形成接口函数(对接口编程而不是对实现编程),这个接口函数在Buildier中是BuildePart函数在Bridge中是Operation函数;

其次,聚合一个基类的指针,如Builder模式中Director类聚合了一个Builder基类的指针,而Brige模式中Abstraction类聚合了一个AbstractionImplement基类的指针(优先采用聚合而不是继承);

而在使用的时候,都把对这个类的使用封装在一个函数中,在Bridge中是封装在Director::Construct函数中,因为装配不同部分的过程是一致的,而在Bridge模式中则是封装在Abstraction::Operation函数中,在这个函数中调用对应的AbstractionImplement::Operation函数.就两个模式而言,Builder封装了不同的生成组成部分的方式,而Bridge封装了不同的实现方式.

桥接模式就将实现与抽象分离开来,使得RefinedAbstraction依赖于抽象的实现,这样实现了依赖倒转原则,而不管左边的抽象如何变化,只要实现方法不变,右边的具体实现就不需要修改,而右边的具体实现方法发生变化,只要接口不变,左边的抽象也不需要修改。

优点
1.将实现抽离出来,再实现抽象,使得对象的具体实现依赖于抽象,满足了依赖倒转原则。

2.将可以共享的变化部分,抽离出来,减少了代码的重复信息。

3.对象的具体实现可以更加灵活,可以满足多个因素变化的要求。

缺点
客户必须知道选择哪一种类型的实现。
设计中有超过一维的变化我们就可以用桥模式。如果只有一维在变化,那么我们用继承就可以圆满的解决问题。

代码示例:

Abstraction.h

#ifndef _ABSTRACTION_H_
#define _ABSTRACTION_H_

class AbstractionImplement;

class Abstraction
{
public:
  virtual void Operation()=0;//定义接口,表示该类所支持的操作
  virtual ~Abstraction();
protected:
  Abstraction();
};

class RefinedAbstractionA:public Abstraction
{
public:
  RefinedAbstractionA(AbstractionImplement* imp);//构造函数
  virtual void Operation();//实现接口
  virtual ~RefinedAbstractionA();//析构函数
private:
  AbstractionImplement* _imp;//私有成员
};

class RefinedAbstractionB:public Abstraction
{
public:
  RefinedAbstractionB(AbstractionImplement* imp);//构造函数
  virtual void Operation();//实现接口
  virtual ~RefinedAbstractionB();//析构函数
private:
  AbstractionImplement* _imp;//私有成员
};
#endif

Abstraction.cpp
#include "Abstraction.h"
#include "AbstractionImplement.h"
#include <iostream>

using namespace std;

Abstraction::Abstraction()
{}

Abstraction::~Abstraction()
{}

RefinedAbstractionA::RefinedAbstractionA(AbstractionImplement* imp)
{
  this->_imp = imp;
}

RefinedAbstractionA::~RefinedAbstractionA()
{
  delete this->_imp;
  this->_imp = NULL;
}

void RefinedAbstractionA::Operation()
{
  cout << "RefinedAbstractionA::Operation" << endl;
  this->_imp->Operation();
}

RefinedAbstractionB::RefinedAbstractionB(AbstractionImplement* imp)
{
  this->_imp = imp;
}

RefinedAbstractionB::~RefinedAbstractionB()
{
  delete this->_imp;
  this->_imp = NULL;
}

void RefinedAbstractionB::Operation()
{
  cout << "RefinedAbstractionB::Operation" << endl;
  this->_imp->Operation();
}

AbstractImplement.h
#ifndef _ABSTRACTIONIMPLEMENT_H_
#define _ABSTRACTIONIMPLEMENT_H_

//抽象基类,定义了实现的接口
class AbstractionImplement
{
public:
  virtual void Operation()=0;//定义操作接口
  virtual ~AbstractionImplement();
protected:
  AbstractionImplement();
};

// 继承自AbstractionImplement,是AbstractionImplement的不同实现之一
class ConcreteAbstractionImplementA:public AbstractionImplement
{
public:
  ConcreteAbstractionImplementA();
  void Operation();//实现操作
  ~ConcreteAbstractionImplementA();
protected:
};

// 继承自AbstractionImplement,是AbstractionImplement的不同实现之一
class ConcreteAbstractionImplementB:public AbstractionImplement
{
public:
  ConcreteAbstractionImplementB();
  void Operation();//实现操作
  ~ConcreteAbstractionImplementB();
protected:
};
#endif

AbstractImplement.cpp
#include "AbstractionImplement.h"
#include <iostream>

using namespace std;

AbstractionImplement::AbstractionImplement()
{}

AbstractionImplement::~AbstractionImplement()
{}

ConcreteAbstractionImplementA::ConcreteAbstractionImplementA()
{}

ConcreteAbstractionImplementA::~ConcreteAbstractionImplementA()
{}

void ConcreteAbstractionImplementA::Operation()
{
  cout << "ConcreteAbstractionImplementA Operation" << endl;
}

ConcreteAbstractionImplementB::ConcreteAbstractionImplementB()
{}

ConcreteAbstractionImplementB::~ConcreteAbstractionImplementB()
{}

void ConcreteAbstractionImplementB::Operation()
{
  cout << "ConcreteAbstractionImplementB Operation" << endl;
}

main.cpp

#include "Abstraction.h"
#include "AbstractionImplement.h"
#include <iostream>

using namespace std;

int main()
{
  /* 将抽象部分与它的实现部分分离,使得它们可以独立地变化

  1、抽象Abstraction与实现AbstractionImplement分离;

  2、抽象部分Abstraction可以变化,如new RefinedAbstractionA(imp)、new RefinedAbstractionB(imp2);

  3、实现部分AbstractionImplement也可以变化,如new ConcreteAbstractionImplementA()、new ConcreteAbstractionImplementB();

  */

  AbstractionImplement* imp = new ConcreteAbstractionImplementA();    //实现部分ConcreteAbstractionImplementA
  Abstraction* abs = new RefinedAbstractionA(imp);            //抽象部分RefinedAbstractionA
  abs->Operation();

  cout << "-----------------------------------------" << endl;

  AbstractionImplement* imp1 = new ConcreteAbstractionImplementB();    //实现部分ConcreteAbstractionImplementB
  Abstraction* abs1 = new RefinedAbstractionA(imp1);            //抽象部分RefinedAbstractionA
  abs1->Operation();

  cout << "-----------------------------------------" << endl;

  AbstractionImplement* imp2 = new ConcreteAbstractionImplementA();    //实现部分ConcreteAbstractionImplementA
  Abstraction* abs2 = new RefinedAbstractionB(imp2);            //抽象部分RefinedAbstractionB
  abs2->Operation();

  cout << "-----------------------------------------" << endl;

  AbstractionImplement* imp3 = new ConcreteAbstractionImplementB();    //实现部分ConcreteAbstractionImplementB
  Abstraction* abs3 = new RefinedAbstractionB(imp3);            //抽象部分RefinedAbstractionB
  abs3->Operation();

  cout << endl;
  return 0;
}

代码说明:
Bridge模式将抽象和实现分别独立实现,在代码中就是Abstraction类和AbstractionImplement类。

使用组合(委托)的方式将抽象和实现彻底地解耦,这样的好处是抽象和实现可以分别独立地变化,系统的耦合性也得到了很好的降低。
GoF的那句话中的“实现”该怎么去理解:“实现”特别是和“抽象”放在一起的时候我们“默认”的理解是“实现”就是“抽象”的具体子类的实现,但是这里GoF所谓的“实现”的含义不是指抽象基类的具体子类对抽象基类中虚函数(接口)的实现,是和继承结合在一起的。而这里的“实现”的含义指的是怎么去实现用户的需求,并且指的是通过组合(委托)的方式实现的,因此这里的实现不是指的继承基类、实现基类接口,而是指的是通过对象组合实现用户的需求。

实际上上面使用Bridge模式和使用带来问题方式的解决方案的根本区别在于是通过继承还是通过组合的方式去实现一个功能需求。

备注:

由于实现的方式有多种,桥接模式的核心就是把这些实现独立出来,让他们各自变化。

将抽象部分与它的实现部分分离:实现系统可能有多角度(维度)分类,每一种分类都可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。

在发现需要多角度去分类实现对象,而只用继承会造成大量的类增加,不能满足开放-封闭原则时,就要考虑用Bridge桥接模式了。

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

适用场景:

  • 你不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
  • 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时B r i d g e 模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
  • 对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
  • (C + +)你想对客户完全隐藏抽象的实现部分。在C + +中,类的表示在类接口中是可见的。
  • 有许多类要生成。这样一种类层次结构说明你必须将一个对象分解成两个部分。R u m b a u g h 称这种类层次结构为“嵌套的普化”(nested generalizations )。
  • 你想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。一个简单的例子便是C o p l i e n 的S t r i n g 类[ C o p 9 2 ],在这个类中多个对象可以共享同一个字符串表示(S t r i n g R e p )。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c++
, 设计模式
, 桥接模式
bridge
bridge桥接模式、编程语言实现模式、编程语言实现模式 pdf、无线路由器桥接bridge、路由 桥接nat bridge,以便于您获取更多的相关知识。

时间: 2016-03-13

C++设计模式编程中使用Bridge桥接模式的完全攻略_C 语言的相关文章

设计模式学习笔记(四)—Bridge桥接模式

<设计模式>一书对Bridge是这样描述的: 将抽象与其实现解耦,使它们都可以独立地变化. 大致意思是说:将一组实现与另一组使用他们的对象分离.这里的实现指的是抽象类及其派生类用来实现自己的对象(而不是抽象类的派生类,这些派生类被称为具体类).下面 是<Design Patterns Explained>书中的例子.其结构图如下: 下面是它的实现: abstract class Shape{ protected Drawing myDrawing; abstract public

实例讲解PHP设计模式编程中的简单工厂模式_php实例

简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Factory Method)模式.简单工厂模式是由一个工厂对象决定创建出那一种产品类的实例. 1.工厂模式的几种形态工厂模式专门负责将大量有共同接口的类实例化.工厂模式可以动态决定将哪一个类实例化,不必事先知道每次要实例化哪一个类.工厂模式有以下几种形态: (1)简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory Method Pattern). (2)工厂方法(Factory Meth

.NET设计模式(9):桥接模式(Bridge Pattern)

概述 在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种"多维度的变化"?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就要使用Bridge模式. 意图 将抽象部分与实现部分分离,使它们都可以独立的变化.[GOF <设计模式>] 结构图 图1 Bridge模式结构图 生活中的例子 桥接模式将抽象部分与它的实现分离,使它们能够独立地变化.一个普通的开关控制的电灯.电风扇等等,都是桥接的例子.开关的目

php设计模式 Bridge (桥接模式)

复制代码 代码如下: <?php /** * 桥接模式 * * 将抽象部份与它实现部分分离,使用它们都可以有独立的变化 */ abstract class Implementor { abstract public function operation(); } class ConcreteImplementorA extends Implementor { public function operation() { echo "ConcreteImplementorA Operation

详解Java设计模式编程中命令模式的项目结构实现_java

正论: 命令模式把一个请求或者操作封装到一个对象中.命令模式运行系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 通俗: 其实很好理解.命令模式,关心的就是命令(或者称为操作).打个比方.在一个公司里面,整个运作就像一个系统.某个boss发布了一个命令,中层领导接到这个命令,然后指派给具体负责这个员工.整个流程很清晰吧.有一个需求,如何将这个流程固定下来,形成一个系统.我们只要抓住了重点:命令.将它抽取出来,其他的都迎刃而解了.抽取出命令,封装成一个独

详解备忘录模式及其在Java设计模式编程中的实现_java

1. 定义在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. 2. 使用的原因想要恢复对象某时的原有状态. 3. 适用的情况举例有很多备忘录模式的应用,只是我们已经见过,却没细想这是备忘录模式的使用罢了,略略举几例: eg1. 备忘录在jsp+javabean的使用: 在一系统中新增帐户时,在表单中需要填写用户名.密码.联系电话.地址等信息,如果有些字段没有填写或填写错误,当用户点击"提交"按钮时,需要在新增页面上保存

详解Java设计模式编程中的Flyweight享元模式的开发结构_java

享元(Flyweight)模式:通过共享技术以便有效的支持大量细粒度的对象. 享元模式在阎宏的<java与模式>中分为单纯享元模式和复合享元模式,复合模式的复合享元是不可以共享的,享元对象能做到共享的关键是区分内蕴态(Internal State)和外蕴态( External State).这两个"蕴态"翻译的太难懂,我不是说翻译的不好,可能是我理解能力差,还是<Design Pattern Elements of Reusable Object-Oriented S

Java设计模式编程中简单工厂与抽象工厂模式的使用实例_java

简单工厂模式类图 通过一个工厂类,以一个条件来创建对应的对象 //业务功能 public interface ICalculation { double getResult(double numA, double numB); } public class CalcAdd implements ICalculation { @Override public double getResult(double numA, double numB) { System.out.println("加法&qu

WPS演示中页眉页脚的使用攻略

  通过对WPS文字的了解,我们知道在编辑文档的时候,常常会为了更好的辨认清文章的顺序,会使用"页眉页脚"的功能,那么在WPS演示中,如何插入页眉页脚呢,小编现在就来和大家分享下. 首先打开wps演示,在菜单栏插入选项中找到"页眉和页脚"选项图标,如下图所示: 插入页眉页脚 打开"页眉和页脚"对话框,我们可以根据自己的需求填写,添加完成后点击"全部应用"或"应用"按钮,其中全部应用将会在所有的ppt中插入页

WPS文字中素材库最全的使用攻略

使用过WPS的朋友,一定对WPS文字中的素材有很深的印象吧.素材是文档编辑好坏的整体体现,如果在编辑文章的时候,导入一份好的素材,将产生事半功倍的效果.而对于素材的收集,小编就教大家几招. 一.填内容 轻松取用在线素材 在启动WPS Office 2012后,单击主界面右上角的"素材库"按钮,即可打开"WPS素材库"窗口.素材分为"在线素材"和"我的素材","在线素材"下又有很多的分类,比如图形.按钮.符号