Bridge
桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。
为什么要有桥接模式?
假如你有一个几何 形状
Shape类, 从它能扩展出两个子类: 圆形
Circle和 方形
Square 。 你希望对这样的类层次结构进行扩展以使其包含颜色, 所以你打算创建名为 红色
Red和 蓝色
Blue的形状子类。 但是, 由于你已有两个子类, 所以总共需要创建四个类才能覆盖所有组合, 例如 蓝色圆形
BlueCircle和 红色方形
RedSquare 。
在层次结构中新增形状和颜色将导致代码复杂程度指数增长。 例如添加三角形状, 你需要新增两个子类, 也就是每种颜色一个; 此后新增一种新颜色需要新增三个子类, 即每种形状一个。 如此以往, 情况会越来越糟糕。
问题的根本原因是我们试图在两个独立的维度——形状与颜色——上扩展形状类。 这在处理类继承时是很常见的问题。
桥接模式通过将继承改为组合的方式来解决这个问题。 具体来说, 就是抽取其中一个维度并使之成为独立的类层次, 这样就可以在初始类中引用这个新层次的对象, 从而使得一个类不必拥有所有的状态和行为。
根据该方法, 我们可以将颜色相关的代码抽取到拥有 红色
和 蓝色
两个子类的颜色类中,然后在 形状
类中添加一个指向某一颜色对象的引用成员变量。 现在, 形状类可以将所有与颜色相关的工作委派给连入的颜色对象。 这样的引用就成为了 形状
和 颜色
之间的桥梁。此后, 新增颜色将不再需要修改形状的类层次, 反之亦然。
桥接模式结构
使用示例: 桥接模式在处理跨平台应用、 支持多种类型的数据库服务器或与多个特定种类(例如云平台和社交网络等) 的 API 供应商协作时会特别有用。
识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别。
样例
第三方平台承接各个支付能力
支付类型桥接抽象类
package structural.bridge.method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
/**
* 支付类型桥接抽象类
*/
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);
}
桥接的核心为 IPayMode
两个支付类型的实现
微信支付
package structural.bridge.method;
import java.math.BigDecimal;
public class WxPay extends Pay {
public WxPay(IPayMode payMode) {
super(payMode);
}
@Override
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";
}
}
支付宝支付
package structural.bridge.method;
import java.math.BigDecimal;
public class ZfbPay extends Pay {
public ZfbPay(IPayMode payMode) {
super(payMode);
}
@Override
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";
}
}
支付模式接口
package structural.bridge.method;
/**
* 定义支付模式接口
*/
public interface IPayMode {
boolean security(String uId);
}
任何一个支付模式;刷脸、指纹、密码,都会过不同程度的安全风控,这里定义一个安全校验接口。
三种支付模式风控(刷脸、指纹、密码)
package structural.bridge.method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayFaceMode implements IPayMode {
protected Logger logger = LoggerFactory.getLogger(PayFaceMode.class);
@Override
public boolean security(String uId) {
logger.info("人脸支付,风控校验脸部识别");
return true;
}
}
package structural.bridge.method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayFingerprintMode implements IPayMode {
protected Logger logger = LoggerFactory.getLogger(PayFingerprintMode.class);
@Override
public boolean security(String uId) {
logger.info("指纹支付,风控校验指纹信息");
return true;
}
}
package structural.bridge.method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayCypher implements IPayMode {
protected Logger logger = LoggerFactory.getLogger(PayCypher.class);
@Override
public boolean security(String uId) {
logger.info("密码支付,风控校验环境安全");
return true;
}
}
测试
@Test
public void testPayBridge() {
System.out.println("\r\n 模拟测试场景:微信支付、人脸方式。");
Pay wxPay = new WxPay(new PayFaceMode());
wxPay.transfer("weixin_001111", "100055464", new BigDecimal(100));
System.out.println("\r\n 模拟测试场景: 支付宝支付、指纹方式");
Pay zfbPay = new ZfbPay(new PayFingerprintMode());
zfbPay.transfer("jlsjdoi2", "454654", new BigDecimal(122));
}
/**
* 模拟测试场景:微信支付、人脸方式。
* 19:33:36.811 [main] INFO structural.bridge.method.Pay - 模拟微信渠道支付划账开始。 uId: weixin_001111 tradeId: 100055464 amount: 100
* 19:33:36.816 [main] INFO structural.bridge.method.PayFaceMode - 人脸支付,风控校验脸部识别
* 19:33:36.816 [main] INFO structural.bridge.method.Pay - 模拟微信渠道支付风控校验. uId: weixin_001111 tradeId: 100055464 security: true
* 19:33:36.816 [main] INFO structural.bridge.method.Pay - 模拟微信渠道支付划账成功. uId: weixin_001111 tradeId: 100055464 amount: 100
*
* 模拟测试场景: 支付宝支付、指纹方式
* 19:33:36.817 [main] INFO structural.bridge.method.Pay - 模拟支付宝渠道支付划账开始。 uId: jlsjdoi2 tradeId: 454654 amount: 122
* 19:33:36.817 [main] INFO s.bridge.method.PayFingerprintMode - 指纹支付,风控校验指纹信息
* 19:33:36.817 [main] INFO structural.bridge.method.Pay - 模拟支付宝渠道支付风控校验. uId: jlsjdoi2 tradeId: 454654 security: true
* 19:33:36.817 [main] INFO structural.bridge.method.Pay - 模拟支付宝渠道支付划账成功. uId: jlsjdoi2 tradeId: 454654 amount: 122
*/
桥接模式优点
- 你可以创建与平台无关的类和程序。
- 客户端代码仅与高层抽象部分进行互动,不会接触到平台的详细信息。
- 开闭原则。 你可以新增抽象部分和实现部分, 且它们之间不会相互影响。
- 单一职责原则。 抽象部分专注于处理高层逻辑, 实现部分处理平台细节。
桥接模式缺点
- 对高内聚的类使用该模式可能会让代码更加复杂。