写点什么

10. 桥接模式设计思想

作者:杨充
  • 2024-11-06
    湖北
  • 本文字数:8563 字

    阅读完需:约 28 分钟

10.桥接模式设计思想

目录介绍

  • 01.桥接模式基础

  • 1.1 桥接模式由来

  • 1.2 桥接模式定义

  • 1.3 桥接模式场景

  • 1.4 桥接模式思考

  • 1.5 解决的问题

  • 02.桥接模式实现

  • 2.1 罗列一个场景

  • 2.2 桥接结构

  • 2.3 桥接基本实现

  • 2.4 有哪些注意点

  • 03.桥接实例演示

  • 3.1 需求分析

  • 3.2 代码案例实现

  • 3.3 是否可以优化

  • 3.4 桥接设计

  • 3.5 演变代码案例

  • 04.桥接实现方式

  • 4.1 继承和组合

  • 4.2 接口和内部类

  • 05.桥接模式分析

  • 5.1 桥接模式优点

  • 5.2 桥接模式缺点

  • 5.3 适用环境

  • 5.4 模式拓展

  • 06.外观代理总结

  • 6.1 总结一下学习

  • 6.2 更多内容推荐

推荐一个好玩网站

一个最纯粹的技术分享网站,打造精品技术编程专栏!编程进阶网


https://yccoding.com/

01.桥接模式基础

1.0 本博客 AI 摘要

本文介绍了桥接模式的设计思想和实现方法。桥接模式通过将抽象部分与实现部分分离,使它们可以独立变化,解决了多层继承带来的复杂性和耦合性问题。文章详细讲解了桥接模式的由来、定义、应用场景和实现步骤,并通过具体实例演示了如何在支付场景中使用桥接模式。此外,还讨论了桥接模式的优缺点及其适用环境,提供了丰富的代码示例和进一步学习的资源链接。

1.1 桥接模式由来

设想如果要绘制矩形、圆形、椭圆、正方形,我们至少需要 4 个形状类,但是如果绘制的图形需要具有不同的颜色,如红色、绿色、蓝色等,此时至少有如下两种设计方案:


  1. 第一种设计方案是为每一种形状都提供一套各种颜色的版本。

  2. 第二种设计方案是根据实际需要对形状和颜色进行组合。


对于有两个变化维度(即两个变化的原因)的系统,采用方案二来进行设计系统中类的个数更少,且系统扩展更为方便。设计方案二即是桥接模式的应用。桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量。

1.2 桥接模式定义

桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。

1.3 桥接模式场景

在实际应用中有许多场景,以下是一些常见的应用场景:更多内容


  1. 图形界面库:桥接模式可以用于图形界面库中的控件和主题的组合。

  2. 数据库访问层:桥接模式可以用于数据库访问层中的数据库连接和数据库驱动的组合。

  3. 消息传输协议:桥接模式可以用于消息传输协议中的消息和传输方式的组合。消息可以作为抽象部分,传输方式可以作为实现部分,通过桥接模式可以实现不同消息和传输方式的组合,而不需要修改消息传输协议的代码。

  4. 音频和视频播放器:桥接模式可以用于音频和视频播放器中的播放器和解码器的组合。

1.4 桥接模式思考

当我们思考桥接模式时,以下几个方面值得考虑:


  1. 系统的抽象和实现:首先,我们需要确定系统中的抽象部分和实现部分。抽象部分是指高层次的业务逻辑或功能,而实现部分是指底层的具体实现或细节。确定这两个部分有助于我们理解系统的结构和关系。

  2. 动态选择和切换:桥接模式允许在运行时动态地选择和切换不同的实现。我们需要思考如何实现这种动态性,以及如何在系统中进行实际的选择和切换。

  3. 解耦和灵活性:桥接模式的主要目标是解耦抽象和实现,使它们可以独立地变化。我们需要思考如何通过桥接模式来实现解耦,以及如何提高系统的灵活性和可维护性。

1.5 解决的问题

桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联来取代传统的多层继承,将类之间的静态继承关系转变为动态的组合关系,使得系统更加灵活,并易于扩展,有效的控制了系统中类的个数 (避免了继承层次的指数级爆炸)。更多内容

02.桥接模式实现

2.1 罗列一个场景

假设有一个几何形状 Shape 类,从它能扩展出两个子类:圆形 Circle 和方形 Square。


你希望对这样的类层次结构进行扩展以使其包含颜色,所以你打算创建名为红色 Red 和蓝色 Blue 的形状子类。


但是,由于你已有两个子类,所以总共需要创建四个类才能覆盖所有组合,例如蓝色圆形 Blue-Circle 和红色方形 Red-Square。在层次结构中新增形状和颜色将导致代码复杂程度指数增长。在这种情况下,桥接模式就能起到作用,它将形状和颜色解耦,使得两者可以相对独立地变化。

2.2 桥接结构

桥接模式包含如下角色:


  1. Abstraction:抽象类。主要负责定义出该角色的行为,并包含一个对实现化对象的引用。

  2. RefinedAbstraction:扩充抽象类。是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。

  3. Implementor:实现类接口。定义实现化角色的接口,包含角色必须的行为和属性,并供扩展抽象化角色调用。

  4. ConcreteImplementor:具体实现类。给出实现化角色接口的具体实现。

2.3 桥接基本实现

首先,我们创建一个抽象类 Shape,它有一个抽象方法 draw():更多内容


public abstract class Shape {    public abstract void draw();}
复制代码


然后,我们创建两个实现了 Shape 接口的具体类:Circle 和 Square:


public class Circle extends Shape {    @Override    public void draw() {        System.out.println("画一个圆形");    }}
public class Square extends Shape { @Override public void draw() { System.out.println("画一个正方形"); }}
复制代码


接下来,我们创建一个桥接类 Color,它也实现了 Shape 接口,并持有一个 Shape 类型的引用:


public class Color extends Shape {    private Shape shape;
public Color(Shape shape) { this.shape = shape; }
@Override public void draw() { setColor(); shape.draw(); resetColor(); }
private void setColor() { System.out.println("设置颜色"); }
private void resetColor() { System.out.println("重置颜色"); }}
复制代码


最后,我们在主函数中测试这个桥接模式:


private void test() {    Shape circle = new Circle();    Shape square = new Square();    Shape coloredCircle = new Color(circle);    Shape coloredSquare = new Color(square);    coloredCircle.draw();    coloredSquare.draw();}
复制代码

2.4 有哪些注意点

使用桥接模式时需要注意以下几点:更多内容


  • 1、抽象部分和实现部分应该分离,不应该有过多的耦合。

  • 2、桥接模式适用于多个维度的变化,如果只有一两个维度的变化,使用继承会更加简单。

  • 3、桥接模式会增加系统的复杂度,需要谨慎使用。

  • 4、桥接模式要求正确选择和使用抽象类和接口,避免过度抽象或过于具体化。

  • 5、桥接模式的实现需要考虑对象的创建和管理,需要合理设计对象之间的关系和依赖关系。

03.桥接实例演示

3.1 需求分析

以当下支付场景为例,看下不同支付模式中桥接模式的应用。


如微信和支付宝都可以完成支付操作,而支付操作又可以有扫码支付、密码支付、人脸支付等,那么关于支付操作其实就有两个维度,包括:支付渠道和支付方式。

3.2 代码案例实现

不使用设计模式来模拟实现不同模式的支付场景。不使用设计模式缺点:维护和扩展都会变得非常复杂,需要修改原来代码,风险较大。更多内容


public class PayController {
/** * @param uId 用户id * @param tradeId 交易流水号 * @param amount 交易金额 * @param channelType 渠道类型 1 微信, 2 支付宝 * @param modeType 支付模式 1 密码,2 人脸,3 指纹 * @return: boolean */ public void doPay(String uId, String tradeId, BigDecimal amount, int channelType, int modeType) { //微信支付 if (1 == channelType) { System.out.println("微信渠道支付划账开始......"); if (1 == modeType) { System.out.println("密码支付"); } else if (2 == modeType) { System.out.println("人脸支付"); } else if (3 == modeType) { System.out.println("指纹支付"); } }
//支付宝支付 if (2 == channelType) { System.out.println("支付宝渠道支付划账开始......"); if (1 == modeType) { System.out.println("密码支付"); } else if (2 == modeType) { System.out.println("人脸支付"); } else if (3 == modeType) { System.out.println("指纹支付"); } } }}
复制代码


使用一下,如下所示:


private void test1() {    PayController payController = new PayController();    System.out.println("测试: 微信支付、人脸支付方式");    payController.doPay("weixin", "1000112333333", new BigDecimal(100), 1, 2);    System.out.println("\n测试: 支付宝支付、指纹支付方式");    payController.doPay("zhifubao", "1000112334567", new BigDecimal(100), 2, 3);}
复制代码

3.3 是否可以优化

虽然不使用设计模式也能实现该支付场景需求,但以后若增加支付渠道或修改支付方式,比如增加了京东支付,抖音支付,或者增加一个微信刷掌支付,则成本比较高,不利于后边的扩展和维护。


使用桥接模式重构支付场景代码。


桥接模式原理的核心是: 首先有要识别出一个类所具有的的两个独立变化维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。更多内容


针对该支付场景上边已经抽出了 2 个维度,即:支付渠道 和 支付方式;这里我们可以把支付渠道作为抽象化角色,支付方式作为实现化角色,支付渠道*支付模式 = 相对应的支付组合;

3.4 桥接设计

1)Pay 抽象类(支付渠道)


  1. I)支付渠道子类: 微信支付

  2. II)支付渠道子类: 支付宝支付


2)IPayMode 接口(支付方式)


  1. I)支付模式实现: 刷脸支付

  2. II)支付模式实现: 指纹支付

  3. III)密码支付

3.5 演变代码案例

Implementor:实现类接口。定义实现化角色的接口,包含角色必须的行为和属性,并供扩展抽象化角色调用。更多内容,这个定义支付方式接口!


/** * 支付模式接口 */public interface IPayMode {
//安全校验功能: 对各种支付模式进行风控校验 boolean security(String uId);}
复制代码


ConcreteImplementor:具体实现类。给出实现化角色接口的具体实现。这里有密码支付,刷脸支付,指纹支付等。


/** * 密码支付及风控校验 * 具体实现化(Concrete Implementor)角色 */public class PayCypher implements IPayMode {    @Override    public boolean security(String uId) {        return false;    }}

/** * 刷脸支付及风控校验 * 具体实现化(Concrete Implementor)角色 */public class PayFaceMode implements IPayMode { @Override public boolean security(String uId) { return true; }}
/** * 指纹支付及风控校验 * 具体实现化(Concrete Implementor)角色 */public class PayFingerprintMode implements IPayMode {
@Override public boolean security(String uId) { return false; }}
复制代码


Abstraction:抽象类。主要负责定义出该角色的行为,并包含一个对实现化对象的引用。这里针对渠道抽象出角色。更多内容


/** * 支付抽象化类 * 抽象化(Abstraction)角色 */public abstract class Pay {
protected IPayMode payMode;
public Pay(IPayMode payMode){ this.payMode = payMode; }
//划账功能 public abstract String transfer(String uId, String tradeId, BigDecimal amount);
}
复制代码


RefinedAbstraction:扩充抽象类。是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。


/** * 支付渠道-微信 * 扩展抽象化(RefinedAbstraction)角色 */public class WxPay extends Pay{
public WxPay(IPayMode payMode) { super(payMode); }
@Override public String transfer(String uId, String tradeId, BigDecimal amount) { System.out.println("微信渠道支付划账开始......");
//支付方式校验 boolean security = payMode.security(uId); System.out.println("微信渠道支付风险校验: " + uId + " , " + tradeId +" , " + security);
if(!security){ System.out.println("微信渠道支付划账失败!"); return "500"; }
System.out.println("微信渠道划账成功! 金额: "+ amount); return "200"; }}

/** * 支付渠道--支付宝 * 扩展抽象化(RefinedAbstraction)角色 */public class ZfbPay extends Pay{
public ZfbPay(IPayMode payMode) { super(payMode); }
@Override public String transfer(String uId, String tradeId, BigDecimal amount) { System.out.println("支付宝渠道支付划账开始......");
//支付方式校验 boolean security = payMode.security(uId); System.out.println("支付宝渠道支付风险校验: " + uId + " , " + tradeId +" , " + security);
if(!security){ System.out.println("支付宝渠道支付划账失败!"); return "500"; }
System.out.println("支付宝渠道划账成功! 金额: "+ amount); return "200"; }}
复制代码


最后测试一下桥接模式,如下


private void test1() {    System.out.println("测试场景1: 微信支付、人脸方式.");    Pay wxpay = new WxPay(new PayFaceMode());    wxpay.transfer("weixin","10001900",new BigDecimal(100));
System.out.println();
System.out.println("测试场景2: 支付宝支付、指纹方式"); Pay zfbPay = new ZfbPay(new PayFingerprintMode()); zfbPay.transfer("zhifubao","567689999999",new BigDecimal(200));}
复制代码

04.桥接实现方式

4.1 继承和组合

使用继承和组合的方式实现桥接模式。更多内容


这种方式需要创建两个类,一个作为抽象类,另一个作为具体类。抽象类中定义了对抽象部分和实现部分的引用,具体类中实现了抽象部分的具体逻辑。


// 抽象部分abstract class Abstraction {    protected Implementation implementation;
public void setImplementation(Implementation implementation) { this.implementation = implementation; }
public abstract void operation();}
// 具体部分class ConcreteAbstraction extends Abstraction { @Override public void operation() { System.out.println("具体操作"); }}
// 实现部分interface Implementation { void operationImpl();}
class ConcreteImplementationA implements Implementation { @Override public void operationImpl() { System.out.println("实现A的操作"); }}
class ConcreteImplementationB implements Implementation { @Override public void operationImpl() { System.out.println("实现B的操作"); }}
// 客户端代码private void test() { Abstraction abstraction = new ConcreteAbstraction(); Implementation implementationA = new ConcreteImplementationA(); Implementation implementationB = new ConcreteImplementationB();
abstraction.setImplementation(implementationA); abstraction.operation(); // 输出:具体操作
abstraction.setImplementation(implementationB); abstraction.operation(); // 输出:具体操作}
复制代码

4.2 接口和内部类

使用接口和内部类的方式实现桥接模式


这种方式需要创建一个接口,一个抽象类和一个内部类。抽象类中定义了对接口的引用,内部类中实现了抽象类的具体逻辑。


// 接口interface Shape {    void draw();}
// 抽象部分abstract class AbstractShape { protected Shape shape;
public void setShape(Shape shape) { this.shape = shape; }
public abstract void draw();}
// 具体部分class Rectangle extends AbstractShape { @Override public void draw() { shape.draw(); }}
class Circle extends AbstractShape { @Override public void draw() { shape.draw(); }}
// 内部类实现接口class ShapeImpl implements Shape { @Override public void draw() { System.out.println("绘制形状"); }}
// 客户端代码private void test() { AbstractShape abstractShape = new Rectangle(); Shape shapeA = new ShapeImpl(); Shape shapeB = new ShapeImpl();
abstractShape.setShape(shapeA); abstractShape.draw(); // 输出:绘制形状
abstractShape.setShape(shapeB); abstractShape.draw(); // 输出:绘制形状}
复制代码

05.桥接模式分析

5.1 桥接模式优点

桥接模式的优点:


  1. 分离抽象接口及其实现部分。

  2. 桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。

  3. 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。

  4. 实现细节对客户透明,可以对用户隐藏实现细节。

5.2 桥接模式缺点

桥接模式的缺点:


  1. 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进。更多内容

5.3 适用环境

在以下情况下可以使用桥接模式:


  1. 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。

  2. 抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。

  3. 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

  4. 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。

  5. 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

5.4 模式拓展

适配器模式与桥接模式的联用:更多内容


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

06.外观代理总结

6.1 总结一下学习

01.桥接模式基础


桥接模式的由来是为了解决软件系统中的复杂性和耦合性问题。在大型软件系统中,各个子系统之间可能存在复杂的依赖关系和交互逻辑,这导致了系统的可维护性和可扩展性变得困难。为了简化客户端与子系统之间的交互,桥接模式被引入。


桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。


主要解决的问题:桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联来取代传统的多层继承,将类之间的静态继承关系转变为动态的组合关系,使得系统更加灵活,并易于扩展。


02.桥接模式实现


假设有一个几何形状 Shape 类,从它能扩展出两个子类:圆形 Circle 和方形 Square。对这样的类层次结构进行扩展以使其包含颜色,所以你打算创建名为红色 Red 和蓝色 Blue 的形状子类。


桥接模式实现如下所示:


  1. 创建一个抽象类 Shape,它有一个抽象方法 draw()。

  2. 抽象化角色的子类,创建两个实现了 Shape 接口的具体类:Circle 和 Square。

  3. 创建一个桥接类 Color,它也实现了 Shape 接口,并持有一个 Shape 类型的引用。

  4. 在主函数中测试这个桥接模式。


03.桥接实例演示


如微信和支付宝都可以完成支付操作,而支付操作又可以有扫码支付、密码支付、人脸支付等,那么关于支付操作其实就有两个维度,包括:支付渠道和支付方式。


不使用设计模式来模拟实现不同模式的支付场景。不使用设计模式缺点:维护和扩展都会变得非常复杂,需要修改原来代码,风险较大。


以后若增加支付渠道或修改支付方式,比如增加了京东支付,抖音支付,或者增加一个微信刷掌支付,则成本比较高,不利于后边的扩展和维护。


桥接模式原理的核心是: 首先有要识别出一个类所具有的的两个独立变化维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。

6.2 更多内容推荐


23 种设计模式


6.3 更多内容


发布于: 刚刚阅读数: 3
用户头像

杨充

关注

还未添加个人签名 2018-07-30 加入

还未添加个人简介

评论

发布
暂无评论
10.桥接模式设计思想_杨充_InfoQ写作社区