如何通过桥接模式重构代码? 青旅半醒 2023-09-24 20:47 47阅读 0赞 同类的业务、同样的功能,怎么就你能写出来那么多if else。 很多时候你写出来的if else都是没有考虑使用设计模式优化,今天介绍一下设计模式中的桥接模式。 ### 什么是桥接模式? ### 桥接模式的主要作⽤就是通过将抽象部分与实现部分分离,把多种可匹配的使⽤进⾏组合。 说⽩了核⼼实现也就是在A类中含有B类接⼝,通过构造函数传递B类的实现,这个B类就是设计的桥 。 ### UML结构图 ### ![format_png][] ①、Abstraction 抽象化角色:它的主要职责是定义出该角色的行为, 同时保存一个对实现化角色的引用, 该角色一般是抽象类。 ②、Implementor 实现化角色:它是接口或者抽象类, 定义角色必需的行为和属性。 ③、RefinedAbstraction 修正抽象化角色:它引用实现化角色对抽象化角色进行修正。 ④、ConcreteImplementor 具体实现化角色:它实现接口或抽象类定义的方法和属性。 ### 通用代码实现 ### 实现化类: publicinterfaceImplementor { voiddoSomething(); } 复制代码 具体实现化类: publicclassConcreteImplementor1implementsImplementor{ @OverridepublicvoiddoSomething() { // 具体业务逻辑处理 } } 复制代码 publicclassConcreteImplementor2implementsImplementor{ @OverridepublicvoiddoSomething() { // 具体业务逻辑处理 } } 复制代码 这里定义了两个,可能有多个。 抽象化角色: publicabstractclassAbstraction { // 定义对实现化角色的引用private Implementor implementor; publicAbstraction(Implementor implementor){ this.implementor = implementor; } // 自身的行为和属性publicvoidrequest(){ this.implementor.doSomething(); } // 获取实现化角色public Implementor getImplementor(){ return implementor; } } 复制代码 修正抽象化角色: publicclassRefinedAbstractionextendsAbstraction{ // 覆写构造函数publicRefinedAbstraction(Implementor implementor){ super(implementor); } // 修正父类的行为@Overridepublicvoidrequest() { super.request(); } } 复制代码 测试: publicclassBridgeClient { publicstaticvoidmain(String[] args) { // 定义一个实现化角色Implementorimplementor=newConcreteImplementor1(); // 定义一个抽象化角色Abstractionabstraction=newRefinedAbstraction(implementor); // 执行方法 abstraction.request(); } } 复制代码 如果我们的实现化角色有很多的子接口, 然后是一堆的子实现。 在构造函数中传递一个明确的实现者, 代码也是很清晰的。 ### 适用场景 ### (1)如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 (2)对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 (3)一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。 ### 案例场景分析 ### 目前市场主流的支付服务是微信和⽀付宝,支付方式也有很多种,例如⼈脸、扫描、密码多种⽅式。为了让用户和商家使用起来更方便,需要有一个第三⽅平台来承接各个⽀付能⼒。 那么这⾥就出现了**多⽀付**与**多模式**的融合使⽤,如果给每⼀个⽀付都实现⼀次不同的模式,即使是继承类也需要开发好多。⽽且随着后⾯接⼊了更多的⽀付服务或者⽀付⽅式,就会呈爆炸似的扩展。 所以你现在可以思考⼀下这样的场景该如何实现? ![format_png 1][] #### ⽤⼀坨坨代码实现 #### publicclassPayController { privateLoggerlogger= LoggerFactory.getLogger(PayController.class); publicbooleandoPay(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("密码支付,风控校验环境安全"); } elseif (2 == modeType) { logger.info("人脸支付,风控校验脸部识别"); } elseif (3 == modeType) { logger.info("指纹支付,风控校验指纹信息"); } } // 支付宝支付elseif (2 == channelType) { logger.info("模拟支付宝渠道支付划账开始。uId:{} tradeId:{} amount:{}", uId, tradeId, amount); if (1 == modeType) { logger.info("密码支付,风控校验环境安全"); } elseif (2 == modeType) { logger.info("人脸支付,风控校验脸部识别"); } elseif (3 == modeType) { logger.info("指纹支付,风控校验指纹信息"); } } returntrue; } } 复制代码 上⾯的类提供了⼀个⽀付服务功能,通过提供的必要字段; ⽤户ID 、 交易ID 、 ⾦额 、 渠道 、 模式 ,来控制⽀付⽅式。以上的 if else 应该是最差的⼀种写法,即使写 if else 也是可以优化的⽅式去写的。 #### 桥接模式重构代码 #### ![format_png 2][] 左侧 Pay 是⼀个抽象类,往下是它的两个⽀付类型实现;微信⽀付、⽀付宝⽀付。 右侧 IPayMode 是⼀个接⼝,往下是它的两个⽀付模型;刷脸⽀付、指纹⽀付。 那么, ⽀付类型 × ⽀付模型 = 就可以得到相应的组合。 **注意**,每种⽀付⽅式的不同,刷脸和指纹校验逻辑也有差异,可以使⽤适配器模式进⾏处理。 #### 代码实现 #### ##### ⽀付类型桥接抽象类 ##### publicabstractclassPay { protectedLoggerlogger= LoggerFactory.getLogger(Pay.class); protected IPayMode payMode; publicPay(IPayMode payMode) { this.payMode = payMode; } publicabstract String transfer(String uId, String tradeId, BigDecimal amount); } 复制代码 在这个类中定义了⽀付⽅式的需要实现的划账接⼝: transfer ,以及桥接接⼝; IPayMode ,并在构造函数中⽤户⽅⾃⾏选择⽀付⽅式。 ##### ⽀付类型的实现 ##### 微信支付 publicclassWxPayextendsPay { publicWxPay(IPayMode payMode) { super(payMode); } public String transfer(String uId, String tradeId, BigDecimal amount) { logger.info("模拟微信渠道支付划账开始。uId:{} tradeId:{} amount:{}", uId, tradeId, amount); booleansecurity= 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"; } } 复制代码 支付宝支付 publicclassZfbPayextendsPay { publicZfbPay(IPayMode payMode) { super(payMode); } public String transfer(String uId, String tradeId, BigDecimal amount) { logger.info("模拟支付宝渠道支付划账开始。uId:{} tradeId:{} amount:{}", uId, tradeId, amount); booleansecurity= 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"; } } 复制代码 这⾥分别模拟了调⽤第三⽅的两个⽀付渠道;微信、⽀付宝,当然作为⽀付综合平台可能不只是接了这两个渠道,还会有其很跟多渠道。 另外可以看到在⽀付的时候分别都调⽤了⻛控的接⼝进⾏验证,也就是不同模式的⽀付( 刷脸 、 指纹 ),都需要过指定的⻛控,才能保证⽀付安全。 ##### 定义⽀付模式接⼝ ##### publicinterfaceIPayMode { booleansecurity(String uId); } 复制代码 任何⼀个⽀付模式;刷脸、指纹、密码,都会过不同程度的安全⻛控,这⾥定义⼀个安全校验接⼝。 **三种⽀付模式⻛控(刷脸、指纹、密码)** **刷脸** publicclassPayFaceModeimplementsIPayMode{ protectedLoggerlogger= LoggerFactory.getLogger(PayCypher.class); publicbooleansecurity(String uId) { logger.info("人脸支付,风控校验脸部识别"); returntrue; } } 复制代码 **指纹** publicclassPayFingerprintModeimplementsIPayMode{ protectedLoggerlogger= LoggerFactory.getLogger(PayCypher.class); publicbooleansecurity(String uId) { logger.info("指纹支付,风控校验指纹信息"); returntrue; } } 复制代码 **密码** publicclassPayCypherimplementsIPayMode{ protectedLoggerlogger= LoggerFactory.getLogger(PayCypher.class); publicbooleansecurity(String uId) { logger.info("密码支付,风控校验环境安全"); returntrue; } } 复制代码 在这⾥实现了三种⽀付模式(刷脸、指纹、密码)的⻛控校验,在⽤户选择不同⽀付类型的时候,则会进⾏相应的⻛控拦截以此保障⽀付安全。 ##### 测试 ##### publicclassApiTest { @Testpublicvoidtest_pay() { System.out.println("\r\n模拟测试场景;微信支付、人脸方式。"); PaywxPay=newWxPay(newPayFaceMode()); wxPay.transfer("weixin_1092033111", "100000109893", newBigDecimal(100)); System.out.println("\r\n模拟测试场景;支付宝支付、指纹方式。"); PayzfbPay=newZfbPay(newPayFingerprintMode()); zfbPay.transfer("jlu19dlxo111","100000109894",newBigDecimal(100)); } } 复制代码 与上⾯的 if else 实现⽅式相⽐,这⾥的调⽤⽅式变得整洁、⼲净、易使⽤; new WxPay(new PayFaceMode()) 、 new ZfbPay(new PayFingerprintMode()) 外部的使⽤接⼝的⽤户不需要关⼼具体的实现,只按需选择使⽤即可。 ### 总结 ### 通过模拟微信与⽀付宝两个⽀付渠道在不同的⽀付模式下, **刷脸 、 指纹 、 密码**的组合从⽽体现了桥接模式的在这类场景中的合理运⽤。简化了代码的开发,给后续的需求迭代增加了很好的扩展性。 从桥接模式的实现形式来看满⾜了单⼀职责和开闭原则,让每⼀部分内容都很清晰易于维护和拓展,但如果我们是实现的⾼内聚的代码,那么就会很复杂。所以在选择重构代码的时候,需要考虑好整体的设计,否则选不到合理的设计模式,将会让代码变得难以开发。 作者:初念初恋 链接:[https://juejin.cn/post/7204601386607116348][https_juejin.cn_post_7204601386607116348] [format_png]: https://img-blog.csdnimg.cn/img_convert/66cf1b0f6a771f778ab9b80806db5954.webp?x-oss-process=image/format,png [format_png 1]: https://img-blog.csdnimg.cn/img_convert/8e2aee7a8fc453281313e33f203cdbb0.webp?x-oss-process=image/format,png [format_png 2]: https://img-blog.csdnimg.cn/img_convert/2aaaeb6f13bde882a1c09414eb492669.webp?x-oss-process=image/format,png [https_juejin.cn_post_7204601386607116348]: https://juejin.cn/post/7204601386607116348
相关 桥接模式 我是在脑壳疼的情况下(今天的工作的量很大,内容很丰富,我很开心,以至于脑壳疼)写下这篇关于桥接模式的文章,不正之处请多指教。 何谓桥接模式,用一座桥连接起来的模式,珠港澳大桥 分手后的思念是犯贱/ 2022年05月08日 17:00/ 0 赞/ 126 阅读
相关 桥接模式 > 本文总结摘自刘伟老师的《设计模式》和程杰老师的《大话设计模式》 1.定义 桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。(桥接模式用关联关系来降低 ╰半橙微兮°/ 2022年01月27日 09:37/ 0 赞/ 244 阅读
相关 桥接模式 桥接模式,有些类似排列组合,下面先引用一个非常经典的例子来理解桥接模式。假如某位画画爱好者去买画笔,他需要大、中、小三种型号且具有6种颜色的画笔,如果买彩笔,他需要买3×6=1 快来打我*/ 2021年11月05日 21:46/ 0 赞/ 376 阅读
相关 桥接模式 桥接模式 桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦 本是古典 何须时尚/ 2021年09月29日 16:06/ 0 赞/ 359 阅读
相关 桥接模式 A1 A2 与 B1 B2 组合 通常情况下 定义A接口或抽象类,A1 A2实现或继承A,然后A1B1和A1B2继承A1,A2B1和A2B2继承A2,各自输出。这样做关联关 超、凢脫俗/ 2021年09月28日 06:58/ 0 赞/ 338 阅读
相关 桥接模式 1.桥 public interface Bridge { void tagetLand(); } 桥的实现 publ 怼烎@/ 2021年09月21日 00:50/ 0 赞/ 367 阅读
相关 桥接模式 桥接模式:是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。 这种模式涉及到一个作 我会带着你远行/ 2021年09月17日 03:34/ 0 赞/ 367 阅读
相关 桥接模式 10.桥接模式 ![70][] class Client { static void Main(string[] arg ╰+攻爆jí腚メ/ 2021年09月16日 23:56/ 0 赞/ 341 阅读
相关 桥接模式 一 概述 现在有一个需求,需要创建不同的图形,并且每个图形都有可能会有不同的颜色。我们可以利用继承的方式来设计类的关系: ![在这里插入图片描述][watermark £神魔★判官ぃ/ 2021年07月24日 20:06/ 0 赞/ 498 阅读
相关 桥接模式 接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。 这种模... 小灰灰/ 2020年06月13日 05:52/ 0 赞/ 742 阅读
还没有评论,来说两句吧...