设计模式之桥接模式

2018-09-20 10:53:13ios

前言

  滥用继承会带来麻烦(实际上何止是麻烦,还会带来性能上的额外开销,关于继承的问题,能够查看继承相关的文章)。好比,对象的继承关系是在编译时就定义好了,因此没法在运行时改变从父类继承的实现。子类实现与它的父类有很是紧密的依赖关系。以致于父类中的任何变化必然会致使子类发生变化。当你须要复用子类时,若是继承下来的实现不适合解决新的问题,则父类必须重写或被其它更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。致使这种问题的缘由是继承是一种强耦合的关系,是is -a 的关系。设计模式

合成聚合原则

  优先使用对象合成、聚合,而不是类继承。(想一下两种模式的适配器,更推荐使用对象适配器就是这个原则的体现了)ide

  合成(Composition,有时也叫组合)和聚合都是关联的特殊种类。聚合表示一种弱的拥有关系,体现的是A对象能够包含B对象,可是B对象不是A对象的一部分;合成则是一种强的拥有关系,体现了严格的总体和部分的关系,部分和总体的生命周期是同样的。优先使用对象的聚合、合成将有助于你保持每一个类被封装,并被集中在单个任务上,这样类和类的继承就会保持较小的规模,而且不太可能增加为不可控制的庞然大物。性能

  聚合和组合关系,这实际上是须要从宏观上来理解的,而不能从微观角度理解。组合必然是几个组件,你们每一个都不能少,少了就不是一个总体,这是一个强拥有的关系。而聚合,多你一个很少,少你一个很多,就像《大话设计模式》里讲的大雁和雁群的关系,大雁A、大雁B、大雁C、大雁D等无数多只大雁聚合在一块儿就是一个雁群,但是你能说大雁A离开了雁群这个雁群就不是雁群了吗?显然不能。可是在具体实现上,其实大都是以类的数据成员来实现的。最多就是在组合的状况下class内嵌套class,可是这种作法并很差,由于过多的嵌套,可能会形成这个类的臃肿。编码

桥接模式

  桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们均可以独立地变化。这里的抽象与它的实现分离,并非指让抽象类和派生类分类,这没有任何意义。实现指的是抽象类和它的派生类用来实现本身的对象。其实就是说,一个系统的实现,可能有多个角度的分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独自变化。举例来讲,手机便可以按照品牌分类(手机是抽象,功能是实现),也能够按照功能分类(功能是实现,而手机变成了抽象)。spa

按照品牌分类实现类图:设计

  按照功能分类实现结构图:3d

  能够看到上面两种接口图,模块与模块之间有着极强的耦合度,对按照手机品牌分类的结构中,当有新手机品牌加入的时候,你须要为这一款手机从新开发功实现TaoBao、Game(我知道你会说软件会兼容,可是这里暂时不考虑这个,就假设没有兼容性)。对按照功能来进行分类的时候,每增长一个新功能就要为对应的品牌独立开发相应的功能。指针

  实现方式是多种多样的,而桥接模式的核心意图就是把实现独立出来,让它们各自独立的变化,而且它们本身的变化不会影响到其它的实现,从而达到应对变化的目的。按照桥接的思路进行设计结构图以下:code

桥接模式UML类图

  • Abstraction(抽象类):用于定义抽象类的接口,而且维护一个指向 Implementor 实现类的指针。它与 Implementor 之间具备聚合关系。
  • RefinedAbstraction(扩充抽象类):扩充由 Abstraction 定义的接口,在 RefinedAbstraction 中能够调用在 Implementor 中定义的业务方法。
  • Implementor(实现类接口):定义实现类的接口,这个接口不必定要与 Abstraction 的接口彻底一致,事实上这两个接口能够彻底不一样。
  • ConcreteImplementor(具体实现类):实现了 Implementor 定义的接口,在不一样的 ConcreteImplementor 中提供基本操做的不一样实现。在程序运行时,ConcreteImplementor 对象将替换其父类对象,提供给 Abstraction 具体的业务操做方法。

桥接模式的优缺点

优势:

  1.桥接分离了抽象和实现的具体联系,使得实现和抽象均可以去按照各自的维度去变化,并且相互之间不会产生影响。所谓隔离了抽象和实现,其实就是经过聚合的方式来实现了组件的动态添加和离开,子类不一样形式的组合可以构建出不一样的对象。

  2.在设计上它能够用来代替多层继承方案。当继承体系达到了必定深度以后,会致使大量的子类,以及冗余的功能,而且子类自己的管理也是一个问题。

  3.桥接模式提升了系统的可扩展性,由于抽象和实现,能够按照各自不一样的维度进行变化,进而组合出不一样的类型

缺点:

  1.增长了设计和理解难度,由于关联关系从一开始就是创建在抽象层面的,意味着编码工做要针对抽象层进行。

  2.桥接模式要求系统正确的识别各个独立变化的维度,可是识别这些维度也须要必定的考量。

桥接模式的适用场景

  • 若是一个系统须要在抽象化和具体化之间增长更多的灵活性,避免在两个层次之间创建静态的继承关系,经过桥接模式可使它们在抽象层创建一个关联关系。
  • “抽象部分”和“实现部分”能够以继承的方式独立扩展而互不影响,在程序运行时能够动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统须要对抽象化角色和实现化角色进行动态耦合。
  • 一个系统存在多个(≥ 2)独立变化的维度,且这多个维度都须要独立进行扩展。
  • 对于那些不但愿使用继承或由于多层继承致使系统类的个数急剧增长的系统,桥接模式尤其适用。

代码示例

  桥接模式使用的关键是分清楚谁是实现,谁是抽象。假设咱们有一间很是大的图书馆,馆藏丰富,内含n个区域,每一个区域中有不一样种类的书籍,每一个图书管理员负责其中一各区域。那图书管理员则是一个抽象。由于图书是客观存在的,它不会由于图书管理员的离开而消失。。也就是说咱们能够经过实现的子类和抽象的子类进行组合,可得出任意形状的系统。使用桥接模式。图书区内上架什么书或者下架什么书,对图书管理员不会产生影响(只要保持方法稳定)。

1.实现的基类(UML类图中的Implementor)

#ifndef IMPLEMENTOR_H_
#define IMPLEMENTOR_H_

class Implementor
{
public:
    virtual void display()=0;
    Implementor() = default;
    virtual ~Implementor() = default;
};
#endif
Abstract Implementor

2.实现的具体类(UML类图中的ConcreteImplementor)

#ifndef IMPLEMENTOR1_H_
#define IMPLEMENTOR1_H_

#include "Implementor.h"
#include <iostream>
class Implementor1:public Implementor
{
public:
    void display() override;
    Implementor1() = default;
    ~Implementor1() = default;
};
#endif

#include "Implementor1.h"

void Implementor1::display()
{
    std::cout << "Book1 " << std::endl;
}
Concrete Implementor

3.抽象的基类(UML类图中的Abstraction)

#ifndef ABSTRACT_H_
#define ABSTRACT_H_

#include "Implementor.h"
class Abstract
{
public:
    virtual void sell() = 0;
    Abstract() = default;
    virtual ~Abstract() = default;
protected:
    Implementor* m_pobjImplementor{nullptr};
};

#endif
Abstract

4.抽象的具体类(UML类图中的RefinedAbstraction)

#ifndef REFINEDABSTRACTION1_H_
#define REFINEDABSTRACTION1_H_

#include "Abstract.h"
#include <string>
#include <iostream>

class RefinedAbstraction1:public Abstract
{
public:
    void sell() override;
    RefinedAbstraction1(std::string strName,Implementor* objImplementor): m_strName(strName)
    {
    m_pobjImplementor = objImplementor;
    }
private:
    std::string m_strName;
};
#endif

#include "RefinedAbstraction1.h"

void RefinedAbstraction1::sell()
{
    if(nullptr != m_pobjImplementor)
    {
    std::cout << "My name is:" << m_strName<<".I am responsible for " << std::endl;
        m_pobjImplementor->display();
    }
}

5.客户端代码(main)

#include "RefinedAbstraction1.h"
#include "Implementor1.h"
#include "Abstract.h"

int main(int argc,char *aargv[])
{
    Implementor1 objConcreteImplement;
    RefinedAbstraction1 concreteAbstraction("Yang",&objConcreteImplement);
    Abstract *ab = &concreteAbstraction;
    ab->sell();
    return (1);
}
Client
相关文章
相关标签/搜索