睬永镭木责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
更简单的描述是:责任链是一个链表结构,这条链上有多个节点,它们有相同的接口,各自有各自的责任和实现。当有数据输入时,第一个节点看自己能否处理问题,如果可以就进行处理,如果不能就数据交给下一个节点进行处理,以此类推直到最后一个责任节点。
责任链模式的使用场景是:
有多个对象可以处理同一个请求,具体由哪个对象处理是在运行时确定的。
例如,有 ABC 三个处理者可以处理一个请求,请求 a 需要 AC 两个处理者处理,请求 b 需要 BC 两个处理者处理,同时两个处理者之间可能有前后依赖关系,这时候就可以使用责任链模式
在不确定请求处理者的时候,向同一类型的处理者中的一个发送请求
02 例子
笔者曾经通过责任链模式优化神州专车支付流程 。
支付流程是通过定时任务执行,每次都需要去判断用户的账号余额、优惠券、企业账号余额是否可以抵扣,假如满足条件,则从对应的账号资产里扣除对应的金额 。
因为原来的代码存在大量的 if/else 逻辑,业务逻辑相互杂糅,维护成本很高,勇哥花了一周时间重构了该逻辑 , 以下是演示伪代码:
1、定义抽象处理者(Handler)
/**
* 支付处理器接口
* 定义所有支付方式需要实现的方法
*/
public interface PaymentHandler {
/**
* 判断该支付处理器是否适用于当前用户
* @param userId 用户ID
* @return 是否适用
*/
boolean canProcess(String userId);
/**
* 执行支付操作
* @param userId 用户ID
* @param amount 支付金额
* @return 支付结果
*/
PaymentResult processPayment(String userId, double amount);
}
/**
* 支付结果类
* 封装支付操作的结果信息
*/
class PaymentResult {
private boolean success; // 支付是否成功
private double paidAmount; // 实际支付金额
private String message; // 支付结果描述信息
public PaymentResult(boolean success, double paidAmount, String message) {
this.success = success;
this.paidAmount = paidAmount;
this.message = message;
}
// Getter方法
public boolean isSuccess() {
return success;
}
public double getPaidAmount() {
return paidAmount;
}
public String getMessage() {
return message;
}
}
2、定义具体处理者(Concrete Handler)
/**
* 企业账户支付处理器实现
*/
public class CorporatePaymentHandler implements PaymentHandler {
private static final double MAX_CORPORATE_PAYMENT = 5000; // 企业账户最大支付限额
@Override
public boolean canProcess(String userId) {
// 只有以"emp_"开头的用户ID才能使用企业账户支付
return userId.startsWith("emp_");
}
@Override
public PaymentResult processPayment(String userId, double amount) {
System.out.printf("尝试使用企业账户支付: 用户[%s], 金额[%.2f]%n", userId, amount);
if (amount <= MAX_CORPORATE_PAYMENT) {
// 金额在限额内,全额支付
return new PaymentResult(true, amount, "企业账户支付成功");
} else {
// 超过限额,只支付限额部分
return new PaymentResult(true, MAX_CORPORATE_PAYMENT,
String.format("企业账户部分支付成功(%.2f),还需支付: %.2f",
MAX_CORPORATE_PAYMENT, amount - MAX_CORPORATE_PAYMENT));
}
}
}
/**
* 个人账户支付处理器实现
*/
public class PersonalPaymentHandler implements PaymentHandler {
private static final double MAX_PERSONAL_PAYMENT = 1000; // 个人账户最大支付限额
@Override
public boolean canProcess(String userId) {
// 所有用户都可以使用个人账户支付
return true;
}
@Override
public PaymentResult processPayment(String userId, double amount) {
System.out.printf("尝试使用个人账户支付: 用户[%s], 金额[%.2f]%n", userId, amount);
if (amount <= MAX_PERSONAL_PAYMENT) {
// 金额在限额内,全额支付
return new PaymentResult(true, amount, "个人账户支付成功");
} else {
// 超过限额,只支付限额部分
return new PaymentResult(true, MAX_PERSONAL_PAYMENT,
String.format("个人账户部分支付成功(%.2f),还需支付: %.2f",
MAX_PERSONAL_PAYMENT, amount - MAX_PERSONAL_PAYMENT));
}
}
}
3、定义组合支付上下文(核心数据结构是链表)
/**
* 组合支付上下文
* 负责管理支付处理器并执行组合支付
*/
public class CompositePaymentContext {
private final List handlers = new ArrayList<>();
/**
* 添加支付处理器
* @param handler 支付处理器实例
*/
public void addHandler(PaymentHandler handler) {
handlers.add(handler);
}
/**
* 执行组合支付
* @param userId 用户ID
* @param totalAmount 总支付金额
* @return 支付结果映射表,key为处理器类名,value为支付结果
*/
public Map executePayment(String userId, double totalAmount) {
// 使用LinkedHashMap保持支付尝试的顺序
Map paymentResults = new LinkedHashMap<>();
double remaining = totalAmount; // 剩余待支付金额
// 按处理器顺序尝试支付
for (PaymentHandler handler : handlers) {
// 检查处理器是否适用且还有金额需要支付
if (handler.canProcess(userId) && remaining > 0) {
// 执行支付
PaymentResult result = handler.processPayment(userId, remaining);
// 记录支付结果
paymentResults.put(handler.getClass().getSimpleName(), result);
if (result.isSuccess()) {
// 支付成功,减少剩余金额
remaining -= result.getPaidAmount();
if (remaining <= 0) {
break; // 金额已支付完成,退出循环
}
}
}
}
// 如果还有剩余金额未支付,记录剩余金额
if (remaining > 0) {
paymentResults.put("Remaining", new PaymentResult(false, remaining,
String.format("支付未完成,剩余金额: %.2f", remaining)));
}
return paymentResults;
}
}
4、测试类
/**
* 组合支付系统演示类
*/
public class CompositePaymentSystem {
public static void main(String[] args) {
// 初始化支付上下文
CompositePaymentContext paymentContext = new CompositePaymentContext();
// 注册支付处理器(顺序决定了支付尝试的优先级)
paymentContext.addHandler(new CorporatePaymentHandler()); // 先尝试企业账户
paymentContext.addHandler(new PersonalPaymentHandler()); // 再尝试个人账户
// 测试用例1:企业用户,只需企业账户支付
System.out.println("\n=== 测试用例1 ===");
System.out.println("用户ID: emp_789, 支付金额: 3000.00");
Map results1 = paymentContext.executePayment("emp_789", 3000);
results1.forEach((handlerName, result) -> {
System.out.printf("[%s] %s (支付金额: %.2f)%n",
handlerName, result.getMessage(), result.getPaidAmount());
});
// 测试用例2:企业用户,需要组合支付
System.out.println("\n=== 测试用例2 ===");
System.out.println("用户ID: emp_789, 支付金额: 6000.00");
Map results2 = paymentContext.executePayment("emp_789", 6000);
results2.forEach((handlerName, result) -> {
System.out.printf("[%s] %s (支付金额: %.2f)%n",
handlerName, result.getMessage(), result.getPaidAmount());
});
// 测试用例3:个人用户,只需个人账户支付
System.out.println("\n=== 测试用例3 ===");
System.out.println("用户ID: user123, 支付金额: 1500.00");
Map results3 = paymentContext.executePayment("user123", 1500);
results3.forEach((handlerName, result) -> {
System.out.printf("[%s] %s (支付金额: %.2f)%n",
handlerName, result.getMessage(), result.getPaidAmount());
});
}
}
4 规则引擎
01 概念
电商系统中,经常会有营销活动,比如支付订单时给与用户一定程度的优惠,比如活动规则:满 1000 减200 ,满 500 减 100 。
这种情况一般都是通过 if-else 做分支判断处理,还是非常简单的。
但假如需要频繁的修改活动规则,那么就需要频繁的修改代码,非常不容易维护(开发成本 + 上线成本)。
此时,引入规则引擎有顺其自然了。规则引擎的优势如下:
降低开发成本
业务人员独立配置业务规则,开发人员无需理解,以往需要业务人员告诉开发人员,开发人员需要理解才能开发,并且还需要大量的测试来确定是否正确,而且开发结果还容易和提出的业务有偏差,种种都导致开发成本上升
增加业务的透明度,业务人员配置之后其它人业务人员也能看到
提高了规则改动的效率和上线的速度
通过规则引擎,我们只需要将业务人员配置的规则转换成一个规则字符串,然后将该规则字符串保存进数据库中,当使用该规则时,只传递该规则所需要的参数,便可以直接计算出结果。
02 例子
回到刚才营销活动 ,使用 AviatorScript 规则引擎的流程如下:
1、创建运营脚本,将脚本存储在数据库 或者 配置中心。
if (amount>=1000){
return 200;
}elsif(amount>=500){
return 100;
}else{
return 0;
}
2、定义调用方法 。
public static BigDecimal getDiscount(BigDecimal amount, String rule) {
// 执行规则并计算最终价格
Map env = new HashMap();
env.put("amount", amount);
Expression expression = AviatorEvaluator.compile(
DigestUtils.md5Hex(rule.getBytes()),
rule,
true);
Object result = expression.execute(env);
if (result != null) {
return new BigDecimal(String.valueOf(result));
}
return null;
}
3、执行测试例子
// step1 : 此处可以从配置中心 或者数据库中获取
String rule = "if (amount>=1000){\n" +
" return 200;\n" +
"}elsif(amount>=500){\n" +
" return 100;\n" +
"}else{\n" +
" return 0;\n" +
"}\n";
// step 2: 通过 AviatorScript 执行
BigDecimal discount = getDiscount(new BigDecimal("600"), rule);
System.out.println("discount:" + discount);
// 执行结果是: discount:100
测试类地址:
5 总结
在实际开发中,我们经常会遇到需要根据不同条件执行不同逻辑的场景。
传统的if-else嵌套方式虽然直观,但随着业务复杂度增加,会导致代码臃肿、可读性差、维护困难等问题。
本文总结了四种优雅的设计模式来解决这类"条件爆炸"问题。
01. 策略模式:灵活切换策略
核心思想:定义一系列算法,将每个算法封装起来,并使它们可以相互替换。
适用场景:
系统中有多个相似的类,仅在行为上有差异
需要在运行时动态选择算法
需要避免暴露复杂的条件判断
优势:
符合开闭原则,易于扩展新策略
消除大量条件语句
算法可以自由切换
示例:支付系统中不同支付方式的处理,如信用卡、支付宝、微信支付等。
02. SPI机制:服务发现的优雅实现
核心思想:通过配置文件动态发现和加载服务实现。
适用场景:
需要实现插件化架构
服务提供方需要独立于服务调用方
需要运行时动态发现服务
优势:
实现完全解耦
新增实现无需修改主代码
支持热插拔
示例:JDBC驱动加载、Dubbo的扩展点实现。
03. 责任链模式:处理流程的链式传递
核心思想:将请求的发送者和接收者解耦,使多个对象都有机会处理请求。
适用场景:
有多个对象可以处理同一请求
需要动态指定处理流程
处理顺序很重要
优势:
降低耦合度
灵活调整处理顺序
新增处理器方便
示例:支付流程中的多账户组合支付、审批流程等。
04. 规则引擎:业务规则的动态管理
核心思想:将业务规则从代码中分离,实现动态配置。
适用场景:
业务规则频繁变化
需要非技术人员配置规则
规则复杂度高
优势:
业务人员可自主配置
降低开发成本
提高规则透明度
示例:营销活动规则、风控规则等。
67

被折叠的 条评论
为什么被折叠?



