桥接模式介绍
桥接模式的作用是通过将抽象部分与实现部分分离,将多种可匹配的使用进行组合。其核心实现是在A类中含有B类接口,通过构造函数传递B类的实现,这个B类就是设计的桥。
这样的桥接模式存在于日常开发中的那些场景呢,包括JDBC多种驱动程序的实现,同品牌类型的台式机和笔记本电脑、业务实现中的多类接口同组过滤服务服务等。这些场景都比较适合用桥接模式实现,因为在一些组合中,如果每一个类都实现不同的服务,可能会出现笛卡尔积,而使用桥接模式就可以变得非常简单。
简单桥接模式使用
场景:多支付和多模式组合场景
在支付服务行业中,有微信、支付宝及一些其他支付服务,但是对于商家来说,并不能只接受某一种支付方式。如果商家只支持使用微信或支付宝付款,那么就会让顾客为难,商品销量也会受到影响。
这时就出现了第三方平台,它们把市面上的多种支付服务都集中到自己平台中,再把这些平台提供给店铺、超市等商家使用,同时支持人脸支付、指纹支付和密码支付等多种支付方式。
违背设计模式实现
没有什么事if…else解决不了的事情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.bestrookie.design; import java.math.BigDecimal;
public class PayController { public Boolean dpPay(String uId, String tradeId, BigDecimal amount,int channelType,int modeType) { if (1 == channelType) { System.out.println("模拟微信渠道支付 uid: " + uId + " tradeId" + tradeId); if (modeType == 1) { System.out.println("密码支付"); } else if (modeType == 2) { System.out.println("人脸支付"); } else if (modeType == 3) { System.out.println("指纹支付"); } } else if (2 == channelType) { System.out.println("模拟支付宝渠道支付 uid: " + uId + " tradeId" + tradeId); if (modeType == 1) { System.out.println("密码支付"); } else if (modeType == 2) { System.out.println("人脸支付"); } else if (modeType == 3) { System.out.println("指纹支付"); } } return true; } }
|
逻辑简单明了,已经满足了不同支付类型和支付模式的组合,但是这样的代码在后面的维护以及扩展中都会变得非常复杂。
桥接模式重构代码
从上面的if…else实现方式来看,这时两种不同类型的相互组合。可以把支付类型和支付模式分离,通过抽象类依赖实现类的方式进行桥接。按照这种方式拆分后,支付方式与支付模式可以单独使用,当需要组合时,只需要把模式传递给各类支付方式。
桥接模式的关键是选择桥接点拆分,看能否找到这样类似的相互组合,如果没有就不用必须使用桥接模式。
- 左侧Pay是一个抽象类,下面是它的两种支付类型:微信支付和支付宝支付。
- 右侧IPayMode是一个接口,下面是它的三种支付模式:密码支付、人脸支付和指纹支付
- 支付类型 X 支付模式 = 相应的组合
支付类型桥接抽象类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.bestrookie.design.pay.channel; import com.bestrookie.design.pay.model.IPayModel; import java.math.BigDecimal;
public abstract class Pay { protected IPayModel iPayModel; public Pay(IPayModel iPayModel){ this.iPayModel = iPayModel; } public abstract String transfer(String uId, String tradeId, BigDecimal amount); }
|
如果没有接触过此类支付需求,可以终点关注IPayMode payMode,这部分是桥接模式的核心。
两种支付类型的实现
微信:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.bestrookie.design.pay.channel; import com.bestrookie.design.pay.model.IPayModel; import java.math.BigDecimal;
public class WxPay extends Pay{ public WxPay(IPayModel iPayModel) { super(iPayModel); } @Override public String transfer(String uId, String tradeId, BigDecimal amount) { System.out.println("微信支付开始 uId:"+uId+" tradeId: "+tradeId); boolean security = iPayModel.security(uId); System.out.println("安全验证========="); if (!security){ System.out.println("安全验证未通过============"); return "0001"; } System.out.println("安全验证通过==========="); return "00000"; } }
|
支付宝:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.bestrookie.design.pay.channel; import com.bestrookie.design.pay.model.IPayModel; import java.math.BigDecimal;
public class ZfbPay extends Pay{ public ZfbPay(IPayModel iPayModel) { super(iPayModel); }
@Override public String transfer(String uId, String tradeId, BigDecimal amount) { System.out.println("模拟支付宝支付 uId: "+uId+" readeId: "+tradeId); boolean security = iPayModel.security(uId); System.out.println("安全验证===================="); if (!security){ System.out.println("安全验证未通过================"); return "0001"; } System.out.println("安全验证通过==============="); return "0000"; } }
|
定义支付模式接口
1 2 3 4 5 6 7 8
| package com.bestrookie.design.pay.model;
public interface IPayModel { boolean security(String uId); }
|
三种支付模式风控
1 2 3 4 5 6 7 8 9 10 11 12
| package com.bestrookie.design.pay.model;
public class PayFaceMode implements IPayModel{ public boolean security(String uId) { System.out.println("人脸支付======="); return true; } }
|
1 2 3 4 5 6 7 8 9 10 11
| package com.bestrookie.design.pay.model;
public class PayFingerprintMode implements IPayModel{ public boolean security(String uId) { System.out.println("指纹支付======"); return true; } }
|
1 2 3 4 5 6 7 8 9 10 11
| package com.bestrookie.design.pay.model;
public class PayCypher implements IPayModel{ public boolean security(String uId) { System.out.println("密码支付======="); return true; } }
|
与上面的if…else实现方式相比,这里的调用方式变得整洁、干净和易用。玩不使用接口的用户不需要关心具体的实现,只需要选择使用即可。
总结
从桥接模式的实现形式来看,它满足了单一职责和开闭原则,让每一部分内容都很清晰,易于维护和扩展。但如果实现的高内聚的代码,则会很复杂。所以在选择重构代码时,需要考虑整体的设计。如果运用的设计模式不合理,也会让代码变得难以开发和维护。
任何一种设计模式的原则和使用都应该以符合场景为主,不能刻意使用。由于业务的复杂性,可能需要用到多种设计模式,才能将代码变得更加合理。