欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识

改进代码可测 图纸加密 性的若干能力

点击: 次  来源:昆山软开发 时间:2018-01-11

原文出处: 琴水玉

概述

软件的工程性表此刻质量与效率。单测是组成软件质量的第一道防地,而单测包围率是软件质量的重要指标之一。 编写容易测试的代码,可带来更佳的单测包围率,间接晋升开拓效率。

为什么措施员不大写单测呢? 主要有如下原因:

  • 习惯于将细小的重要业务点反复性地稠浊在应用中。 功效是:难以对那些重要的业务点编写单测。
  • 习惯于编写“一泻千里”的大函数概略领。往往需要耗费至少1.5倍的力气去编写一段测试代码,合起来就是2.5倍的开拓量。基于工期紧要,又有几多人愿意艰辛不奉迎呢?
  • 习惯于编写耦合外部状态的要领。这是面向工具要领论的一个直接功效,可是也可以通过一个小能力来改进。
  • 习惯于将外部依赖耦合到要领中。这样就需要耗艰辛气去mock外部依赖以及一堆单调乏味的mock代码,同样会使单测难度增加和开拓量大增。
  • 针对上述环境,利用“代码语义化”、“疏散独立逻辑”、“疏散实例状态”、“表达与执行疏散”、“参数工具”、“疏散纯函数”、“面向接口编程”的能力,用于编写更容易测试的代码。

    能力

    代码语义化

    在工程中,经常多处看到雷同无语义的代码:

    if (state.equals(5)) {
        // code ....
    }

    这段代码有两个问题:(1) 无语义,易反复; (2) 容易引起 NPE。 state.equals(5) 是想表达什么业务语义呢? 在差异规模里,有差异的寄义。好比用于订单状态,可用于表达已付款。那么,代码里就应该明晰表达这一寄义,新建一个类 OrderStateUtil 及 isOrderPaid() ,把这段代码放进去;另外,假如 state = null,会引起 NPE,因此保险的写法是 Integer.valueOf(5).equals(state) 。 这段代码可写作:

    public class OrderStateUtil {
        public static isOrderPaid() {
            return Integer.valueOf(State.ISPAID).equals(state);
        }
    }

    这些,就可以对这段代码举办测试,而且多处安心引用。 像这样的代码,可称之“业务点”。 业务系统中布满着大量这样的细小的业务点。将业务点抽离出来,一则可以大量复用,二则可以任意组合, 就能制止系统重构时需要改多处的问题了。

    将纯真的业务点从要领中疏散出来。

    疏散独立逻辑

    独立逻辑是不依赖于任何外部处事依赖的业务逻辑或通用逻辑,切合“沟通输入运行任意次老是获得沟通输出”的函数模子。独立逻辑容易编写单测,然而许多开拓者却习惯把大段的独立逻辑放在一个大的流程要领里导致单测难写。来看这段放在流程要领里的代码:

    if(!OrderUtils.isNewOrderNo(param.getOrderNo())){
                deliveryParam.setItemIds(param.getItemIds().stream().map(itemId->itemId.intValue()).collect(Collectors.toList()));
            }else {
                deliveryParam.setItemIds(param
                        .getItemIds()
                        .stream()
                        .map(
                                x -> {
                                    if (orderItems.stream().anyMatch(orderItem -> x.equals(orderItem.getTcOrderItemId()))) {
                                        return orderItems
                                                .stream()
                                                .filter(orderItem -> x.equals(orderItem.getTcOrderItemId()))
                                                .map(orderItem -> orderItem.getId())
                                                .collect(Collectors.toList()).get(0);
                                    } else {
                                        return x.intValue();
                                    }
                                }
                        ).collect(Collectors.toList())
                );
            }

    这段代码本质上就是获取itemIds并配置参数工具,由于嵌入到要领中,导致难以单测,且增大地址要领的长度。另外,不须腹地利用stream的双重轮回,导致代码难以领略和维护。假如这段逻辑很是重要,将一段未测的逻辑放在逐日挪用百万次的接口里,那的确是存荣幸心理,犯兵家之忌。该当抽离出来,建设成一个纯函数:

    private List<Integer> getItemIds(DeliveryParamV2 param, List<OrderItem> orderItems) {
            if(!OrderUtils.isNewOrderNo(param.getOrderNo())){
                return StreamUtil.map(param.getItemIds(), Long::intValue);
            }
    
            Map<Long, Integer> itemIdMap = orderItems.stream().collect(
                                                     Collectors.toMap(OrderItem::getTcOrderItemId, OrderItem::getId));
            return StreamUtil.map(param.getItemIds(),
                                  itemId -> itemIdMap.getOrDefault(itemId, itemId.intValue()));
        }
    
    public class StreamUtil {
    
      public static <T,R> List<R> map(List<T> dataList, Function<T,R> getData) {
        if (dataList == null || dataList.isEmpty()) { return new ArrayList(); }
        return dataList.stream().map(getData).collect(Collectors.toList());
      }
    
    }