C++中的桥接设计模式

 

 本文简单介绍C++中的桥接设计模式。主要是为了隐藏数据以及减轻编译时的压力。

   通常的做法是另外定义一个新的类,其接口和原来的类一致,但是其数据全部隐藏在新的类中。例如我们定义A类,然后定义一个新类B和其有相同的接口,其数据成员都在新类中。引用A类的代码就不需要重新编译。    

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

class Cimpl; // forward declaration

class C {

    public:

        C(int val);

        ~C();

        int get_a() const;

        int get_b() const;

    private:

        Cimpl *impl_;

};

                                 

class Cimpl {

    public:

        Cimpl(int val):a_(val), _(a_){}

        ~Cimpl();

        int get_a() const {return a_;}

        int get_b() const {return b_;}

    private:

        int a_;

        int b_;

};

                                 

C::C(int val): impl_(new Cimpl(val)){}

C::~C(){ delete impl_;}

int C::get_a() { return impl_->get_a();}

int C::get_b() { return impl_->get_b();}

   C中的代码如果不发生变化其内存布局就不会发生变化。以后只要修改实现类Cimpl即可。这种被称为编译防火墙。这种技术被广泛应用,也被成为”pimpl用法“,“柴郡猫技术” ( Cheshire Cat technique)。使用这种技术后我们代码使用C类不必重新编译。

   但是使用这种技术也会带来缺点。第一,内联函数失效,因为都是使用另外的调用实现的。第二,原来只需要一个类,现在用2个类来表示。

 

来自

Impl:

实现Implement)在指导教师的帮助、指导下实现控制器的设计培养中帮助程度的掌握既不包办代替又不放任自流。

 

来自

 

这就是桥接设计模式,将抽象和实现进行分类,派生类只继承抽象的接口,实现由实现类impl来完成。因为子类不需要重写父类的非纯虚函数。

 

设计模式之桥接模式

桥接模式,将抽象部分与它的实现部分分离,使它们都可以独立地变化。这里说的实现分离,并不是说然抽象类与派生类分离,这没有任何意义。实现指的是抽象类和它的派生类用来实现自己的对象。其UML图如下:

 

 

结合上图理解一下其定义。抽象部分指的是Abstraction或者RefinedAbstraction。实现部分指的是Implementor。以为抽象部分得实现有多种变化,为了减少耦合,所以把实现部分得变化封装起来,通过组合得方式实现。这就是定义中说得将抽象部分与它得实现部分分离。而定义中说得实现指得是抽象类和它得派生类用来实现自己得对象就是对变化封装后得类,即Implementor。举个例子就很好理解啦。就拿电脑来举例子吧。电脑又有微软得电脑,联想得电脑。而电脑上可以运行游戏,也可以运行记事本等软件。有两种方式可以实现电脑产品,如下:

 

方式一:

 

这种方式耦合性太高,比如像添加一种软件,需要添加微软电脑的软件和联想电脑的软件。同样的像添加一种电脑,也要添加相应的软件。如果要修改一种软件,则要修改很多软件。

 

方式二,那就是电脑是电脑,软件是软件两者分离。如下图:

 

很明显方式二耦合性底,容易维护和扩展。这里电脑就相当于抽线,而软件则相当于实现。总的来说就是桥接模式主要是实现系统可能有多个角度分类,每一种分类都有可能变化。那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。

 

以电脑和软件为例其示例代码如下:

---------------------

作者:konglongdanfo1

来源:CSDN

原文:https://blog.csdn.net/konglongdanfo1/article/details/83381476

版权声明:本文为博主原创文章,转载请附上博文链接!

软件领域中的设计模式为开发人员提供了一种使用专家设计经验的有效途径。设计模式中运用了面向对象编程语言的重要特性:封装、继承、多态,真正领悟设计模式的精髓是可能一个漫长的过程,需要大量实践经验的积累。最近看设计模式的书,对于每个模式,用C++写了个小例子,加深一下理解。主要参考《大话设计模式》和《设计模式:可复用面向对象软件的基础》两本书。本文介绍桥接模式的实现。

 

       [DP]书上定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。考虑装操作系统,有多种配置的计算机,同样也有多款操作系统。如何运用桥接模式呢?可以将操作系统和计算机分别抽象出来,让它们各自发展,减少它们的耦合度。当然了,两者之间有标准的接口。这样设计,不论是对于计算机,还是操作系统都是非常有利的。下面给出这种设计的UML图,其实就是桥接模式的UML图。

 

 

 

         给出C++的一种实现:

//操作系统

class OS

{

public:

virtual void InstallOS_Imp() {}

};

class WindowOS: public OS

{

public:

void InstallOS_Imp() { cout<<"安装Window操作系统"<InstallOS_Imp(); }

};

class AppleComputer: public Computer

{

public:

void InstallOS(OS *os) { os->InstallOS_Imp(); }

};

class HPComputer: public Computer

{

public:

void InstallOS(OS *os) { os->InstallOS_Imp(); }

};

        客户使用方式:



int main()

{

OS *os1 = new WindowOS();

OS *os2 = new LinuxOS();

Computer *computer1 = new AppleComputer();

computer1->InstallOS(os1);

computer1->InstallOS(os2);

}

       本人享有博客文章的版权,转载请标明出处 http://blog.csdn.net/wuzhekai1985

 

     优点:1)将可能变化的部分单独封装起来,使得变化产生的影响最小,不用编译不必要的第代码。

                    2)抽象部分和实现部分可以单独的变动,并且每一部分的扩充都不会破坏桥梁模式搭起来架子。

                    3)实现细节对客户透明

        缺点:1)结构比较复杂。

                    2)抽象类的修改影响到子类。

Bridge模式的产生原因:

 

         总结面向对象实际上就两句话:一是松耦合(Coupling),二是高内聚(Cohesion)。面向对象系统追求的目标就是尽可能地提高系统模块内部的内聚(Cohesion)、尽可能降低模块间的耦合(Coupling)。然而这也是面向对象设计过程中最为难把握的部分,大家肯定在OO系统的开发过程中遇到这样的问题:

 

(1).客户给了你一个需求,于是使用一个类来实现(A);

 

(2).客户需求变化,有两个算法实现功能,于是改变设计,我们通过一个抽象的基类,再定义两个具体类实现两个不同的算法(A1和A2);

 

(3).客户又告诉我们说对于不同的操作系统,于是再抽象一个层次,作为一个抽象基类A0,在分别为每个操作系统派生具体类(A00和A01,其中A00表示原来的类A)实现不同操作系统上的客户需求,这样我们就有了一共4个类。

 

(4).可能用户的需求又有变化,比如说又有了一种新的算法……..

 

(5).我们陷入了一个需求变化的郁闷当中,也因此带来了类的迅速膨胀。

 

Bridge模式则正是解决了这类问题。

 

Bridge模式的作用:

 

        作用:将抽象部份与它的实现部份分离,使它们都可以独立地变化。将抽象(Abstraction)与实现(Implementation)分离,使得二者可以独立地变化。桥接模式号称设计模式中最难理解的模式之一,关键就是这个抽象和实现的分离非常让人奇怪,大部分人刚看到这个定义的时候都会认为实现就是继承自抽象,那怎么可能将他们分离呢。

 

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

 

        手机品牌和软件是两个概念,不同的软件可以在不同的手机上,不同的手机可以有相同的软件,两者都具有很大的变动性。如果我们单独以手机品牌或手机软件为基类来进行继承扩展的话,无疑会使类的数目剧增并且耦合性很高,将两者抽象出来两个基类分别是PhoneBrand和PhoneSoft,那么在品牌类中聚合一个软件对象的基类将解决软件和手机扩展混乱的问题,这样两者的扩展就相对灵活,剪短了两者的必要联系,结构图如下:

 

                                 

 

Bridge模式的UML结构图如图1所示:

 

                

 

Bridge模式构成:

 

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依赖于抽象的实现,这样实现了依赖倒转原则,而不管左边的抽象如何变化,只要实现方法不变,右边的具体实现就不需要修改,而右边的具体实现方法发生变化,只要接口不变,左边的抽象也不需要修改。就像给不同的电脑安装不同的操作系统一样。,

 

原文:https://blog.csdn.net/fanyun_01/article/details/51766505

版权声明:本文为博主原创文章,转载请附上博文链接!

Bridge模式适用场景:

 

1.当一个对象有多个变化因素的时候,考虑依赖于抽象的实现,而不是具体的实现。如上面例子中手机品牌有2种变化因素,一个是品牌,一个是功能。

 

2.当多个变化因素在多个对象间共享时,考虑将这部分变化的部分抽象出来再聚合/合成进来,如上面例子中的通讯录和游戏,其实是可以共享的。

 

3.当我们考虑一个对象的多个变化因素可以动态变化的时候,考虑使用桥接模式,如上面例子中的手机品牌是变化的,手机的功能也是变化的,所以将他们分离出来,独立的变化。

 

总结:

 

1.设计中有超过一维的变化我们就可以用桥模式。如果只有一维在变化,那么我们用继承就可以圆满的解决问题。

Bridge模式优缺点总结:

 

优点:

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

 

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

 

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

 

缺点:

1.客户必须知道选择哪一种类型的实现。

 

Bridge模式使用总结:

 

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

 

使用组合(委托)的方式将抽象和实现彻底地解耦,这样的好处是抽象和实现可以分别独立地变化,系统的耦合性也得到了很好的降低。

       GoF的那句话中的“实现”该怎么去理解:“实现”特别是和“抽象”放在一起的时候我们“默认”的理解是“实现”就是“抽象”的具体子类的实现,但是这里GoF所谓的“实现”的含义不是指抽象基类的具体子类对抽象基类中虚函数(接口)的实现,是和继承结合在一起的。而这里的“实现”的含义指的是怎么去实现用户的需求,并且指的是通过组合(委托)的方式实现的,因此这里的实现不是指的继承基类、实现基类接口,而是指的是通过对象组合实现用户的需求。

 

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

 

备注:

 

        由于实现的方式有多种,桥接模式的核心就是把这些实现独立出来,让他们各自变化。将抽象部分与它的实现部分分离:实现系统可能有多角度(维度)分类,每一种分类都可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。在发现需要多角度去分类实现对象,而只用继承会造成大量的类增加,不能满足开放-封闭原则时,就要考虑用Bridge桥接模式了。合成/聚合复用原则:尽量使用合成/聚合,精良不要使用类继承。

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

 

 

---------------------

作者:老樊Lu码

来源:CSDN

原文:https://blog.csdn.net/fanyun_01/article/details/51766505

版权声明:本文为博主原创文章,转载请附上博文链接!

一个结构体就是一个模板。所以说模板实在是太好用了。

 

你可能感兴趣的:(C++,技术类杂项)