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

news2024/10/5 15:52:40

一、桥接模式介绍

       桥接模式(bridge pattern) 的定义是:将抽象部分与它的实现部分分离,使它们都可以独立

                                                                 地变化。

       桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联来取代传统的多层继承,

       将类之间的静态继承关系转变为动态的组合关系,使得系统更加灵活,并易于扩展,有效

       的控制了系统中类的个数 (避免了继承层次的指数级爆炸)。

二、桥接模式原理

       桥接模式类图如下所示:

               

       桥接模式(Bridge)包含以下角色:

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

                    对象的引用。

              2)扩展抽象化(RefinedAbstraction)角色 :是抽象化角色的子类,实现父类中的业

                    务方法,并通过组合关系调用实现化角色中的业务方法。

              3)实现化(Implementor)角色 :定义实现化角色的接口,包含角色必须的行为和属

                    性,并供扩展抽象化角色调用。

              4)具体实现化(Concrete Implementor)角色 :给出实现化角色接口的具体实现。

       桥接模式原理的核心是: 首先有要识别出一个类所具有的的两个独立变化维度,将它们设计

       为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。

       总结一句话就是: 抽象角色引用实现角色。

       如我们拿毛笔举例, 型号和颜色是毛笔的两个维度:

              1)型号是其固有的维度,所以抽象出一个毛笔类,而将各种型号的毛笔作为其子类,

                   也就是下图的右侧抽象部分内容。

              2)颜色是毛笔的另一个维度,它与毛笔之间存在一种设置的关系,因此可以提供一个

                   抽象的颜色接口,将具体颜色作为该接口的子类。

              

       

三、桥接模式应用示例

       以当下支付场景为例,看下不同支付模式中桥接模式的应用,如微信和支付宝都可以完成

       支付操作,而支付操作又可以有扫码支付、密码支付、人脸支付等,那么关于支付操作

       其实就有两个维度,包括:支付渠道和支付方式,如下图所示:

              

1、不使用设计模式来模拟实现不同模式的支付场景

/*******************************************************
 * 模拟网络支付场景
 * 模拟不同的支付工具对应不同的支付模式,比如微信和支付宝都可以完成支付操作,而支付操作又可以有扫码支付、密码支付、人脸支付等,
 * 那么关于支付操作其实就有两个维度, 包括:支付渠道和支付方式
 *
 * 不使用设计模式实现
 *
 * 不使用设计模式缺点:
 *    维护和扩展都会变得非常复杂,需要修改原来代码,风险较大
 *
 *******************************************************/
public class PayController {

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

        //支付宝支付
        if(2 == channelType){
            System.out.println("支付宝渠道支付划账开始......");
            if(1 == modeType){
                System.out.println("密码支付");
            }if(2 == modeType){
                System.out.println("人脸支付");
            }if(3 == modeType){
                System.out.println("指纹支付");
            }
        }
        return true;
    }
}


//测试
public class Test {

    public static void main(String[] args) {
        PayController payController = new PayController();
        System.out.println("测试: 微信支付、人脸支付方式");
        payController.doPay("weixin_001","1000112333333",new BigDecimal(100),1,2);

        System.out.println("\n测试: 支付宝支付、指纹支付方式");
        payController.doPay("hifubao_002","1000112334567",new BigDecimal(100),2,3);


    }
}

      虽然不使用设计模式也能实现该支付场景需求,但以后若增加支付渠道或修改支付方式,则

      成本比较高,不利于后边的扩展和维护。

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

      我们知道桥接模式原理的核心是: 首先有要识别出一个类所具有的的两个独立变化维度,

      将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。

      针对该支付场景上边已经抽出了2个维度,即:支付渠道 和 支付方式;这里我们可以把支付

      渠道作为抽象化角色,支付方式作为实现化角色,支付渠道*支付模式 = 相对应的支付组合;

      这样就得到下边2个类:

             1)Pay抽象类(支付渠道)
                   I)支付渠道子类: 微信支付
                   II)支付渠道子类: 支付宝支付
            2)IPayMode接口(支付方式)
                  I)支付模式实现: 刷脸支付
                  II)支付模式实现: 指纹支付
                  III)密码支付

      类图如下:

              

         示例代码如下:

         1)实现化角色

/**
 * 支付模式接口
 * 实现化(Implementor)角色
 */
public interface IPayMode {

    //安全校验功能: 对各种支付模式进行风控校验
    boolean security(String uId);
}


/*******************************************************
 * 密码支付及风控校验
 * 具体实现化(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;
    }
}

       2)抽象化角色代码

/*******************************************************
 * 支付抽象化类
 * 抽象化(Abstraction)角色
 *
 *******************************************************/
public abstract class Pay {

    protected IPayMode payMode;

    /**
     * todo 注意:
     *    抽象类的公共构造函数,不能用来new 创建对象(抽象类不能实例化),
     *    该构造函数必须由子类调用(在子类构造函数中通过super调用)
     *
     * @param payMode
     */
    public Pay(IPayMode payMode){
        this.payMode = payMode;
    }

    //划账功能
    public abstract String transfer(String uId, String tradeId, BigDecimal amount);

}



/*******************************************************
 * 支付渠道-微信
 * 扩展抽象化(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";
    }
}

        3)测试

public class Test {

    public static void main(String[] args) {
        System.out.println("测试场景1: 微信支付、人脸方式.");
        Pay wxpay = new WxPay(new PayFaceMode());
        wxpay.transfer("wx_00100100","10001900",new BigDecimal(100));

        System.out.println();

        System.out.println("测试场景2: 支付宝支付、指纹方式");
        Pay zfbPay = new ZfbPay(new PayFingerprintMode());
        zfbPay.transfer("jlu1234567","567689999999",new BigDecimal(200));
    }
}

四、桥接模式总结

1、桥接模式优点

     1)分离抽象接口及其实现部分,桥接模式使用"对象间的关联关系"解耦了抽象和实现之间

           固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化

     2)在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了单一职责原则,

           复用性差,类的个数多,桥接模式很好的解决了这些问题

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

           有系统,符合开闭原则

2、桥接模式缺点

      1)桥接模式的使用会增加系统的理解和设计难度,由于关联关系建立在抽象层,要求开

           发者一开始就要对抽象层进行设计和编程

      2)桥接模式要求正确识别出系统中的两个独立变化的维度,因此具有一定的局限性,并且

            如果正确的进行维度的划分,也需要相当丰富的经验

3、桥接模式使用场景

      1)需要提供平台独立性的应用程序时。 比如,不同数据库的 JDBC 驱动程序、硬盘驱动

            程序等。

      2)需要在某种统一协议下增加更多组件时。 比如,在支付场景中,我们期望支持微信、

          支付宝、各大银行的支付组件等。这里的统一协议是收款、支付、扣款,而组件就是微

          信、支付宝等

      3)基于消息驱动的场景。 虽然消息的行为比较统一,主要包括发送、接收、处理和回执,

           但其实具体客户端的实现通常却各不相同,比如,手机短信、邮件消息、QQ 消息、

           微信消息等。

      4)拆分复杂的类对象时。 当一个类中包含大量对象和方法时,既不方便阅读,也不方便修改

      5)希望从多个独立维度上扩展时。 比如,系统功能性和非功能性角度,业务或技术角度等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2190124.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CKA】十四、监控pod的日志

14、监控pod的日志 1. 考题内容: 2. 答题思路: 查看名字是foobar的pod的日志,过滤出 unable-to-access-website,导入到文件中就可以,这道题还是挺简单的 3. 官网地址: https://kubernetes.io/zh-cn/doc…

dll动态库加载失败导致程序启动报错以及dll库加载失败的常见原因分析与总结

目录 1、问题说明 2、dll库的隐式加载与动态加载 2.1、dll库的隐式加载 2.2、dll库的显式加载 3、使用Process Explorer查看进程加载的dll库信息以及动态加载的dll库有没有加载成功 3.1、使用Process Explorer查看进程加载的dll库信息 3.2、使用Process Explorer查看动态…

警惕!脑出血前这8大预警信号,你不可不知!

在这个快节奏的时代,健康成为了我们最宝贵的财富之一。然而,突如其来的疾病往往让人措手不及,尤其是像脑出血这样的严重疾病,其发病急、进展快,若不及时识别并就医,后果不堪设想。今天,我们就来…

在登陆功能中添加Redis缓存

目录 基于Redis实现短信登录 实现流程图 实现代码 解决登录状态刷新问题 初始方案思路: 实现代码 发送验证码 登陆实现 如果是新用户则自动创建 运行测试 基于Redis实现短信登录 实现流程图 实现代码 Overridepublic Result login(LoginFormDTO loginForm…

影刀RPA:Excel内容填充指令

1.实战目标 本次主要介绍影刀RPA如何操作内容相关的填充与替换指令。主要包含以下 这些指令在数据处理方面有着重要的作用,可以对数据做运算,填充,替换,实现数据格式统一,便于最终的数据分析。在操作的过程中&#xf…

WordPress修改固定链接后301的重定向方法

网站改版实际上是很忌讳的,尤其是针对已被搜索引擎收录的网站,新站不用考虑这些问题,而已经收录的网站网页在不遵守搜索引擎规则的前提下,是会被降权,关键词排名下滑、流量IP会被剥夺、收录会减少 、业务成交量会急剧下…

(Linux驱动学习 - 6).Linux中断

一. Linux 中断 API 函数 1.中断号 每个中断都有一个中断号,通过中断号即可区分不同的中断,有的资料也把中断号叫做中 断线。在 Linux 内核中使用一个 int 变量表示中断号。 2.申请中断 - request_irq 函数原型: int request_irq(unsigne…

【ubuntu】APT、apt、apt-get介绍

目录 1.APT简介 2.常用apt指令 2.1安装 2.2更新列表 2.3更新已经安装的软件包 2.4搜索软件包 2.5显示软件包信息 2.6移除软件包 2.7清理无用的安装包 2.8清理无用的依赖项 3.apt和apt-get 3.1区别 3.2 总结 1.APT简介 APT的全称是advanced package …

Java反射、自定义注解Demo

本文主要尝试使用反射、自定义注解,实现简单的Demo,所有代码均可直接复制使用。(反射和注解是Java框架不可或缺的一部分,我们应该熟练掌握这部分知识!) 本文的代码结构如下: 代码如下&#xff…

【Linux】详解Linux下的工具(内含yum指令和vim指令)

文章目录 前言1. Linux下软件安装的方式2. yum2.1 软件下载的小知识2.2 在自己的Linux系统下验证yum源的存在2.3 利用yum指令下载软件2.4 拓展yum源(针对于虚拟机用户) 3. vim编辑器3.1 vim是什么?3.2 如何打开vim3.2 vim各模式下的讲解3.2.1…

高级图片编辑器Photopea

什么是 Photopea ? Photopea 是一款免费的在线工具,用于编辑光栅和矢量图形,支持PSD、AI 和 Sketch文件。 功能上,Photopea 和 老苏之前介绍的 miniPaint 比较像 文章传送门:在线图片编辑器miniPaint 支持的格式 复杂…

创建django项目,编译类型选择Custom environment后,却没有manage.py文件,无法启动项目?

选择 Custom environment 创建后,启动项目却发现没有manage.py文件 解决办法: 1、 首先查看项目中是否安装了django,没有则安装 2 、创建项目(这里的myproject则表示项目名) django-admin startproject myproject …

C语言 | Leetcode C语言题解之第454题四数相加II

题目&#xff1a; 题解&#xff1a; struct hashTable {int key;int val;UT_hash_handle hh; };int fourSumCount(int* A, int ASize, int* B, int BSize, int* C, int CSize, int* D, int DSize) {struct hashTable* hashtable NULL;for (int i 0; i < ASize; i) {for (…

外包功能测试干了4年,技术退步太明显了。。。。。​

先说一下自己的情况&#xff0c;本科生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了差不多4年的功能测试&#xff0c;今年中秋&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测…

Day01-postgresql数据库基础入门培训

Day01-postgresql数据库基础入门培训 1、PostgresQL数据库简介2、PostgreSQL行业生态应用3、PostgreSQL版本发展与特性4、PostgreSQL体系结构介绍5、PostgreSQL与MySQL的区别6、PostgreSQL与Oracle、MySQL的对比 1、PostgresQL数据库简介 PostgreSQL【简称&#xff1a;PG】是加…

搭建shopify本地开发环境

虽然shopify提供了在线编辑器的功能&#xff0c;但是远不及本地编辑器方便高效&#xff0c;这篇文章主要介绍如何在本地搭建shopify开发环境&#xff1a; 1、安装nodejs 18.2 2、安装git 3、安装shopify cli ,使用指令: npm install -g shopify/clilatest 4、安装ruby 5、…

软件设计师——数据结构

本博文所有内容来自于B站up主zst_2001 目录 时间复杂度 常规数据结构 链表 栈与队列 ​编辑 串 数组 树 卡特兰数&#xff1a; 平衡二叉树 哈夫曼 图 AOV 排序 顺序 折半 哈希 时间复杂度 常规数据结构 链表 栈与队列 串 找i位置前面的字符串&#xff0c…

JS | JavaScript中document.write()有哪些用法?

document.write()是 JavaScript 中用于向文档中插入内容的方法。它可以在文档加载过程中或在脚本执行时动态地将任意内容写入到 HTML 文档中。 document.write() 是 JavaScript 中的一个方法&#xff0c;用于在 HTML 文档中动态生成内容。 这个方法可以在网页加载过程中动态地…

Python 工具库每日推荐 【Pandas】

文章目录 引言Python数据处理库的重要性今日推荐:Pandas工具库主要功能:使用场景:安装与配置快速上手示例代码代码解释实际应用案例案例:销售数据分析案例分析高级特性数据合并和连接时间序列处理数据透视表扩展阅读与资源优缺点分析优点:缺点:总结【 已更新完 TypeScrip…

重学SpringBoot3-集成Redis(一)

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-集成Redis&#xff08;一&#xff09; 1. 项目初始化2. 配置 Redis3. 配置 Redis 序列化4. 操作 Redis 工具类5. 编写 REST 控制器6. 测试 API7. 总结 随…