• 重构日记一——flatMap与builder模式


       最近在做一个老系统的优化工作,由于里面的代码实在惨不忍睹,所以在做业务逻辑梳理及系统优化的同时,也开始做起了重构工作。由于没有充足的时间去做重新设计,毕竟在阿里白天要和沙雕产品撕逼,晚上要做正事,留给你优化系统的时间基本上少之又少,因此只能先从一些点开始,逐步进行,由点到面,记录下近期的重构工作。废话不多说,进入正题。

       厂里有好多系统都是跑了好多年了,很多代码如果没问题就不会有人来重构,像这样的JDK1.6前的代码,在我们日常的老系统中几乎随地可见,冗长恶心,极易重复,IDEA到处飘黄,为了这样的for循环去专门写个方法或工具类做抽象又小题大做,而且也比较难复用。这段代码的目的是从reasons这样一个数组里,取出所有reasonId的列表并去重,我们先把几个类型定义列出来,不必要的代码先省略了。

    1. 用flatMap化解嵌套循环

    1         Set<Long> reasonIds = Sets.newHashSet();
    2         for (RejectItemReasonDO rejectDO : reasons) {
    3             List<RejectMapDO> reasonMap = rejectDO.getRejectMapList();
    4             if (CollectionUtils.isNotEmpty(reasonMap)) {
    5                 for (RejectMapDO rejectInfo : reasonMap) {
    6                     reasonIds.addAll(rejectInfo.getRejectIds());
    7                 }
    8             }
    9         }

        reasons是一个如下类型定义的列表:

     1 public class RejectItemReasonDO extends AbstractBaseDO {
     2 
     3     private static final long serialVersionUID = -111111111L;
     4 
     5     @Setter
     6     @Getter
     7     private List<RejectMapDO> rejectMapList    = Lists.newArrayList();
     8 
     9     ...
    10 }
    RejectMapDO 定义
     1 @Data
     2 public class RejectMapDO extends BaseDO {
     3 
     4     private static final long serialVersionUID = -181828238283L;
     5 
     6     /**
     7      * 原因ID
     8      */
     9     private Set<Long>         rejectIds        = Sets.newHashSet();
    10     
    11     ...
    12 }

       让我们用stream和flatMap改写一下,在这里用flapMap将数据结构中两层的list,化解嵌套循环,代码一下子就干净了很多。

    1 Set<Long> reasonIds = rejectItemReasonDOS.stream().flatMap(rejectItemReasonDO -> rejectItemReasonDO.getRejectMapList()
    2                 .stream()).filter(Objects::nonNull).flatMap(rejectMapDO -> rejectMapDO.getRejectIds()
    3                 .stream()).filter(Objects::nonNull).collect(Collectors.toSet());

       

        有没有发现有什么问题?

        这里加了两个 .filter(Objects::nonNull) 过滤空元素而原代码中似乎只有一个,为啥要加两个? 原代码中使用的是 reasonIds.addAll(rejectInfo.getRejectIds()),list的addAll方法如果碰到空元素是会抛异常的,我们不希望这样,因此在重构中顺带修复了一个可能的bug,增加代码的健壮性,当然你也可以说我的数据来源保证了不会为空,那也可以,但是在实际代码的编写中,原则上是不能相信他人和数据的,你懂的。

     2. 用builder模式改写复杂参数构造方式

      我们的代码中有许多对象成员变量较多,在构建参数的时候,往往会有许多get/set操作,极为丑陋。这个时候我们想到了builder模式,builder的主要功能就是用来构建复杂对象,分离对象的表示和实现,从而让代码更整洁。示例代码如下,在需要构建的复杂对象中,创建一个static的builder,或者专门为这个复杂对象创建一个builder类,通过builder的操作封装参数类的操作。

          参数类及builder定义:

     1 @Data
     2 public class GlobalPublishAuditContext implements Serializable {
     3 
     4     private static final long serialVersionUID = 7663807161349892L;
     5 
     6     private Long productId;
     7 
     8     private Long sellerId;
     9 
    10     private String operator;
    11 
    12     private Boolean isPublish;
    13 
    14     private String  source;
    15 
    16     private Integer newItemStatus;
    17  
    18     private IqcExpand newIqcExpand;
    19 
    20     private Integer newItemSubStatus;
    21  
    22     private ProductForAuditDTO productAuditDTO;
    23 
    24     private Map<String, SkuForAuditDTO> skuMap;
    25 
    26     private Boolean needSendMTeeAuditMsg;
    27 
    28     private Boolean needSendScmAuditMsg;
    29 
    30     private Boolean needDoPostApproveIfQcSkip;
    31 
    32     private Boolean needManualCheck;
    33 
    34     private Boolean isImageEdit;
    35 
    36     private Boolean isFirstActive;
    37     /**
    38      *
    39      */
    40     private Map<String, String> extension;
    41 
    42     /**
    43      * new builder
    44      * @return
    45      */
    46     public static GlobalPublishAuditContextBuilder builder(){
    47         return new GlobalPublishAuditContextBuilder();
    48     }
    49 
    50     /**
    51      * builder for GlobalPublishAuditContext
    52      */
    53     public static class GlobalPublishAuditContextBuilder {
    54 
    55         private GlobalPublishAuditContext context;
    56 
    57         private ProductForAuditDTO productForAuditDTO;
    58 
    59         private IqcExpand newIqcExpand;
    60 
    61         private Map<String, String> extension;
    62 
    63         private Map<String, SkuForAuditDTO> skuMap;
    64 
    65 
    66         public GlobalPublishAuditContextBuilder() {
    67             context = new GlobalPublishAuditContext();
    68         }
    69     
    70        ...
    71 ...
    72 }

      使用Demo如下,是不是比各种get/set操作清爽多了。好了,先写到这里,清理一波烂代码后继续。

    1 GlobalPublishAuditContext productAuditContext = GlobalPublishAuditContext.builder()
    2                 .isPublish(request.isPublish())
    3                 .sellerId(request.getSellerId())
    4                 .productId(request.getProductId())
    5                 .needManualCheck(request.isNeedManualCheck())
    6                 .newItemSubStatus(ItemAuditStatus.APPROVED.getValue()).build();
  • 相关阅读:
    JS正则表达式
    JS验证电话号是否合法
    特性Attribute 的使用
    三层架构(面向对象思想)
    oracle 游标的使用
    oracle中的net manager 无法配置
    .net学习网站汇总
    每天进步一点点之后缀表达式求值
    每天进步一点点之中缀表达式转后缀表达式
    下载Android代码
  • 原文地址:https://www.cnblogs.com/XiaoHDeBlog/p/12900863.html
Copyright © 2020-2023  润新知