如何通过桥接模式重构代码?

news2024/11/23 21:33:37

文章目录

  • 什么是桥接模式?
  • UML结构图
  • 通用代码实现
  • 适用场景
  • 案例场景分析
    • ⽤⼀坨坨代码实现
    • 桥接模式重构代码
    • 代码实现
      • ⽀付类型桥接抽象类
      • ⽀付类型的实现
      • 定义⽀付模式接⼝
      • 测试
  • 总结


同类的业务、同样的功能,怎么就你能写出来那么多if else

很多时候你写出来的if else都是没有考虑使用设计模式优化,今天介绍一下设计模式中的桥接模式。

什么是桥接模式?

桥接模式的主要作⽤就是通过将抽象部分与实现部分分离,把多种可匹配的使⽤进⾏组合。

说⽩了核⼼实现也就是在A类中含有B类接⼝,通过构造函数传递B类的实现,这个B类就是设计的桥 。

UML结构图

a2ad2e61f3b970b66615c4839775fe8.png

①、Abstraction

抽象化角色:它的主要职责是定义出该角色的行为, 同时保存一个对实现化角色的引用, 该角色一般是抽象类。

②、Implementor

实现化角色:它是接口或者抽象类, 定义角色必需的行为和属性。

③、RefinedAbstraction

修正抽象化角色:它引用实现化角色对抽象化角色进行修正。

④、ConcreteImplementor

具体实现化角色:它实现接口或抽象类定义的方法和属性。

通用代码实现

实现化类:

public interface Implementor {
    void doSomething();
}

具体实现化类:

public class ConcreteImplementor1 implements Implementor{
    @Override
    public void doSomething() {
        // 具体业务逻辑处理
    }
}
public class ConcreteImplementor2 implements Implementor{
    @Override
    public void doSomething() {
        // 具体业务逻辑处理
    }
}

这里定义了两个,可能有多个。

抽象化角色:

public abstract class Abstraction {
    // 定义对实现化角色的引用
    private Implementor implementor;

    public Abstraction(Implementor implementor){
        this.implementor = implementor;
    }

    // 自身的行为和属性
    public void request(){
        this.implementor.doSomething();
    }

    // 获取实现化角色
    public Implementor getImplementor(){
        return implementor;
    }
}

修正抽象化角色:

public class RefinedAbstraction extends  Abstraction{
    // 覆写构造函数
    public RefinedAbstraction(Implementor implementor){
        super(implementor);
    }

    // 修正父类的行为
    @Override
    public void request() {
        super.request();
    }
}

测试:

public class BridgeClient {
    public static void main(String[] args) {
        // 定义一个实现化角色
        Implementor implementor = new ConcreteImplementor1();
        // 定义一个抽象化角色
        Abstraction abstraction = new RefinedAbstraction(implementor);
        // 执行方法
        abstraction.request();

    }
}

如果我们的实现化角色有很多的子接口, 然后是一堆的子实现。 在构造函数中传递一个明确的实现者, 代码也是很清晰的。

适用场景

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

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

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

案例场景分析

目前市场主流的支付服务是微信和⽀付宝,支付方式也有很多种,例如⼈脸、扫描、密码多种⽅式。为了让用户和商家使用起来更方便,需要有一个第三⽅平台来承接各个⽀付能⼒。

那么这⾥就出现了多⽀付多模式的融合使⽤,如果给每⼀个⽀付都实现⼀次不同的模式,即使是继承类也需要开发好多。⽽且随着后⾯接⼊了更多的⽀付服务或者⽀付⽅式,就会呈爆炸似的扩展。

所以你现在可以思考⼀下这样的场景该如何实现?

dd82e763278766ec0d1f908471a837a.png

⽤⼀坨坨代码实现

public class PayController {

    private Logger logger = LoggerFactory.getLogger(PayController.class);

    public boolean doPay(String uId, String tradeId, BigDecimal amount, int channelType, int modeType) {
        // 微信支付
        if (1 == channelType) {
            logger.info("模拟微信渠道支付划账开始。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
            if (1 == modeType) {
                logger.info("密码支付,风控校验环境安全");
            } else if (2 == modeType) {
                logger.info("人脸支付,风控校验脸部识别");
            } else if (3 == modeType) {
                logger.info("指纹支付,风控校验指纹信息");
            }
        }
        // 支付宝支付
        else if (2 == channelType) {
            logger.info("模拟支付宝渠道支付划账开始。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
            if (1 == modeType) {
                logger.info("密码支付,风控校验环境安全");
            } else if (2 == modeType) {
                logger.info("人脸支付,风控校验脸部识别");
            } else if (3 == modeType) {
                logger.info("指纹支付,风控校验指纹信息");
            }
        }
        return true;
    }
}

上⾯的类提供了⼀个⽀付服务功能,通过提供的必要字段; ⽤户ID 、 交易ID 、 ⾦额 、 渠道 、 模式 ,来控制⽀付⽅式。以上的 if else 应该是最差的⼀种写法,即使写 if else 也是可以优化的⽅式去写的。

桥接模式重构代码

b93fa80c853cb0fe7851028886791e4.png

左侧 Pay 是⼀个抽象类,往下是它的两个⽀付类型实现;微信⽀付、⽀付宝⽀付。

右侧 IPayMode 是⼀个接⼝,往下是它的两个⽀付模型;刷脸⽀付、指纹⽀付。

那么, ⽀付类型 × ⽀付模型 = 就可以得到相应的组合。

注意,每种⽀付⽅式的不同,刷脸和指纹校验逻辑也有差异,可以使⽤适配器模式进⾏处理。

代码实现

⽀付类型桥接抽象类

public abstract class Pay {

    protected Logger logger = LoggerFactory.getLogger(Pay.class);

    protected IPayMode payMode;

    public Pay(IPayMode payMode) {
        this.payMode = payMode;
    }

    public abstract String transfer(String uId, String tradeId, BigDecimal amount);

}

在这个类中定义了⽀付⽅式的需要实现的划账接⼝: transfer ,以及桥接接⼝; IPayMode ,并在构造函数中⽤户⽅⾃⾏选择⽀付⽅式。

⽀付类型的实现

微信支付

public class WxPay extends Pay {

    public WxPay(IPayMode payMode) {
        super(payMode);
    }

    public String transfer(String uId, String tradeId, BigDecimal amount) {
        logger.info("模拟微信渠道支付划账开始。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
        boolean security = payMode.security(uId);
        logger.info("模拟微信渠道支付风控校验。uId:{} tradeId:{} security:{}", uId, tradeId, security);
        if (!security) {
            logger.info("模拟微信渠道支付划账拦截。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
            return "0001";
        }
        logger.info("模拟微信渠道支付划账成功。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
        return "0000";
    }
}

支付宝支付

public class ZfbPay extends Pay {

    public ZfbPay(IPayMode payMode) {
        super(payMode);
    }

    public String transfer(String uId, String tradeId, BigDecimal amount) {
        logger.info("模拟支付宝渠道支付划账开始。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
        boolean security = payMode.security(uId);
        logger.info("模拟支付宝渠道支付风控校验。uId:{} tradeId:{} security:{}", uId, tradeId, security);
        if (!security) {
            logger.info("模拟支付宝渠道支付划账拦截。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
            return "0001";
        }
        logger.info("模拟支付宝渠道支付划账成功。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);
        return "0000";
    }
}

这⾥分别模拟了调⽤第三⽅的两个⽀付渠道;微信、⽀付宝,当然作为⽀付综合平台可能不只是接了这两个渠道,还会有其很跟多渠道。

另外可以看到在⽀付的时候分别都调⽤了⻛控的接⼝进⾏验证,也就是不同模式的⽀付( 刷脸 、 指纹 ),都需要过指定的⻛控,才能保证⽀付安全。

定义⽀付模式接⼝

public interface IPayMode {
    boolean security(String uId);
}

任何⼀个⽀付模式;刷脸、指纹、密码,都会过不同程度的安全⻛控,这⾥定义⼀个安全校验接⼝。

三种⽀付模式⻛控(刷脸、指纹、密码)

刷脸

public class PayFaceMode implements IPayMode{

    protected Logger logger = LoggerFactory.getLogger(PayCypher.class);

    public boolean security(String uId) {
        logger.info("人脸支付,风控校验脸部识别");
        return true;
    }
}

指纹

public class PayFingerprintMode implements IPayMode{

    protected Logger logger = LoggerFactory.getLogger(PayCypher.class);

    public boolean security(String uId) {
        logger.info("指纹支付,风控校验指纹信息");
        return true;
    }
}

密码

public class PayCypher implements IPayMode{

    protected Logger logger = LoggerFactory.getLogger(PayCypher.class);

    public boolean security(String uId) {
        logger.info("密码支付,风控校验环境安全");
        return true;
    }
}

在这⾥实现了三种⽀付模式(刷脸、指纹、密码)的⻛控校验,在⽤户选择不同⽀付类型的时候,则会进⾏相应的⻛控拦截以此保障⽀付安全。

测试

public class ApiTest {

    @Test
    public void test_pay() {
        System.out.println("\r\n模拟测试场景;微信支付、人脸方式。");
        Pay wxPay = new WxPay(new PayFaceMode());
        wxPay.transfer("weixin_1092033111", "100000109893", new BigDecimal(100));

        System.out.println("\r\n模拟测试场景;支付宝支付、指纹方式。");
        Pay zfbPay = new ZfbPay(new PayFingerprintMode());
        zfbPay.transfer("jlu19dlxo111","100000109894",new BigDecimal(100));
    }
}

与上⾯的 if else 实现⽅式相⽐,这⾥的调⽤⽅式变得整洁、⼲净、易使⽤;

new WxPay(new PayFaceMode()) 、 new ZfbPay(new PayFingerprintMode()) 外部的使⽤接⼝的⽤户不需要关⼼具体的实现,只按需选择使⽤即可。

总结

通过模拟微信与⽀付宝两个⽀付渠道在不同的⽀付模式下, 刷脸 、 指纹 、 密码的组合从⽽体现了桥接模式的在这类场景中的合理运⽤。简化了代码的开发,给后续的需求迭代增加了很好的扩展性。

从桥接模式的实现形式来看满⾜了单⼀职责和开闭原则,让每⼀部分内容都很清晰易于维护和拓展,但如果我们是实现的⾼内聚的代码,那么就会很复杂。所以在选择重构代码的时候,需要考虑好整体的设计,否则选不到合理的设计模式,将会让代码变得难以开发。

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

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

相关文章

【Web服务器】Tomcat的部署

文章目录 前言一、Tomcat 的概念1. Tomcat 核心组件1.1 什么是 servlet1.2 什么是 JSP 2. Tomcat 功能组件结构2.1 Container 结构分析 3. Tomcat 请求过程4. 配置文件4.1 安装目录4.2 conf 子目录 二、Tomcat 服务部署1. 下载并安装 JDK1.1 关闭防火墙,将安装 Tomc…

Milvus Lite 已交卷!轻量版 Milvus,主打就是一个轻便、无负担

想要体验世界上最快的向量数据库?缺少专业的工程师团队作为支撑?Milvus 安装环境受限? 别担心,轻量版 Milvus 来啦! 在正式介绍 Milvus Lite 之前,先简单回顾一下 Milvus。Milvus 是一款开源的向量数据库&a…

logstash启动时默认连接本机节点elasticsearch问题

背景 今天在排查处理一个logstash读取kafka数据写入到hdfs的问题时候,发现在启动日志中多了个 logstash.outputs.elasticsearch 连接的地址是localhost:9200 部分日志如下: 排查过程说明 1、首先确认 logstash 启动的配置文件中的 output 配置&#x…

SonarQube Data Center Edition 10.0 Crack

使用{SonarQube}为团队和企业提供干净的代码 SonarQube Data Center Edition为开发团队提供深度集成到企业环境中的代码质量和安全解决方案;使您能够一致且可靠地部署干净的代码。 灵活性和治理:完美的企业代码质量工具 与您的企业环境深度集成 自我管理&#xff0…

【软件测试】软件测试的基本概念和开发模型

1. 前言 在进行软件测试的学习之前,我们要了解软件测试一些基本概念. 这些基本概念将帮助我们更加明确工作的目标以及软件测试到底要做什么. 2. 软件测试的基本概念 软件测试的基本概念有3个,分别是需求,测试用例和BUG. 2.1 需求 这里的需求还可以分为 用户需求和软件需求,用户…

【博客646】将消息队列放在Prometheus前以提高可靠性并不总是“好主意“

将队列放在Prometheus前以提高可靠性并不总是"好主意" 为了防止突发流量,而在prometheus前加上消息队列以达到削峰填谷的目的 架构如下: 应用程序将指标推送到某种形式的队列(通常是 Kafka),暴露器二进制…

超详细IDEA创建Maven项目

文章目录 一、Maven概述二、创建Maven项目三、Maven项目简单介绍3.1 标准化的项目结构3.2 标准化的构建流程3.3 方便的依赖管理 一、Maven概述 Maven是一个专门用于管理和构建Java项目的工具。我们之所以要使用Maven,是因为Maven可以为我们提供一套标准化的项目结构…

Python单元测试框架《python 自动化框架 pytest》

​ 编辑切换为居中 添加图片注释,不超过 140 字(可选) Pytest 简介 pytest 是python 的一种单元测试框架,不python 自带的unittest 测试框架类似,但是比 unittest 框架使用起来更简洁,效率更高。根据pyt…

智警杯半决赛知识点

hive: 启动hadoop 启动本地连接,连接外网 主机就是外网IP 1.修改云主机host文件,添加内网ip,对应映射名为hadoop000,实现云主机自身使用root用户ssh访问hadoop000免密登录 改host文件 vim/etc/hosts 比赛时会提…

【论文阅读】(2023.05.10-2023.06.03)论文阅读简单记录和汇总

(2023.05.10-2023.06.08)论文阅读简单记录和汇总 2023/05/10:今天状态,复阳大残,下午淋了点雨吹了点风,直接躺了四个小时还是头晕- -应该是阳了没跑了。 2023/06/03:前两周出差复阳,这两周调整作息把自己又…

第十三届蓝桥杯c++b组国赛题解(还在持续更新中...)

第十三届蓝桥杯Cb组国赛真题pdf自取 试题A:2022 解题思路: 有2022个物品,它们的编号分别是1到2022,它们的价值分别等于它们的编号。也就是说,有2022种物品,物品价值等于物品编号。 从2022个物品种选取10个…

虹科案例 | 虹科Dimetix激光测距传感器可实现无接触式监测采矿设备健康状况!

Part.01 行业挑战 采矿业机器的环境条件通常非常恶劣,损坏的设备会导致生产力和利润下降。虹科虹科Dimetix激光测距传感器为采矿设备和危险区域机器的机器健康监测提供了非接触式解决方案。 采矿操作中的处理设备通常包括分离、混合和粉碎矿石的机械设备。许多重型…

客户线上反馈:从信息搜集到疑难 bug 排查全流程经验分享

写在前面:本文是我在前端团队的第三次分享,应该很少会有开发者写客户反馈处理流程以及 bug 排查的心得技巧,全文比较长,写了一个多星期大概1W多字(也是我曾经2年工作的总结),如果你有耐心阅读&a…

因数据库字段可存储长度设置的小了,已达到设置字符类型的存储上限,导致数据存储时报错 及 常用的数据类型

项目场景: 提示:这里简述项目相关背景: 使用MySQL数据库,因数据库字段可存储长度设置的小了,已达到设置字符类型的存储上限,导致数据存储时报错 问题描述 提示:这里描述项目中遇到的问题&am…

2天刷完这套八股文,offer到手啦?

前言 大家好,最近有不少小伙伴在后台留言,得准备面试了,又不知道从何下手!为了帮大家节约时间,特意准备了一份面试相关的资料,内容非常的全面,真的可以好好补一补,希望大家在都能拿…

我的GIT练习Three

目录 前言 GIT安装教程 Git作者 GIT优点 GIT缺点 为什么要使用 Git GIT练习Three 总结 前言 Git 是一个分布式版本控制及源代码管理工具;Git 可以为你的项目保存若干快照,以此来对整个项目进行版本管理 GIT安装教程 点击进入查看教程:点击进入 G…

(深入浅出)详解虚拟内存

概述 我们都知道一个进程是与其他进程共享CPU和内存资源的。正因如此,操作系统需要有一套完善的内存管理机制才能防止进程之间内存泄漏的问题。 为了更加有效地管理内存并减少出错,现代操作系统提供了一种对主存的抽象概念,即是虚拟内存&am…

从未尝试过的 7 种处理 CSS 的方法

啊,Web 开发的世界——您可以从学习 HTML、JavaScript,当然还有 CSS 等基础知识开始。 但是一旦你在现实世界中构建应用程序,纯 CSS 就不再适用了。 挣扎是真的。 因此,今天我们将深入探讨使用 NextJS 为 React 应用程序编写 CSS…

NLP与GPT联合碰撞:大模型与小模型联合发力

目录标题 NLP是什么东西?Al大小模型联合发力 NLP是自然语言处理,而GPT是自然语言生成模型。 它们的联合碰撞结果是大模型与小模型联合发力,是因为大模型可以提供更好的语言理解和生成能力,而小模型则可以更快地进行推理和预测。 …

考虑微网新能源经济消纳的共享储能优化配置(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…