首页 > 其他分享 >设计模式之桥接模式

设计模式之桥接模式

时间:2022-10-27 11:57:57浏览次数:74  
标签:桥接 接口 抽象 模式 维度 抽象类 设计模式

概述

桥接模式是一种很实用的结构型设计模式。如果软件系统中某个类存在两个独立变化的维度,通过该模式可以将这两个维度分离出来,使两者可以独立扩展,让系统更加符合单一职责原则。与多层继承方案不同,它将两个独立变化的维度设计为两个独立的继承等级结构,并且在抽象层建立一个抽象关联,该关联关系类似一条连接两个独立继承结构的桥,故名桥接模式。

桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联取代了传统的多层继承,将类之间的静态继承关系转换为动态的对象组合关系,使得系统更加灵活,并易于扩展,同时有效控制了系统中类的个数。桥接模式定义如下:将抽象部分与其实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式

桥接模式的结构与其名称一样,存在一条连接两个继承等级结构的桥。桥接模式结构如图所示:

从图中可以看出,在桥接模式结构图中包含以下4个角色:

  1. Abstraction(抽象类):用于定义抽象类的接口,它一般是抽象类而不是接口,其中定义了一个 Implementor(实现类接口)类型的对象并可以维护该对象,它与 Implementor 之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
  2. RefinedAbstraction(扩充抽象类):扩充由 Abstraction 定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在 Abstraction 中声明的抽象业务方法,在 RefinedAbstraction 中可以调用在 Implementor 中定义的业务方法。
  3. Implementor(实现类接口):定义实现类的接口,这个接口不一定要与 Abstraction 的接口完全一致,事实上这两个接口可以完全不同,一般而言,Implementor 接口仅提供基本操作,而 Abstraction 定义的接口可能会做更多、更复杂的操作。Implementor 接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在 Abstraction 中不仅拥有自己的方法,还可以调用到 Implementor 中定义的方法,使用关联关系来替代继承关系。
  4. ConcreteImplementor(具体实现类):具体实现 Implementor 接口,在不同的 ConcreteImplementor 中提供基本操作的不同实现,在程序运行时,ConcreteImplementor 对象将替换其父类对象,提供给抽象类具体的业务操作方法。

在使用桥接模式时,首先应该识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。通常情况下,将具有两个独立变化维度的类的一些普通业务方法和与之关系最密切的维度设计为抽象类层次结构(抽象部分),而将另一个维度设计为实现类层次结构(实现部分)。

例如,对于毛笔而言,由于型号是其固有的维度,因此可以设计一个抽象的毛笔类,在该类中声明并部分实现毛笔的业务方法,而将各种型号的毛笔作为其子类;颜色是毛笔的另一个维度,由于它与毛笔之间存在一种“设置”的关系,因此可以提供一个抽象的颜色接口,而将具体的颜色作为实现该接口的子类。在此,型号可认为是毛笔的抽象部分,而颜色是毛笔的实现部分,其结构示意图如图所示:

在具体编码实现时,由于在桥接模式中存在两个独立变化的维度,为了使两者之间耦合度降低,首先需要针对两个不同的维度提取抽象类和实现类接口,并建立一个抽象关联关系。对于“实现部分”维度,典型的实现类接口代码如下:

class Implementor {
public:
	virtual void operationImpl() = 0;
};

在实现 Implementor 接口的子类中实现了在该接口中声明的方法,用于定义与该维度相对应的一些具体方法。对于另一 "抽象部分" 维度而言,其典型的抽象类代码如下:

class Abstraction {
public:
	void setImpl(const Implementor& impl) { this->impl = impl; }
	virtual void operation() = 0;

protected:
	Implementer impl;
};

在抽象类 Abstraction 中定义了一个实现类接口类型的成员对象 impl,再通过注入的方式给该对象赋值,一般将该对象的可见性定义为 protected,以便在其子类中访问 Implementor 的方法,其子类一般称为扩充抽象类或细化抽象(RefinedAbstraction),典型的 RefinedAbstraction 类代码如下:

class RefinedAbstracion : public Abstraction {
public:
	virtual void operation() {
		// 业务代码
		impl.operationImpl();
		// 业务代码
	}
};

对于客户端而言,可以针对两个维度的抽象层编程,在程序运行时再动态确定两个维度的子类,动态组合对象,将两个独立变化的维度完全解耦,以便能够灵活地扩充任一维度而对另一维度不造成任何影响。

适配器模式与桥接模式的结合

在软件开发中,适配器模式通常可以与桥接模式联合使用。适配器模式可以解决两个已有接口间不兼容问题,在这种情况下被适配的类往往是一个黑盒子,有时候用户不想也不能改变这个被适配的类,也不能控制其扩展。适配器模式通常用于现有系统与第三方产品功能的集成,采用增加适配器的方式将第三方类集成到系统中。桥接模式则不同,用户可以通过接口继承或类继承的方式来对系统进行扩展。

桥接模式和适配器模式用于设计的不同阶段。桥接模式用于系统的初步设计,对于存在两个独立变化维度的类可以将其分为抽象类和实现类两个角色,使它们可以分别进行变化;而在初步设计完成之后,当发现系统与已有类无法协同工作时,可以采用适配器模式。但有时候在设计初期也需要考虑适配器模式,特别是那些涉及大量第三方应用接口的情况。

例如在某系统的报表处理模块中,需要将报表显示和数据采集分开,系统可以有多种报表显示方式,也可以有多种数据采集方式,例如可以从文本文件中读取数据,也可以从数据库中读取数据,还可以从 Excel 文件中获取数据。如果需要从 Excel 文件中获取数据,则需要调用与 Excel 相关的 API,而这个 API 是现有系统所不具备的,该 API 由厂商提供。使用适配器模式和桥接模式设计该模块。

在设计过程中,由于存在报表显示数据采集两个独立变化的维度,因此可以使用桥接模式进行初步设计;为了使用 Excel 相关的 API 来进行数据采集,则需要使用适配器模式。系统的完整设计中需要将两个模式联用,如图所示:

总结

优点

  1. 分离抽象接口及其实现部分。桥接模式使用 "对象间的关联关系" 解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化(即抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任意组合子类,从而获得多维度组合对象)。
  2. 在很多情况下,桥接模式可以取代多层继承方案。多层继承方案违背了单一职责原则,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大地减少了子类的个数。
  3. 桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合开闭原则。

缺点

  1. 桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。
  2. 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累。

适用场景

  1. 如果一个系统需要在抽象类和具体类之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使它们在抽象层建立一个关联关系。
  2. 抽象部分和实现部分可以以继承的方式独立扩展而互不影响,在程序运行时可以动态地将一个抽象类子类的对象和一个实现类子类的对象进行组合,即系统需要对抽象类角色和实现类角色进行动态耦合。
  3. 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。
  4. 对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

所有代码见 Kohirus-Github

标签:桥接,接口,抽象,模式,维度,抽象类,设计模式
From: https://www.cnblogs.com/tuilk/p/16831707.html

相关文章

  • preg_replace /e 模式下的代码执行问题
    preg_replace/e模式下的代码执行问题preg_replace在/e模式下存在代码执行问题这里借用例题分析functioncomplex($re,$str){returnpreg_replace(......
  • 简单工厂模式
    1.利用面向对象思想实现1.1面向对象的好处通过封装、继承和多态把程序的耦合性降低,用设计模式使得程序更加灵活,容易修改,并易于复用。1.2简单工厂模式静态方法模式(因......
  • linux LVS的DR模式实现
    架构图:环境:一台:客户端eth0:仅主机192.168.10.6/24GW:192.168.10.200一台:ROUTEReth0:NAT 10.0.0.200/24eth1:仅主机192.168.10.200/24启用IP_FORWARD一......
  • javascript编程单线程之异步模式Asynchronous
    异步模式Asynchronous不会等待这个任务结束才开始执行下一个任务,开启之后立即执行下一个任务,后续逻辑一般会通过回调函数的方式定义,异步模式对js非常重要,没有异步任务单线......
  • javascript编程单线程之异步模式Asynchronous
    异步模式Asynchronous不会等待这个任务结束才开始执行下一个任务,开启之后立即执行下一个任务,后续逻辑一般会通过回调函数的方式定义,异步模式对js非常重要,没有异步任务单......
  • 适配器模式
    适配器模式 将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类结构型模式和对象结构型模式两种,前......
  • 建造者模式
    建造者模式(生成者模式)指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的......
  • Java设计模式 —— 代理模式
    15代理模式15.1代理模式概述ProxyPattern:给某一个对象提供一个代理或占位符,由代理对象来控制对原对象的访问。代理对象是客户端和目标对象的之前的桥梁,它接收来......
  • Linux LVS-NAT模式的实现
    结构图:环境准备lvs[root@lvs~]#cat/etc/sysconfig/network-scripts/ifcfg-eth0TYPE=EthernetBOOTPROTO=noneNAME=eth0DEVICE=eth0ONBOOT=yesIPADDR=10.0.0.8......
  • PO模式介绍及案例
    概念PO(PageObject)设计模式是一种面向对象(页面对象)的设计模式,将测试对象及单个的测试步骤封装在每个Page对象以page为单位进行管理。优点可以使代码复用降低维护成本......