如何重构重复的代码?

时间:2015-01-16 10:49:21

标签: java refactoring duplicates

我知道副本有气味

但是如何重构代码?

public List<HighWay> updateAllNewHighWays(HighWayRepository repository)
            throws IOException {
        List<HighWay> highWays = new ArrayList<HighWay>();
        for (RoadCode code : RoadCode.values()) {
            try {
                pageParam.setRoadName(code);
                highWays.addAll(getAndSaveNewHighWay(repository));
            } catch (IOException e) {
                IOException exception = dealException(e, code);
                throw exception;
            }
        }
        return highWays;
    }

    public List<HighWay> getAllNewHighWays(HighWayRepository repository)
            throws IOException {
        List<HighWay> highWays = new ArrayList<HighWay>();
        for (RoadCode code : RoadCode.values()) {
            try {
                pageParam.setRoadName(code);
                highWays.addAll(getNewHighWay(repository));
            } catch (IOException e) {
                IOException exception = dealException(e, code);
                throw exception;
            }
        }
        return highWays;
    }

2 个答案:

答案 0 :(得分:1)

由于唯一改变的部分是循环的内部,你可以重构循环部分任何只有循环内的部分改变。

如果您使用Java 8,则可以将getAndSaveNewHighWay(repository)getNewHighWay(repository)方法作为方法参考传递为Function<HighWayRepository, List<HighWay>>实现

public List<HighWay> handleHighways(HighWayRepository repository, Function<HighWayRepository, List<HighWay>> function){
  List<HighWay> highWays = new ArrayList<HighWay>();
        for (RoadCode code : RoadCode.values()) {
            try {

                pageParam.setRoadName(code);
                //call our method 
                highWays.addAll(function.apply(repository));
            } catch (IOException e) {
                IOException exception = dealException(e, code);
                throw exception;
            }
        }
        return highWays;
}

然后在你的主叫代码中:

 List<HighWay> highways = handleHighways(repository, MyClass::getAndSaveNewHighWay);

List<HighWay> highways = handleHighways(repository, MyClass::getNewHighWay);

如果没有Java 8,您可以通过创建自己的接口来实现类似的功能,该接口的方法需要HighWayRepository并返回List<HighWay>,然后编写2个不同的实现

答案 1 :(得分:0)

为了删除重复项,我使用了下一个算法:

  1. 请确保您具有可验证此类功能是否有效的测试(重构所有测试之前应以绿色运行)

  2. 将重复的逻辑复制并粘贴到新方法中(例如tobeReused,稍后您可以将其重命名为更好的方法)

  3. 使用lambdas参数化哪些变化(Java支持以下lambda类型:Runnable,Consumer,Predicate,Function)

  4. 在1.中重命名方法。为了不陷入 analysis-paralysis ,最好在理解后重新命名。


  1. 一个好的测试套件应该

    1. 关注行为测试

    2. 掩盖正面和负面的情景

    3. 封面案例

    4. 封面例外

    5. 测试名称应描述将要测试的内容而不是方法

    6. 每个测试的格式都应遵循“当下然后给出”或“安排行为声明”的格式。

    可能的测试套件的示例代码结构:

    public class HighwaysTest {
      //Repository is valid
      @Test
      public void whenHighWayRepositoryIsValidThenHighWaysShouldBeSaved() {
      }
    
      //Repository is invalid
      @Test
      public void whenHighWayRepositoryIsInvalidThenHighWaysShouldBeSaved() {
      }
    
      //Wrong road code
      @Test
      public void whenRoadCodeIsInvalidThenPageParamIsNotUpdated() {
      }
    
      //Function getAndSaveNewHighWay fails
      @Test
      public void whenGetAndSaveNewHighWayFailsThenExceptionIsThrown() {
    
      }
    
      //Function getNewHighWay fails
      @Test
      public void whenGetNewHighWayFailsThenExceptionIsThrown() {
    
      }
    }
    
  2. 将重复的逻辑复制并粘贴到新方法中

     private List<HighWay> tobeReused(HighWayRepository repository) throws IOException {
            List<HighWay> highWays = new ArrayList<HighWay>();
            for (RoadCode code : RoadCode.values()) {
                try {
                    pageParam.setRoadName(code);
                    highWays.addAll(getAndSaveNewHighWay(repository));
                } catch (IOException e) {
                    IOException exception = dealException(e, code);
                    throw exception;
                }
            }
            return highWays;
        }
    
  3. 使用lambdas(在这种情况下为Function<ParameterType, ReturnType>)对变化进行参数设置:

        private <P extends  HighWayRepository, 
                 R extends  Collection> List<HighWay> tobeReused(P repository, Function<P, R> lambda) throws IOException {
    List<HighWay> highWays = new ArrayList<HighWay>();
                for (RoadCode code : RoadCode.values()) {
                    try {
                        pageParam.setRoadName(code);
                        highWays.addAll(lambda.apply(repository));
                    } catch (IOException e) {
                        IOException exception = dealException(e, code);
                        throw exception;
                    }
                }
                return highWays;
            }
    
  4. 因为找到好名字可能很棘手,所以最好在掌握业务领域后再命名。然后可以将功能tobeReused重命名为updatePageParamAndReturnHighways

您的代码如下:

public List<HighWay> updateAllNewHighWays(HighWayRepository repository) throws IOException {
        Function<HighWayRepository, Collection> lambda = (repo) -> getAndSaveNewHighWay(repo);
        List<HighWay> highWays = updatePageParamAndReturnHighways(repository, lambda);
        return highWays;
    }

public List<HighWay> getAllNewHighWays(HighWayRepository repository) throws IOException {
        Function<HighWayRepository, Collection> lambda = (repo) -> getNewHighWay(repo);
        List<HighWay> highWays = updatePageParamAndReturnHighways(repository, lambda);
        return highWays;
    }

单一责任原则(SRP)

您可能要考虑重构您的功能 updateAllNewHighWaysgetAllNewHighWays,因为它们负有多项责任(更新pageParam并返回高速公路)。