桥接模式介绍

桥接模式的作用是通过将抽象部分与实现部分分离,将多种可匹配的使用进行组合。其核心实现是在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;
/**
* @author bestrookie
* @date 2021/11/8 9:24 上午
*/
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实现方式来看,这时两种不同类型的相互组合。可以把支付类型和支付模式分离,通过抽象类依赖实现类的方式进行桥接。按照这种方式拆分后,支付方式与支付模式可以单独使用,当需要组合时,只需要把模式传递给各类支付方式。

桥接模式的关键是选择桥接点拆分,看能否找到这样类似的相互组合,如果没有就不用必须使用桥接模式。

image-20211108135715598

  • 左侧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;
/**
* @author bestrookie
* @date 2021/11/8 10:02 上午
*/
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;
/**
* @author bestrookie
* @date 2021/11/8 10:25 上午
*/
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;
/**
* @author bestrookie
* @date 2021/11/8 10:37 上午
*/
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;
/**
* @author bestrookie
* @date 2021/11/8 10:06 上午
*/
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;

/**
* @author bestrookie
* @date 2021/11/8 10:29 上午
*/
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;
/**
* @author bestrookie
* @date 2021/11/8 10:30 上午
*/
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;
/**
* @author bestrookie
* @date 2021/11/8 10:31 上午
*/
public class PayCypher implements IPayModel{
public boolean security(String uId) {
System.out.println("密码支付=======");
return true;
}
}

与上面的if…else实现方式相比,这里的调用方式变得整洁、干净和易用。玩不使用接口的用户不需要关心具体的实现,只需要选择使用即可。

总结

从桥接模式的实现形式来看,它满足了单一职责和开闭原则,让每一部分内容都很清晰,易于维护和扩展。但如果实现的高内聚的代码,则会很复杂。所以在选择重构代码时,需要考虑整体的设计。如果运用的设计模式不合理,也会让代码变得难以开发和维护。

任何一种设计模式的原则和使用都应该以符合场景为主,不能刻意使用。由于业务的复杂性,可能需要用到多种设计模式,才能将代码变得更加合理。

评论