责任链模式实战应用:从理论到生产实践

责任链模式实战应用:从理论到生产实践

在企业级开发中,我们经常遇到这样的场景:一个请求需要多个对象依次处理,每个对象根据自身职责决定是否处理或传递下去。这时候,责任链模式就能派上大用场。

一、什么是责任链模式

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它允许多个对象按顺序处理请求,每个对象都有机会处理请求或将其传递给链中的下一个对象。

1.1 核心思想

为请求创建一个接收者对象链,让请求沿着链传递,直到有对象处理它为止。

1.2 系统架构图

从上图可以看出,责任链模式包含以下几个核心层次:

  • 请求层:封装需要在链中传递的数据
  • 处理器接口层:定义处理器的标准行为
  • 抽象处理器层:实现责任链的基本逻辑
  • 具体处理器层:实现具体的业务处理逻辑
  • 客户端层:创建链并发起请求

1.3 为什么要使用责任链模式

假设你在开发一个企业OA系统的审批模块:

scss 复制代码
传统写法:
if (amount <= 1000) {
    teamLeader.approve();
} else if (amount <= 5000) {
    teamLeader.approve();
    manager.approve();
} else if (amount <= 20000) {
    teamLeader.approve();
    manager.approve();
    director.approve();
} else {
    // 一大堆代码逻辑
    .........
    .........
}

这种写法的问题显而易见:

  • 代码耦合严重,难以维护
  • 新增审批人需要修改大量代码
  • 不符合开闭原则
  • 逻辑复杂,容易出错

而使用责任链模式后:

scss 复制代码
✓ 责任链写法:
teamLeader.setNext(manager).setNext(director).setNext(vp);
teamLeader.handle(request);

简洁、优雅、易扩展!

二、责任链模式核心设计

2.1 类图结构

核心角色:

  1. Handler(处理器接口) :定义处理请求的接口
  2. AbstractHandler(抽象处理器) :实现责任链的基本逻辑,维护对下一个处理器的引用
  3. ConcreteHandler(具体处理器) :具体的业务逻辑实现

2.2 工作原理

工作流程:

  1. 客户端将请求发送给责任链的第一个处理器
  2. 每个处理器判断自己是否能处理该请求
  3. 如果能处理则处理并返回(可选择是否继续传递)
  4. 如果不能处理则传递给下一个处理器
  5. 直到有处理器处理或到达链尾

三、实战案例一:企业审批流程(Apache Commons Chain实现)

3.1 需求分析

某企业的费用审批规则如下:

金额范围 审批人
0-1000元 组长
1000-5000元 经理
5000-20000元 总监
20000元以上 VP
超过50000元 拒绝(需董事会审批)

3.2 添加Maven依赖

xml 复制代码
<dependency>
    <groupId>commons-chain</groupId>
    <artifactId>commons-chain</artifactId>
    <version>1.2</version>
</dependency>

3.3 代码实现

定义审批上下文:

typescript 复制代码
public class ApprovalContext extends ContextBase {
    public String getApplicant() {
        return (String) get("applicant");
    }

    public void setApplicant(String applicant) {
        put("applicant", applicant);
    }

    public Double getAmount() {
        return (Double) get("amount");
    }

    public void setAmount(Double amount) {
        put("amount", amount);
    }

    public String getReason() {
        return (String) get("reason");
    }

    public void setReason(String reason) {
        put("reason", reason);
    }

    public boolean isApproved() {
        Boolean approved = (Boolean) get("approved");
        return approved != null && approved;
    }

    public void setApproved(boolean approved) {
        put("approved", approved);
    }

    public String getApprover() {
        return (String) get("approver");
    }

    public void setApprover(String approver) {
        put("approver", approver);
    }
}

定义审批命令(Command):

java 复制代码
// 组长审批命令
public class TeamLeaderCommand implements Command {
    private static final double MAX_AMOUNT = 1000;
    private final String teamLeaderName;

    public TeamLeaderCommand(String teamLeaderName) {
        this.teamLeaderName = teamLeaderName;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        ApprovalContext ctx = (ApprovalContext) context;
        Double amount = ctx.getAmount();

        System.out.println("【组长-" + teamLeaderName + "】审批:" + ctx.getApplicant() + "申请" + amount + "元");

        if (amount <= MAX_AMOUNT) {
            ctx.setApproved(true);
            ctx.setApprover(teamLeaderName);
            System.out.println("  ✓ 组长审批通过");
            return false;  // 处理完成,终止链条
        }

        System.out.println("  → 提交上级审批");
        return true;  // 继续执行下一个命令
    }
}

// 经理审批命令
public class ManagerCommand implements Command {
    private static final double MIN_AMOUNT = 1000;
    private static final double MAX_AMOUNT = 5000;
    private final String managerName;

    public ManagerCommand(String managerName) {
        this.managerName = managerName;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        ApprovalContext ctx = (ApprovalContext) context;
        Double amount = ctx.getAmount();

        System.out.println("【经理-" + managerName + "】审批:" + ctx.getApplicant() + "申请" + amount + "元");

        if (amount > MIN_AMOUNT && amount <= MAX_AMOUNT) {
            ctx.setApproved(true);
            ctx.setApprover(managerName);
            System.out.println("  ✓ 经理审批通过");
            return false;  // 处理完成,终止链条
        }

        System.out.println("  → 提交上级审批");
        return true;  // 继续执行下一个命令
    }
}

// 总监审批命令
public class DirectorCommand implements Command {
    private static final double MIN_AMOUNT = 5000;
    private static final double MAX_AMOUNT = 20000;
    private final String directorName;

    public DirectorCommand(String directorName) {
        this.directorName = directorName;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        ApprovalContext ctx = (ApprovalContext) context;
        Double amount = ctx.getAmount();

        System.out.println("【总监-" + directorName + "】审批:" + ctx.getApplicant() + "申请" + amount + "元");

        if (amount > MIN_AMOUNT && amount <= MAX_AMOUNT) {
            ctx.setApproved(true);
            ctx.setApprover(directorName);
            System.out.println("  ✓ 总监审批通过");
            return false;  // 处理完成,终止链条
        }

        System.out.println("  → 提交上级审批");
        return true;  // 继续执行下一个命令
    }
}

// VP审批命令
public class VPCommand implements Command {
    private static final double MIN_AMOUNT = 20000;
    private static final double REJECT_AMOUNT = 50000;
    private final String vpName;

    public VPCommand(String vpName) {
        this.vpName = vpName;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        ApprovalContext ctx = (ApprovalContext) context;
        Double amount = ctx.getAmount();

        System.out.println("【VP-" + vpName + "】审批:" + ctx.getApplicant() + "申请" + amount + "元");

        if (amount > REJECT_AMOUNT) {
            ctx.setApproved(false);
            ctx.setApprover("系统");
            System.out.println("  ✗ 金额过大,需董事会审批");
            return false;  // 处理完成,终止链条
        }

        ctx.setApproved(true);
        ctx.setApprover(vpName);
        System.out.println("  ✓ VP审批通过");
        return false;  // 处理完成,终止链条
    }
}

配置审批责任链:

arduino 复制代码
public class ApprovalChainConfig {
    private final String teamLeaderName;
    private final String managerName;
    private final String directorName;
    private final String vpName;

    public ApprovalChainConfig(String teamLeaderName, String managerName,
                               String directorName, String vpName) {
        this.teamLeaderName = teamLeaderName;
        this.managerName = managerName;
        this.directorName = directorName;
        this.vpName = vpName;
    }

    public ChainBase createApprovalChain() {
        ChainBase chain = new ChainBase();

        // 按顺序添加审批命令:组长 -> 经理 -> 总监 -> VP
        chain.addCommand(new TeamLeaderCommand(teamLeaderName));
        chain.addCommand(new ManagerCommand(managerName));
        chain.addCommand(new DirectorCommand(directorName));
        chain.addCommand(new VPCommand(vpName));

        return chain;
    }
}

使用示例:

csharp 复制代码
public class ApprovalChainDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("========================================");
        System.out.println("   Apache Commons Chain - 企业审批流程");
        System.out.println("========================================\n");

        // 创建审批责任链
        ApprovalChainConfig config = new ApprovalChainConfig("张三", "李四", "王五", "赵六");
        ChainBase approvalChain = config.createApprovalChain();

        // 测试场景1:500元 - 组长审批
        testApproval(approvalChain, "小明", "技术部", 500.0, "购买办公文具");

        System.out.println();

        // 测试场景2:3000元 - 经理审批
        testApproval(approvalChain, "小红", "市场部", 3000.0, "客户招待费");

        System.out.println();

        // 测试场景3:10000元 - 总监审批
        testApproval(approvalChain, "小刚", "销售部", 10000.0, "市场推广费用");

        System.out.println();

        // 测试场景4:30000元 - VP审批
        testApproval(approvalChain, "小丽", "研发部", 30000.0, "服务器采购");

        System.out.println();

        // 测试场景5:60000元 - 拒绝
        testApproval(approvalChain, "小华", "运营部", 60000.0, "年度团建费用");
    }

    private static void testApproval(ChainBase chain, String applicant,
                                     String department, double amount, String reason) throws Exception {
        System.out.println("----------------------------------------");
        System.out.println("测试场景:" + applicant + "申请" + amount + "元");
        System.out.println("----------------------------------------");

        ApprovalContext context = new ApprovalContext();
        context.setApplicant(applicant);
        context.setAmount(amount);
        context.setReason(reason);

        chain.execute(context);

        System.out.println("\n最终结果:");
        System.out.println("  状态:" + (context.isApproved() ? "✓ 通过" : "✗ 拒绝"));
        System.out.println("  审批人:" + context.getApprover());
    }
}

3.4 Apache Commons Chain的优势

通过使用Apache Commons Chain实现审批流程,带来以下优势:

  1. 配置灵活:可以通过XML配置或编程方式动态调整审批链
  2. 职责分离:每个Command只关注自己的审批逻辑
  3. 易于扩展:新增审批人只需添加新的Command
  4. 统一上下文:Context在整个链条中传递数据
  5. 标准化:使用成熟的开源框架,代码更规范

四、实战案例二:日志处理系统(Apache Commons Chain实现)

4.1 场景描述

在日志系统中,不同级别的日志需要不同的处理方式:

  • DEBUG/INFO:输出到控制台
  • INFO及以上:写入文件
  • ERROR:发送告警邮件

4.2 代码实现

定义日志上下文:

csharp 复制代码
public class LogContext extends ContextBase {
    public LogLevel getLevel() {
        return (LogLevel) get("level");
    }

    public void setLevel(LogLevel level) {
        put("level", level);
    }

    public String getMessage() {
        return (String) get("message");
    }

    public void setMessage(String message) {
        put("message", message);
    }

    public LocalDateTime getTimestamp() {
        return (LocalDateTime) get("timestamp");
    }

    public void setTimestamp(LocalDateTime timestamp) {
        put("timestamp", timestamp);
    }
}

enum LogLevel {
    DEBUG(1), INFO(2), WARN(3), ERROR(4);
    private final int level;
    LogLevel(int level) { this.level = level; }
    public int getLevel() { return level; }
}

定义日志处理命令:

java 复制代码
// 控制台日志命令
public class ConsoleLogCommand implements Command {
    private final LogLevel minLevel;

    public ConsoleLogCommand(LogLevel minLevel) {
        this.minLevel = minLevel;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        LogContext ctx = (LogContext) context;

        if (ctx.getLevel().getLevel() >= minLevel.getLevel()) {
            String logMessage = String.format("[%s] %s %s",
                    ctx.getLevel(),
                    ctx.getTimestamp(),
                    ctx.getMessage());
            System.out.println(logMessage);
        }

        return true;  // 继续执行下一个命令
    }
}

// 文件日志命令
public class FileLogCommand implements Command {
    private final LogLevel minLevel;
    private final String filePath;

    public FileLogCommand(LogLevel minLevel, String filePath) {
        this.minLevel = minLevel;
        this.filePath = filePath;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        LogContext ctx = (LogContext) context;

        if (ctx.getLevel().getLevel() >= minLevel.getLevel()) {
            try (FileWriter writer = new FileWriter(filePath, true)) {
                String logEntry = String.format("[%s] %s %s%n",
                        ctx.getLevel(),
                        ctx.getTimestamp(),
                        ctx.getMessage());
                writer.write(logEntry);
                System.out.println(">>> 已写入文件:" + filePath);
            }
        }

        return true;  // 继续执行下一个命令
    }
}

// 错误告警命令
public class ErrorAlertCommand implements Command {
    private final String alertEmail;

    public ErrorAlertCommand(String alertEmail) {
        this.alertEmail = alertEmail;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        LogContext ctx = (LogContext) context;

        if (ctx.getLevel() == LogLevel.ERROR) {
            // 模拟发送告警邮件
            System.out.println("!!! 发送告警邮件到:" + alertEmail);
            System.out.println("!!! 错误内容:" + ctx.getMessage());
        }

        return true;  // 继续执行下一个命令
    }
}

配置日志处理链:

csharp 复制代码
public class LoggingChainConfig {
    public ChainBase createLoggingChain() {
        ChainBase chain = new ChainBase();

        // 按顺序添加日志处理命令
        chain.addCommand(new ConsoleLogCommand(LogLevel.DEBUG));
        chain.addCommand(new FileLogCommand(LogLevel.INFO, "application.log"));
        chain.addCommand(new ErrorAlertCommand("admin@example.com"));

        return chain;
    }
}

使用示例:

scss 复制代码
public class LoggingChainDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("========================================");
        System.out.println("   Apache Commons Chain - 日志处理系统");
        System.out.println("========================================\n");

        LoggingChainConfig config = new LoggingChainConfig();
        ChainBase loggingChain = config.createLoggingChain();

        // 测试不同级别的日志
        testLog(loggingChain, LogLevel.DEBUG, "系统启动中...");
        System.out.println();

        testLog(loggingChain, LogLevel.INFO, "用户登录成功");
        System.out.println();

        testLog(loggingChain, LogLevel.WARN, "内存使用率达到80%");
        System.out.println();

        testLog(loggingChain, LogLevel.ERROR, "数据库连接失败");
    }

    private static void testLog(ChainBase chain, LogLevel level, String message) throws Exception {
        LogContext context = new LogContext();
        context.setLevel(level);
        context.setMessage(message);
        context.setTimestamp(LocalDateTime.now());

        chain.execute(context);
    }
}

五、实战案例三:订单创建的多重校验(Apache Commons Chain实现)

5.1 场景描述

在电商系统中,创建订单时需要进行多重校验,包括:

  • 用户状态校验(是否被禁用)
  • 商品库存校验(是否充足)
  • 优惠券有效性校验(是否可用)
  • 金额限制校验(是否超限)
  • 风控规则校验(是否可疑订单)

5.2 代码实现

定义订单上下文:

typescript 复制代码
public class OrderContext extends ContextBase {
    public Long getUserId() { return (Long) get("userId"); }
    public void setUserId(Long userId) { put("userId", userId); }

    public Long getProductId() { return (Long) get("productId"); }
    public void setProductId(Long productId) { put("productId", productId); }

    public Integer getQuantity() { return (Integer) get("quantity"); }
    public void setQuantity(Integer quantity) { put("quantity", quantity); }

    public Long getCouponId() { return (Long) get("couponId"); }
    public void setCouponId(Long couponId) { put("couponId", couponId); }

    public BigDecimal getTotalAmount() { return (BigDecimal) get("totalAmount"); }
    public void setTotalAmount(BigDecimal amount) { put("totalAmount", amount); }

    public boolean isValid() { return Boolean.TRUE.equals(get("valid")); }
    public void setValid(boolean valid) { put("valid", valid); }

    public String getErrorCode() { return (String) get("errorCode"); }
    public void setErrorCode(String errorCode) { put("errorCode", errorCode); }

    public String getErrorMsg() { return (String) get("errorMsg"); }
    public void setErrorMsg(String errorMsg) { put("errorMsg", errorMsg); }
}

定义校验命令:

java 复制代码
// 用户状态校验命令
public class UserValidateCommand implements Command {
    private final UserService userService;

    public UserValidateCommand(UserService userService) {
        this.userService = userService;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        OrderContext ctx = (OrderContext) context;
        User user = userService.getById(ctx.getUserId());

        if (user == null || user.getStatus() != UserStatus.NORMAL) {
            ctx.setValid(false);
            ctx.setErrorCode("USER_INVALID");
            ctx.setErrorMsg("用户状态异常");
            return false;  // 终止链条
        }

        return true;  // 继续执行
    }
}

// 商品库存校验命令
public class StockValidateCommand implements Command {
    private final ProductService productService;

    public StockValidateCommand(ProductService productService) {
        this.productService = productService;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        OrderContext ctx = (OrderContext) context;
        Product product = productService.getById(ctx.getProductId());

        if (product == null || product.getStock() < ctx.getQuantity()) {
            ctx.setValid(false);
            ctx.setErrorCode("STOCK_INSUFFICIENT");
            ctx.setErrorMsg("库存不足");
            return false;  // 终止链条
        }

        return true;  // 继续执行
    }
}

// 其他校验命令(优惠券、金额、风控)省略...

配置订单校验链:

java 复制代码
@Service
public class OrderValidatorChain {
    private final ChainBase validatorChain;

    public OrderValidatorChain(UserService userService,
                               ProductService productService,
                               CouponService couponService,
                               RiskControlService riskControlService) {
        ChainBase chain = new ChainBase();

        // 按顺序添加校验命令
        chain.addCommand(new UserValidateCommand(userService));
        chain.addCommand(new StockValidateCommand(productService));
        chain.addCommand(new CouponValidateCommand(couponService));
        chain.addCommand(new AmountValidateCommand());
        chain.addCommand(new RiskControlValidateCommand(riskControlService));

        this.validatorChain = chain;
    }

    public ValidationResult validate(OrderRequest request) {
        try {
            OrderContext context = new OrderContext();
            context.setUserId(request.getUserId());
            context.setProductId(request.getProductId());
            context.setQuantity(request.getQuantity());
            context.setCouponId(request.getCouponId());
            context.setTotalAmount(request.getTotalAmount());
            context.setValid(true);  // 默认为有效

            validatorChain.execute(context);

            if (context.isValid()) {
                return ValidationResult.success();
            } else {
                return ValidationResult.fail(context.getErrorCode(), context.getErrorMsg());
            }
        } catch (Exception e) {
            return ValidationResult.fail("SYSTEM_ERROR", "系统异常");
        }
    }
}

六、Apache Commons Chain核心优势

通过前面的实战案例,我们可以总结出Apache Commons Chain的核心优势:

6.1 标准化

  • 成熟的开源框架:Apache Commons Chain是Apache软件基金会下的成熟项目
  • 经过大量项目验证:在众多企业级项目中得到验证
  • 社区支持:拥有活跃的社区支持和完善的文档

6.2 灵活性

  • 支持多种配置方式:可以通过XML配置或编程方式配置责任链
  • 动态调整:可以在运行时动态添加或删除Command
  • 可嵌套:支持Chain的嵌套,可以构建复杂的处理流程

6.3 易用性

  • 简单的API:只需要实现Command接口的execute方法
  • 统一的Context:Context在整个链条中传递数据
  • 清晰的执行流程:return true继续执行,return false终止链条

6.4 适用场景

特别适合以下场景:

  1. 复杂业务流程编排:订单处理、审批流程、工作流等
  2. 规则引擎:业务规则链式执行、风控规则等
  3. 任务处理:异步任务处理、批量任务处理等
  4. 审批系统:多级审批、条件分支审批等

6.5 与手写责任链的对比

对比项 手写责任链 Apache Commons Chain
实现复杂度 需要自己实现链式逻辑 使用现成的框架
灵活性 完全自定义 支持自定义但遵循框架规范
配置方式 硬编码 支持XML和编程配置
学习成本 需要学习框架API
可维护性 取决于实现质量 统一标准,易于维护
生态支持 Apache社区支持

建议:

  • 对于简单场景:手写责任链即可
  • 对于复杂场景:推荐使用Apache Commons Chain
  • 对于企业级项目:推荐使用Apache Commons Chain

七、责任链模式的应用场景

典型应用场景:

  1. 审批流程:请假、报销、采购等企业审批
  2. 日志处理:不同级别日志输出到不同目标
  3. 异常处理:Web框架的异常处理链
  4. 过滤器链:请求预处理、编码转换、权限校验

7.1 适用条件

适合使用责任链模式的场景:

  • 有多个对象可以处理请求,但不知道具体哪个对象处理
  • 处理请求的对象需要动态指定
  • 处理顺序很重要
  • 需要在不影响客户端的情况下动态增删处理器

不适合使用责任链模式的场景:

  • 只有一个处理器
  • 处理顺序不重要
  • 性能要求极高(责任链有性能开销)

八、最佳实践

8.1 性能优化

纯责任链 vs 不纯责任链

typescript 复制代码
// 纯责任链:一个请求只能被一个处理器处理
protected boolean doHandle(Request request) {
    if (canHandle(request)) {
        // 处理并结束
        return true;
    }
    return false;  // 传递给下一个
}

// 不纯责任链:请求可以被多个处理器处理
protected void handle(Request request) {
    if (canHandle(request)) {
        doHandle(request);  // 处理后继续传递
    }
    if (next != null) {
        next.handle(request);
    }
}

性能考虑:

  1. 控制链条长度:过长的链条会影响性能
  2. 避免循环引用:确保链条有终点
  3. 使用缓存:对处理器实例进行缓存
  4. 异步处理:对于耗时操作考虑异步处理

8.2 实战技巧

技巧1:使用建造者模式构建责任链

csharp 复制代码
public class ResponsibilityChainBuilder {
    private List<Handler> handlers = new ArrayList<>();

    public ResponsibilityChainBuilder addHandler(Handler handler) {
        handlers.add(handler);
        return this;
    }

    public Handler build() {
        if (handlers.isEmpty()) {
            throw new IllegalStateException("至少需要一个处理器");
        }

        for (int i = 0; i < handlers.size() - 1; i++) {
            handlers.get(i).setNext(handlers.get(i + 1));
        }
        return handlers.get(0);
    }
}

// 使用
Handler chain = new ResponsibilityChainBuilder()
    .addHandler(new TeamLeaderHandler("张三"))
    .addHandler(new ManagerHandler("李四"))
    .addHandler(new DirectorHandler("王五"))
    .build();

技巧2:责任链+策略模式

java 复制代码
// 策略接口
public interface HandleStrategy {
    boolean canHandle(Request request);
    boolean doHandle(Request request);
}

// 处理器使用策略
public class StrategyHandler extends AbstractHandler {
    private HandleStrategy strategy;

    public StrategyHandler(String name, HandleStrategy strategy) {
        super(name);
        this.strategy = strategy;
    }

    @Override
    protected boolean canHandle(Request request) {
        return strategy.canHandle(request);
    }

    @Override
    protected boolean doHandle(Request request) {
        return strategy.doHandle(request);
    }
}

九、总结

核心要点

  1. 解耦:将请求者与处理者解耦,提高系统灵活性
  2. 动态:可以动态地增加或删除处理器
  3. 扩展:符合开闭原则,易于扩展新功能
  4. 灵活:可以自定义处理顺序和处理逻辑

责任链模式是一种简单但强大的设计模式,掌握它能让你的代码更加优雅、灵活。希望通过本文的实战案例,你能在实际项目中灵活运用这一模式。

相关推荐
沛沛老爹18 小时前
Web开发者进阶AI:Agent技能设计模式之迭代分析与上下文聚合实战
前端·人工智能·设计模式
Geoking.20 小时前
【设计模式】装饰者模式详解
设计模式·装饰器模式
vx-bot55566621 小时前
企业微信接口在自动化工作流中的关键角色与设计模式
设计模式·自动化·企业微信
Yu_Lijing1 天前
基于C++的《Head First设计模式》笔记——工厂模式
c++·笔记·设计模式
HL_风神1 天前
设计原则之迪米特
c++·学习·设计模式
HL_风神1 天前
设计原则之合成复用
c++·学习·设计模式
Aeside12 天前
揭秘 Nginx 百万并发基石:Reactor 架构与 Epoll 底层原理
后端·设计模式
帅气的你2 天前
从零封装一个通用的 API 接口返回类:统一前后端交互格式
java·设计模式
阿里巴巴淘系技术团队官网博客2 天前
GenAI输出内容控制的5种设计模式
设计模式