由「抽象一致性」思考如何书写可读性代码

java 4 2016-02-29 13:03
女装

由「抽象一致性」思考如何书写可读性代码

场景

  1. 用户实体User

  2. 通过用户Id查找用户的订单信息接口List<Order> orders = orderService.queryOrders(long userId)

  3. 通过用户Id查找用户退款订单Id列表List<String> orderIds = orderRefundService.queryRefundOrderIds(long userId)

  4. 查找所有用户的信息List<User> users = UserService.queryAllUsers()

  5. 要求查询用户订单消费总金额(除去退过款的订单)

实现一

public List<User> queryUserStatistics(){
    List<User> users = UserService.queryAllUsers()
    for (User user : users) {
        long userId = user.getAccountId();
        List<Order> orders = orderService.queryOrderInfo();
        BigDecimal totlePayAmount = filterRefundOrderAndCalculateAmount(orders,accountId);
        user.setSumPayAmount(totlePayAmount);
    }
}
private BigDecimal filterRefundOrderAndCalculateAmount(List<Order> orders, long     userId) {
    List<String> orderIds = orderRefundService.queryRefundOrderIds(userId);
    BigDecimal totlePayAmount = BigDecimal.ZERO;
    for (Order order : orders) {
        if (!orderIds.contains(order.getOrderId())) {
            totlePayAmount = totlePayAmount.add(order.getPayAmount());
        }
    }
    return totlePayAmount;
}

实现二

public List<User> queryUserStatistics(){
    List<User> users = UserService.queryAllUsers()
    for(User user : users){
         BigDecimal sumPayAmount = calculateTotleConsumeAmount(user);
         user.setSumPayAmount(sumPayAmount);
    }
}
private BigDecimal calculateTotleConsumeAmount(User user) {
    long userId = user.getUserId();
    List<Order> orders = orderService.queryOrderInfo(userId);
    return filterRefundOrderAndCalculateAmount(orders, userId);
}

private BigDecimal filterRefundOrderAndCalculateAmount(List<Order> orders, long     userId) {
    List<String> orderIds = orderRefundService.queryRefundOrderIds(userId);
    BigDecimal totlePayAmount = BigDecimal.ZERO;
    for (Order order : orders) {
        if (!orderIds.contains(order.getOrderId())) {
            totlePayAmount = totlePayAmount.add(order.getPayAmount());
        }
    }
    return totlePayAmount;
}

实现三

public List<User> queryUserStatistics(){
    List<User> users = UserService.queryAllUsers()
    for(User user : users){
         BigDecimal sumPayAmount = calculateTotleConsumeAmount(user);
         user.setSumPayAmount(sumPayAmount);
    }
}
private BigDecimal calculateTotleConsumeAmount(User user) {
    long userId = user.getUserId();
    List<Order> orders = orderService.queryOrderInfo(userId);
    List<String> orderIds = orderRefundService.queryRefundOrderIds(long userId);
    return filterRefundOrderAndCalculateAmount(orderPayAmountVOs, orderIds);
}

private BigDecimal filterRefundOrderAndCalculateAmount(List<Order> orders,List<String>         orderIds) {
    BigDecimal is = BigDecimal.ZERO;
    for (Order order : orders) {
        if (!orderIds.contains(order.getOrderId())) {
            totlePayAmount = totlePayAmount.add(order.getPayAmount());
        }
    }
    return totlePayAmount;
}

实现一:接到一个需求,完成工作,未仔细推敲代码的合理性

实现二:接到另外一个需求,需要复用实现一的部分代码,所以重新组织了下代码

实现三:仔细看了下实现二,觉得很别扭,深层次的思考了下代码的结构,发现实现二并没有满足现在已知需求的「抽象一致性」

说明一

对于实现二
queryUserStatistics()的抽象层次,我们可以这样定义:

查找用户的订单消费总额和用户信息
  1. 查找用户信息

  2. 计算订单消费金额

计算订单消费金额
  1. 查找所有的订单信息

  2. 查找退款信息

  3. 过滤退款信息,计算消费金额

或者

  1. 查找所有的订单

  2. 过滤退款订单

  3. 计算消费金额

过滤退款订单
  1. 查找退款订单

  2. 过滤

结论一:实现二在已知的需求里面其实是不是非常满足「抽象一致性的」

结论二仔细推敲下实现三,其实这个方法中filterRefundOrderAndCalculateAmount还是不是非常满足「抽象一致性」的要求的。

说明二
从另外一个角度来看实现二

计算订单消费金额
  1. 查找所有的订单信息

  2. 过滤退款并计算消费金额

过滤退款并计算消费金额
  1. 查找退款信息

  2. 过滤退款信息

  3. 计算消费金额

如果这样子理解,也是可以说实现二也满足「抽象一致性」的。
结论 从不同的角度去看「抽象一致性」得出的结论是不一样的,只有最符合现有业务的,没有最正确的

说明三
对于实现一,queryUserStatistics方法是查询用户的消费信息,而这里的详细计算订单金额直接写在这个方法里面,抽象层次十分混乱,所以实现一是不推荐的。

总结

  1. 在编写代码的时候,尽量让方法或者类保持「抽象一致性」,这里说尽量的意思就是说明在很多时间可以违反这个约定(因为有的时候会觉得很没有必要,保持抽象一致性,就代表着你的方法里面可能需要很多私有方法,并且在极端情况下私有方法里面的代码数很少)

  2. 「抽象一致性」从不同的角度看,可能会有不同的结果,所以尽量选一个更加符合当前业务的角度(比如说实现一和实现二)

PS:关于抽象一致性更加详细的描述,可以翻看我以前的提问对抽象层次一致性的看法

女装
文章评论