断言 + 异常机制 = 清晰简洁的代码

业务代码的"噪音"

如果让一个开发者回顾自己写的业务代码,他会发现一个令人沮丧的事实:真正处理业务的代码可能只占 30%,剩下的 70% 都是各种"防御性代码"。

// 这种代码你一定写过
public Result buyItem(long userId, int itemId, int count) {
    if (userId <= 0) {
        return Result.fail(1001, "用户ID无效");
    }
    if (itemId <= 0) {
        return Result.fail(1002, "商品ID无效");
    }
    if (count <= 0 || count > 99) {
        return Result.fail(1003, "购买数量无效");
    }

    User user = userService.getUser(userId);
    if (user == null) {
        return Result.fail(1004, "用户不存在");
    }
    if (user.isBanned()) {
        return Result.fail(1005, "用户已被封禁");
    }

    Item item = itemService.getItem(itemId);
    if (item == null) {
        return Result.fail(1006, "商品不存在");
    }
    if (!item.isOnSale()) {
        return Result.fail(1007, "商品已下架");
    }
    if (user.getGold() < item.getPrice() * count) {
        return Result.fail(1008, "金币不足");
    }

    // === 终于到业务逻辑了 ===
    user.setGold(user.getGold() - item.getPrice() * count);
    user.addItem(itemId, count);
    return Result.success("购买成功");
}

20 行防御性代码,3 行业务逻辑。这不是代码,这是防弹衣。


ionet 的解法:断言 + 异常

在 ionet 中,同样的业务用断言写:

@ActionMethod(BuyCmd.buyItem)
private void buyItem(FlowContext flowContext, BuyMessage message) {
    // 断言:直接抛出错误码,框架自动返回给客户端
    ErrorCode.userNotFound.assertTrue(user != null);
    ErrorCode.userBanned.assertFalse(user.isBanned());
    ErrorCode.itemNotFound.assertTrue(item != null);
    ErrorCode.itemOffSale.assertTrue(item.isOnSale());
    ErrorCode.goldNotEnough.assertTrue(user.getGold() >= item.getPrice() * message.count);

    // 业务逻辑
    user.setGold(user.getGold() - item.getPrice() * message.count);
    user.addItem(message.itemId, message.count);
}

关键差异

  1. 没有 if-else:断言失败自动抛出异常
  2. 没有 Result 封装:框架自动将错误码返回给客户端
  3. 方法签名清晰:return 就是正常的业务数据,不需要 Result<T> 包装
  4. 可以直接 return void:如果不需要返回数据给客户端

ErrorCode 枚举:集中管理错误码

public enum ErrorCode implements ErrorInformation {
    // 用户相关
    userNotFound(1001, "用户不存在"),
    userBanned(1002, "用户已被封禁"),

    // 商品相关
    itemNotFound(2001, "商品不存在"),
    itemOffSale(2002, "商品已下架"),

    // 资源相关
    goldNotEnough(3001, "金币不足"),
    diamondNotEnough(3002, "钻石不足");

    final int code;
    final String message;

    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public String getMessage() { return this.message; }

    @Override
    public int getCode() { return this.code; }
}

所有错误码集中在一个枚举中,一目了然。不会出现"这个错误码 1008 是在哪个方法里定义的?"的困惑。


异常的传播路径

当断言失败时,内部流程是这样的:

断言失败 → 抛出业务异常(携带 ErrorCode)
    → 业务框架捕获异常
    → 将 ErrorCode 放入 ResponseMessage.responseStatus
    → 通过对外服协议返回给客户端
    → 客户端收到 { responseStatus: 1001, message: "用户不存在" }

你不需要做任何额外的事情。 不需要写异常处理器,不需要全局拦截器——框架已经内置了完整的异常传播链路。


assertTrue vs assertFalse

ionet 的 ErrorCode 提供了两个断言方法:

// 条件为 true 时通过,为 false 时抛出异常
ErrorCode.goldNotEnough.assertTrue(user.getGold() >= price);

// 条件为 false 时通过,为 true 时抛出异常
ErrorCode.userBanned.assertFalse(user.isBanned());

选哪个取决于语义的自然性。比如:

  • "金币不足" → assertTrue(金币 >= 价格) 更自然
  • "用户被封禁" → assertFalse(被封禁) 更自然

嵌套调用中的异常

断言机制不仅在 Action 方法中有效,在 Action 调用的任何方法中都有效:

@ActionMethod(0)
private UserMessage login(LoginMessage message) {
    // 这个方法内部也可以抛出断言异常
    User user = userService.validateAndGetUser(message.jwt);
    return toUserMessage(user);
}

// 在 Service 中
public User validateAndGetUser(String jwt) {
    ErrorCode.jwtInvalid.assertTrue(jwt != null && !jwt.isEmpty());
    User user = findByJwt(jwt);
    ErrorCode.userNotFound.assertTrue(user != null);
    return user;
}

无论异常从哪一层抛出,框架都能正确捕获并返回给客户端。


对比传统方案

维度 传统 if-else ionet 断言
代码量 ~20 行防御代码 ~5 行断言
可读性 业务逻辑被淹没 业务逻辑突出
错误码管理 散落在各方法中 枚举集中管理
返回值封装 需要 Result<T> 直接 return 业务数据
异常传播 需手动处理 框架自动处理
客户端收到 可能不规范 总是规范的错误码

小结

ionet 的断言 + 异常机制,让你的业务代码回归业务本身。

  • 断言失败 = 自动返回错误码给客户端,不需要手动处理
  • 错误码枚举管理,不会散落在业务代码中
  • 方法签名纯粹,return 就是正常数据,不需要 Result 包装

这个机制是 ionet "零学习成本"的重要组成部分——让你不需要学习复杂的异常处理链路,也能写出健壮的业务代码。


更多资源

下一篇预告:[告别 if-else 地狱 —— JSR380 参数验证在 ionet 中的应用]

posted @ 2026-03-23 14:36  渔民小镇  阅读(10)  评论(0)    收藏  举报