如何消除for循环中的重复代码?

时间:2015-05-13 12:48:36

标签: java dry

我在同一个类中实现了两个成员函数:

  private static void getRequiredTag(Context context) throws IOException
  {
    //repeated begin
    for (Record record : context.getContext().readCacheTable("subscribe")) {
      String traceId = record.get("trace_id").toString();
      if (traceSet.contains(traceId) == false)
        continue;
      String tagId = record.get("tag_id").toString();
      try {
        Integer.parseInt(tagId);
      } catch (NumberFormatException e) {
        context.getCounter("Error", "tag_id not a number").increment(1);
        continue;
      }
      //repeated end
      tagSet.add(tagId);
    }
  }

  private static void addTagToTraceId(Context context) throws IOException
  {
    //repeated begin
    for (Record record : context.getContext().readCacheTable("subscribe")) {
      String traceId = record.get("trace_id").toString();
      if (traceSet.contains(traceId) == false)
        continue;
      String tagId = record.get("tag_id").toString();
      try {
        Integer.parseInt(tagId);
      } catch (NumberFormatException e) {
        context.getCounter("Error", "tag_id not a number").increment(1);
        continue;
      }
      //repeated end
      Vector<String> ret = traceListMap.get(tagId);
      if (ret == null) {
        ret = new Vector<String>();
      }
      ret.add(traceId);
      traceListMap.put(tagId, ret);
    }    
  }

我将在另外两个成员函数中调用这两个成员函数(因此我无法将它们合并为一个函数):

private static void A()
{
  getRequiredTag()
}
private static void B()
{
  getRequiredTag()
  addTagToTraceId()
}

tagSetjava.util.SettraceListMapjava.util.Map

我知道DRY principle我真的想要消除重复代码,所以我来看看这段代码:

  private static void getTraceIdAndTagIdFromRecord(Record record, String traceId, String tagId) throws IOException
  {
    traceId = record.get("trace_id").toString();
    tagId = record.get("tag_id").toString();
  }

  private static boolean checkTagIdIsNumber(String tagId)
  {
    try {
      Integer.parseInt(tagId);
    } catch (NumberFormatException e) {
      return false;
    }
    return true;
  }

  private static void getRequiredTag(Context context) throws IOException
  {
    String traceId = null, tagId = null;
    for (Record record : context.getContext().readCacheTable("subscribe")) {
      getTraceIdAndTagIdFromRecord(record, traceId, tagId);
      if (traceSet.contains(traceId) == false)
        continue;
      if (!checkTagIdIsNumber(tagId))
      {
        context.getCounter("Error", "tag_id not a number").increment(1);
        continue;
      }
      tagSet.add(tagId);
    }
  }

  private static void addTagToTraceId(Context context) throws IOException
  {
    String traceId = null, tagId = null;
    for (Record record : context.getContext().readCacheTable("subscribe")) {
      getTraceIdAndTagIdFromRecord(record, traceId, tagId);
      if (traceSet.contains(traceId) == false)
        continue;
      if (!checkTagIdIsNumber(tagId))
      {
        context.getCounter("Error", "tag_id not a number").increment(1);
        continue;
      }
      Vector<String> ret = traceListMap.get(tagId);
      if (ret == null) {
        ret = new Vector<String>();
      }
      ret.add(traceId);
      traceListMap.put(tagId, ret);
    }    
  }

似乎我有了一个新的重复......我不知道在那种情况下消除重复,有人可以给我一些建议吗?

更新2015-5-13 21:15:12:

有些人给出了一个布尔参数来消除重复,但我知道 Robert C. Martin's Clean Code Tip #12: Eliminate Boolean Arguments.(您可以谷歌查询更多详情)。

你能否对此发表评论?

4 个答案:

答案 0 :(得分:2)

更改的部分需要String tagIdString traceId的值,因此我们将从提取带有这些参数的接口开始:

public static class PerformingInterface {
     void accept(String tagId, String traceId);
}

然后将常用部分提取到此方法中:

  private static void doSomething(Context context, PerformingInterface perform) throws IOException
  {
    String traceId = null, tagId = null;
    for (Record record : context.getContext().readCacheTable("subscribe")) {
      getTraceIdAndTagIdFromRecord(record, traceId, tagId);
      if (traceSet.contains(traceId) == false)
        continue;
      if (!checkTagIdIsNumber(tagId))
      {
        context.getCounter("Error", "tag_id not a number").increment(1);
        continue;
      }
      perform.accept(tagId, traceId);
    }    
  }

然后以两种不同的方式调用此方法:

private static void getRequiredTag(Context context) throws IOException {
    doSomething(context, new PerformingInterface() {
         @Override public void accept(String tagId, String traceId) {
              tagSet.add(tagId);
         }
    });
}

private static void addTagToTraceId(Context context) throws IOException {
    doSomething(context, new PerformingInterface() {
         @Override public void accept(String tagId, String traceId) {
             Vector<String> ret = traceListMap.get(tagId);
             if (ret == null) {
                 ret = new Vector<String>();
             }
             ret.add(traceId);
             traceListMap.put(tagId, ret);
         }
    });
}

请注意,我在这里使用lambdas,这是一个Java 8特性(BiConsumer也是Java 8中定义的功能接口),但完全有可能在Java 7中完成同样的事情,它只需要一些更冗长的代码。

您的代码存在其他一些问题:

  • 太多的事情是static
  • Vector类已旧,建议使用ArrayList(如果需要同步,请将其包装在Collections.synchronizedList中)
  • 始终使用大括号,即使是单行

答案 1 :(得分:1)

第二次尝试时,

tagId似乎总是为空。

然而,一种方法是将收集tagIds的代码(在两种方法中似乎相同)提取到自己的方法中。然后,在两个方法中的每个方法中,只迭代返回的tagIds集合并对它们执行不同的操作。

for (String tagId : getTagIds(context)) {
// do method specific logic
}

修改

现在我注意到你在第二种方法中也使用了traceId。原理保持不变,只需在单独的方法中收集记录,并在两种方法中迭代它们(通过记录中的tagId和traceId)。

使用lambdas的解决方案是最优雅的解决方案,但没有它们会涉及创建单独的接口和两个匿名类,这对于这个用例来说过于冗长(老实说,我宁愿使用布尔参数而不是没有策略lambda表达式)。

答案 2 :(得分:1)

你可以使用一个流(没有经过测试):

private static Stream<Record> validRecords(Context context) throws IOException {
    return context.getContext().readCacheTable("subscribe").stream()
        .filter(r -> {
            if (!traceSet.contains(traceId(r))) {
                return false;
            }
            try {
                Integer.parseInt(tagId(r));
                return true;
            } catch (NumberFormatException e) {
                context.getCounter("Error", "tag_id not a number").increment(1);
                return false;
            }
        });
}

private static String traceId(Record record) {
    return record.get("trace_id").toString();
}

private static String tagId(Record record) {
    return record.get("tag_id").toString();
}

然后可以做到:

private static void getRequiredTag(Context context) throws IOException {
    validRecords(context).map(r -> tagId(r)).forEach(tagSet::add);
}

private static void addTagToTraceId(Context context) throws IOException {
    validRecords(context).forEach(r -> {
        String tagId = tagId(r);
        Vector<String> ret = traceListMap.get(tagId);
        if (ret == null) {
            ret = new Vector<String>();
        }
        ret.add(traceId(r));
        traceListMap.put(tagId, ret);
    });
}

答案 3 :(得分:-1)

尝试这种方法

private static void imYourNewMethod(Context context,Boolean isAddTag){
        String traceId = null, tagId = null;
        for (Record record : context.getContext().readCacheTable("subscribe")) {
          getTraceIdAndTagIdFromRecord(record, traceId, tagId);
          if (traceSet.contains(traceId) == false)
            continue;
          if (!checkTagIdIsNumber(tagId))
          {
            context.getCounter("Error", "tag_id not a number").increment(1);
            continue;
          }
        if(isAddTag){
            Vector<String> ret = traceListMap.get(tagId);
              if (ret == null) {
                ret = new Vector<String>();
              }
          ret.add(traceId);
          traceListMap.put(tagId, ret);
        }else{
            tagSet.add(tagId);
        }
    }

调用此方法并传递一个参数boolean true,如果你想添加其他false来获取它。