同类的业务、同样的功能,怎么就你能写出来那么多if else。
很多时候你写出来的if else都是没有考虑使用设计模式优化,今天介绍一下设计模式中的桥接模式。
什么是桥接模式?
桥接模式的主要作用就是通过将抽象部分与实现部分分离,把多种可匹配的使用进行组合。
说白了核心实现也就是在A类中含有B类接口,通过构造函数传递B类的实现,这个B类就是设计的桥。
UML结构图
1、Abstraction
抽象化角色:它的主要职责是定义出该角色的行为, 同时保存一个对实现化角色的引用, 该角色一般是抽象类。
2、Implementor
实现化角色:它是接口或者抽象类, 定义角色必需的行为和属性。
3、RefinedAbstraction
修正抽象化角色:它引用实现化角色对抽象化角色进行修正。
4、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)一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
案例场景分析
目前市场主流的支付服务是微信和支付宝,支付方式也有很多种,例如刷脸、扫描、密码多种放式。为了让用户和商家使用起来更方便,需要有一个第三方平台来承接各个支付能力。
那么这里就出现了多支付与多模式的融合使用,如果给每一个支付都实现一次不同的模式,即使是继承类也需要开发好多。而且随着后面接入了更多的支付服务或者支付方式,就会呈爆炸似的扩展。
所以你现在可以思考一下这样的场景该如何实现?
用一坨坨代码实现
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 也是可以优化的方式去写的。
桥接模式重构代码
左侧 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()) 外部的使用接口的用户不需要关心具体的实现,只按需选择使用即可。
总结
通过模拟微信与支付宝两个支付渠道在不同的支付模式下, 刷脸 、 指纹 、 密码的组合从而体现了桥接模式的在这类场景中的合理运用。简化了代码的开发,给后续的需求迭代增加了很好的扩展性。
从桥接模式的实现形式来看满足了单一职责和开闭原则,让每一部分内容都很清晰易于维护和拓展,但如果我们是实现的高内聚的代码,那么就会很复杂。所以在选择重构代码的时候,需要考虑好整体的设计,否则选不到合理的设计模式,将会让代码变得难以开发。
作者:初念初恋
链接:https://juejin.cn/post/7204601386607116348
来源:稀土掘金
网友评论