当前位置: 首页 > news >正文

张家港网站建设早晨设计公司网站建设会计上怎么处理

张家港网站建设早晨设计,公司网站建设会计上怎么处理,银川软件开发公司,子目录安装wordpress概述 软件的工程性体现在质量与效率。单测是构成软件质量的第一道防线#xff0c;而单测覆盖率是软件质量的重要指标之一。 编写容易测试的代码#xff0c;可带来更佳的单测覆盖率#xff0c;间接提升开发效率。 为什么程序员不大写单测呢#xff1f; 主要有如下原因#… 概述 软件的工程性体现在质量与效率。单测是构成软件质量的第一道防线而单测覆盖率是软件质量的重要指标之一。 编写容易测试的代码可带来更佳的单测覆盖率间接提升开发效率。 为什么程序员不大写单测呢 主要有如下原因 习惯于将细小的重要业务点重复性地混杂在应用中。 结果是难以对那些重要的业务点编写单测。习惯于编写“一泻千里”的大函数大方法。往往需要花费至少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);} } 这些就可以对这段代码进行测试并且多处放心引用。 像这样的代码可称之“业务点”。 业务系统中充满着大量这样的细小的业务点。将业务点抽离出来一则可以大量复用二则可以任意组合 就能避免系统重构时需要改多处的问题了。 将单纯的业务点从方法中分离出来。 分离独立逻辑 独立逻辑是不依赖于任何外部服务依赖的业务逻辑或通用逻辑符合“相同输入运行任意次总是得到相同输出”的函数模型。独立逻辑容易编写单测然而很多开发者却习惯把大段的独立逻辑放在一个大的流程方法里导致单测难写。来看这段放在流程方法里的代码 deliveryParam.setItemIds(param.getItemIds().stream().map(x - {if (orderItems.stream().anyMatch(orderItem - x.equals(orderItem.getNewItemId()))) {return orderItems.stream().filter(orderItem - x.equals(orderItem.getNewItemId())).map(orderItem - orderItem.getId()).collect(Collectors.toList()).get(0);} else {return x.intValue();}}).collect(Collectors.toList())); 这段代码本质上就是获取itemIds并设置参数对象由于嵌入到方法中导致难以单测且增大所在方法的长度。此外不必要地使用stream的双重循环导致代码难以理解和维护。如果这段逻辑非常重要将一段未测的逻辑放在每日调用百万次的接口里那简直是存侥幸心理犯兵家之忌。应当抽离出来创建成一个纯函数 private ListInteger getItemIds(DeliveryParamV2 param, ListOrderItem orderItems) {MapLong, Integer itemIdMap orderItems.stream().collect(Collectors.toMap(OrderItem::getNewItemId, OrderItem::getId));return StreamUtil.map(param.getItemIds(), itemId - itemIdMap.getOrDefault(itemId, itemId.intValue())); }public class StreamUtil {public static T,R ListR map(ListT dataList, FunctionT,R getData) {if (dataList null || dataList.isEmpty()) { return new ArrayList(); }return dataList.stream().map(getData).collect(Collectors.toList());}}getItemIds 是纯函数容易编写单测而原来的一段代码转化为一行调用 deliveryParam.setItemIds(getItemIds(param, orderItems)); 缩短了业务方法的长度。这里封装了一个更安全的 StreamUtil.map 是为了防止NPE。 将独立逻辑和通用逻辑从方法流程中分离出来。 分离实例状态 在博文 “使用Java函数接口及lambda表达式隔离和模拟外部依赖更容易滴单测” 的隔离依赖配置实际上已经给出了一个例子。 开发人员习惯于将类的实例变量在类方法中直接引用而这样做的后果就是破坏了方法的通用性和纯粹性。改进的方法其实很简单编写一个纯函数将实例变量或实例对象作为参数传入然后编写一个“外壳函数”调用这个函数实现功能。这样既能保证对于外部一致的访问接口又能保证内部实现的通用性和纯粹性且更容易单测。 分离外部服务调用 现在我们进入正题。 一环扣一环的外部服务调用正是使单测编写变得困难的主要因素。 在 “使用Java函数接口及lambda表达式隔离和模拟外部依赖更容易滴单测” 一文已经初步探讨了如何使用函数接口及lambda表达式来隔离和模拟外部依赖增强代码可测性。不过不彻底。 如果一个方法里含有多个外部服务调用怎么办 如果方法A调用BB调用CC调用DD依赖了外部服务怎么让 A,B,C,D更加容易测试 如何可配置化地调用外部服务而让类的大部分方法保持函数纯粹性而容易单测少部分方法则承担外部服务调用的职责指导思想是 通过函数接口隔离外部服务依赖分离出真正可单测的部分 。真正可单测的部分往往是条件性、循环性的不含服务调用依赖的业务性逻辑而顺序的含服务调用依赖的流程性逻辑应当通过接口测试用例来验证。 表达与执行分离 表达通常是声明式的无状态的执行通常是命令式的有状态且依赖外部环境的。 表达与执行分离可将状态与依赖分离出来从而对表达本身进行单测。来看一段代码 public BizComponent getBizComponentInstance(BizContext BizContext, BizParam params) {if (ACondition1) {LogUtils.info(log, AComponent for {}, params);return (BizComponent) applicationContext.getBean(AComponent);}if(BCondition2){LogUtils.info(log, BComponent for {}, params);return (BizComponent) applicationContext.getBean(BComponent);}if (ECondition) {LogUtils.info(log, EComponent for {}, params);return (BizComponent) applicationContext.getBean(EComponent);}LogUtils.info(log, normalComponent for {}, params);return (BizComponent) applicationContext.getBean(normalComponent);} 这段代码根据不同条件获取对应的发货子组件。 可见代码要完成两个子功能 (1) 根据不同条件判断需要何种组件 (2) 获取相应组件并打印必要日志。 (1) 是表达真正值得测试的部分 (2) 是执行通过接口测试即可验证 而代码将(1)与(2) 混杂到一起从而使得编写整个单测难度变大了因为要mock applicationContext还需要注入外部变量 log 。 可以将(1) 抽离出来只返回要发货组件标识更容易单测而(2) 则使用多种方式实现。如下代码所示 public BizComponent getBizComponentInstanceBetter(BizContext bizContext, BizParam params) {return getActualComponentInstance(getBizComponentID(bizContext, params).name(), params);}public ComponentEnum getBizComponentID(BizContext BizContext, BizParam params) {if (ACondition1) {return AComponent;}if(BCondition2){return BComponent;}if (ECondition) {return EComponent;}return NormalComponent;}public BizComponent getActualComponentInstance(String componentName, BizParam params) {LogUtils.info(log, component {} for {}, componentName, params);return (BizComponent) applicationContext.getBean(componentName);}public enum BizComponentEnum {NormalComponent, AComponent, BComponent, EComponent} 虽然多出了两个方法但是只有 getBizComponentID 方法是最核心的最需要单测的并且是无状态不依赖外部环境的很容易编写单测只需要测试各种条件即可。这里定义了 BizComponentEnum 是为了规范发货组件的名称仅限于指定的若干种防止拼写错误。 识别业务逻辑中的表达与执行将表达部分分离出来。 分离纯函数 看下面这段代码 /*** 根据指定rowkey列表及指定列族、列集合获取Hbase数据* param tableName hbase表名* param rowKeyList rowkey列表* param cfName 列族* param columns 列名* param allowNull 是否允许值为null,通常针对rowkey* return hbase 数据集* throws Exception 获取数据集失败时抛出异常*/public ListResult getRows(String tableName, ListString rowKeyList,String cfName, ListString columns,boolean allowNull) throws Exception {HTable table getHtable(tableName);final String cf (cfName null) ? cf : cfName;ListGet gets rowKeyList.stream().map(rowKey - {String rowKeyNotEmpty (rowKey null ? null : rowKey);Get get new Get(Bytes.toBytes(rowKeyNotEmpty));if (columns ! null !columns.isEmpty()) {for (String col: columns) {get.addColumn(Bytes.toBytes(cf), Bytes.toBytes(col));}}return get;}).collect(Collectors.toList());Result[] results table.get(gets);logger.info(Got {} results from hbase table {}. cf: {}, columns: {}, results.length, tableName, cf, columns);ListResult rsList new ArrayList();for (int i 0; i rowKeyList.size(); i) {if (!allowNull isResultEmpty(results[i])) {logger.warn(cantt get record for rowkey:{}, rowKeyList.get(i));continue;}rsList.add(results[i]);}logger.info(got {} rows from table {} with {} rowkeys, rsList.size(), tableName, rowKeyList.size());return rsList;} 这段代码有大部分代码惯有的毛病多个逻辑混杂在一起大量条件性的业务逻辑中间藏有一小段外部依赖的调用HTable table getHtable(tableName); Result[] results table.get(gets); 访问 Hbase数据源而这一小段外部依赖使得整个方法的单测编写变得麻烦了。 在 “使用Java函数接口及lambda表达式隔离和模拟外部依赖更容易滴单测” 一文中已经指出只要使用一个 BiFunction 来模拟 Result[] results table.get(gets); 这段调用即可使得 getRows 整个方法变成纯函数。 不过这个方法已经有好几个参数了再增加一个参数会比较难看。可以应用参数对象模式将多个紧密关联的原子参数聚合为一个参数对象。注意到 htableName,rowkeyList, cf, columns, allowNull 确实是从Hbase获取数据所需要的紧密关联的参数聚合因此适合参数对象模式。重构后代码如下所示 public ListResult getRows(String tableName, ListString rowKeyList,String cfName, ListString columns,boolean allowNull) throws Exception {return getRows(new HbaseFetchParamObject(tableName, rowKeyList, cfName, columns, allowNull),this::getFromHbase);}private Result[] getFromHbase(String tableName, ListGet gets) {try {HTable table getHtable(tableName);return table.get(gets);} catch (Exception ex) {logger.error(ex.getMessage(), ex);throw new RuntimeException(ex);}}public ListResult getRows(HbaseFetchParamObject hbaseFetchParamObject,BiFunctionString, ListGet, Result[] getFromHbaseFunc) throws Exception {String tableName hbaseFetchParamObject.getTableName();String cfName hbaseFetchParamObject.getCfName();ListString rowKeyList hbaseFetchParamObject.getRowKeyList();ListString columns hbaseFetchParamObject.getColumns();boolean allowNull hbaseFetchParamObject.isAllowNull();String cf (cfName null) ? cf : cfName;ListGet gets buildGets(rowKeyList, cf, columns);Result[] results getFromHbaseFunc.apply(tableName, gets);logger.info(Got {} results from hbase table {}. cf: {}, columns: {}, results.length, tableName, cf, columns);ListResult rsList buildResult(rowKeyList, results, allowNull);logger.info(got {} rows from table {} with {} rowkeys, rsList.size(), tableName, rowKeyList.size());return rsList;}private ListGet buildGets(ListString rowKeyList, String cf, ListString columns) {return StreamUtil.map(rowKeyList,rowKey - {String rowKeyNotEmpty (rowKey null ? null : rowKey);Get get new Get(Bytes.toBytes(rowKeyNotEmpty));if (columns ! null !columns.isEmpty()) {for (String col: columns) {get.addColumn(Bytes.toBytes(cf), Bytes.toBytes(col));}}return get;});}private ListResult buildResult(ListString rowKeyList, Result[] results, boolean allowNull) {ListResult rsList new ArrayList();for (int i 0; i rowKeyList.size(); i) {if (!allowNull isResultEmpty(results[i])) {logger.warn(cantt get record for rowkey:{}, rowKeyList.get(i));continue;}rsList.add(results[i]);}return rsList;} 重构后的代码中(tableName, rowKeyList, cfName, columns, allowNull) 这些原子性参数都聚合到参数对象 hbaseFetchParamObject 中大幅减少了方法参数个数。现在getRows(hbaseFetchParamObject, getFromHbaseFunc) 这个从Hbase获取数据的核心函数变成无依赖外部的纯函数了可以更容易滴单测而原来的方法则变成了一个接口不变的外壳供外部调用。 这说明了 任何一个依赖外部服务的非纯函数总可以分为一个不依赖外部服务的具备核心逻辑的纯函数和一个调用外部服务的壳函数。而单测正是针对这个具备核心逻辑的纯函数。 此外将构建 gets 和 results 的逻辑分离出来使得 getRows 流程更加清晰。现在 getRows(hbaseFetchParamObject, getFromHbaseFunc) , buildGets, buildResult 都是纯函数对三者编写单测后对从Hbase获取数据的基础函数的质量会更加自信了。 只要方法中的调用服务调用不多于2个不包括调用方法中的服务依赖都可以采用这种方法来解决单测的问题。 使用函数接口将外部依赖隔离。 代码模式 纵观业务系统里的代码主要原子代码模式主要有五种 构建参数判断条件是否满足组装数据调用服务查询数据调用服务执行操作前三者是可单测的后两者是不可测的。而代码常常将前三者和后两者混杂在一起必须想办法将其分离开。 依赖于外部服务的代码模式主要有如下五种 构建参数 - 判断条件满足后调用服务查询数据 - 判断逻辑或组装数据构建参数 - 判断条件满足后调用服务执行操作 - 判断逻辑或组装数据构建参数 - 判断条件满足后调用服务查询数据 - 判断逻辑或组装数据 - 判断条件满足后调用服务执行操作 - 判断逻辑或组装数据构建参数 - 判断条件满足后调用服务执行操作 - 判断逻辑或组装数据 - 判断条件满足后调用服务查询数据 - 判断逻辑或组装数据以上的任意可能的组合。一般前四种都可以采用函数接口的方式来解耦外部依赖。 面向接口编程 面向接口编程有两层含义类级别面向接口编程 方法级别面向函数接口编程。 当要编写单测时很容易编写接口的mock类或lambda表达式。 比如 A 对象依赖 B 对象里的 M 方法而 M 方法会从数据库里读取数据。那么 A 就不要直接依赖 B 的实体类而引用 B 的接口。 当对 A 编写单测时只要注入 B 的 mock 实现即可。 同理方法中含有 service 调用时不要直接依赖 service 调用而是依赖函数接口在函数接口中传递 service 调用如上面的做法。这样编写单测时只要传入 lambda 表达式返回mock数据即可。 假设有 m1, m2, m3 方法m1调用m2, m2调用m3, m1, m2 都是纯函数, m3 会调用外部服务依赖。由于 m3 不纯以及调用关系导致 m1, m2 也不纯。解耦的方法是面向函数接口编程。 m3 不依赖于外部服务而是依赖函数接口。在 m3 的参数中提供一个函数接口m1, m2 传入一个 lambda 表达式。如果 m1, m2 也有很多业务逻辑要测试那么 m1, m2 也提供相同的函数接口传入服务依赖直到某一层只是一层“壳函数”。 这样含有业务逻辑的方法都可以方便地单测而且更容易理解函数接口表达了需要什么外部依赖 而壳函数不需要单测。 当然这需要对编程方式和习惯的一种改变而目前大部分编程习惯就是直接在方法里调用service看上去直观却会导致方法耦合了外部依赖难以单测。 小结 良好的编程习惯会带来可测性更佳的代码对软件的质量和开发效率都有积极影响。代码语义化、分离通用逻辑、将实例状态放在参数中、参数对象、面向接口编程等都是一些小的技巧和做法结合起来使用就能让代码表达更加容易理解和维护而函数编程则可以解耦外部服务依赖分离出容易测试的具有核心业务逻辑的纯函数。 面向对象/函数式编程是非常强大的混合编程范式。面向对象提供了贴近现实的自然的表达方法为应用系统提供一个优秀的外部视角 而函数编程则着重于内部结构优化可以让内部实现解耦得更加清晰。 两者是相辅相成的而非对立的。 转载于:https://www.cnblogs.com/lovesqcc/p/7898319.html
http://www.yutouwan.com/news/11764/

相关文章:

  • 深圳华鑫峰网站建设中国网站开发的前景
  • 北京大学两学一做网站怎么写网站
  • 旅游网官方网站郑州网站推广价
  • 网站更换主机需要怎么做基层建设期刊在哪个网站被收录
  • 濮阳团购网站建设搜索引擎广告收费方式
  • 深圳网站制作公司资讯建设多语种网站
  • 网站建设价格标准方案高端网站建设公司兴田德润可以不
  • 公司企业网站免费建设三合一网站开发架构
  • 那些网站能够做推广案例学 网页设计与网站建设
  • 网站设计初步规划建筑业企业资质证书多少钱
  • 龙岩网站改版较好的公司张店网站制作首选专家
  • 微信网站设计尺寸百度推广助手客户端
  • 闽清建设局网站做一个电子商务网站建设策划书
  • 做漫画网站的素材织梦网站如何更新系统
  • 南阳旅游网站建设电商外贸平台大全
  • 投简历找工作哪个网站好乐从网站建设
  • 自己做的网站能备案吗用.net做网站好 还是用php
  • 南昌哪里学做网站dw网页制作详细步骤景颇族
  • php招聘网站建设网站风格介绍
  • 创世网站网络建设互联网网站建设一条龙服务
  • 网站常用英文字体动态公司网站设计
  • 网站头尾一样的怎么做最好公众号软文推广
  • 网站建设需要多久wordpress woocommerce 单位
  • 网站开发和维护360广告联盟平台
  • 苏州专业网站建设公司建网站得多少钱
  • 深圳网站设计廊坊公司上海中高风险地区名单
  • 网站里网格怎么做装饰设计公司资质
  • 天津市设计网站的公司那个网站可以帮助做数学题
  • 吴桥网站建设游戏seo推广
  • 做ps兼职的网站一个云主机怎么挂两个网站