02|TDD演示(2):识别坏味道与代码重构

TDD 学习笔记 | 极客时间 | 徐昊·TDD 项目实战 70 讲

🏆 课程原文链接 🍿 源码地址

是否进入重构有两个先决条件

  1. 测试都是绿的
  2. 坏味道足够明显

TDD 的红 / 绿 / 重构循环,分离了关注点

  • 在红 / 绿阶段,我们不关心代码结构,只关注功能的累积
  • 而在重构的过程中,因为测试的存在,我们可以时刻检查功能是否依旧正确,同时将关注点转移到“怎么让代码变得更好”上去

IDEA Refactor(重构)快捷键(Mac 系统)

  • Extract Method ⌥⌘M
  • Inline ⌥⌘N (两种情况)
    • 选中当前行
    • 选中一个调用方法
  • 选中接口的实现类:Use Interface Where Possible (尽可能使用接口)

优化思路

  • 不同的实现提取出接口
  • 实现接口
  • if else 分支使用接口替换
  • 构造函数转工厂方法(因为工厂方法可以 inLine,构造函数不行)
  • 尽可能使用接口
  • inLine
    • 精简代码(不用跳转到方法看具体实现)
    • 消除多余代码

代码和老师不一样的地方

  • 区别:提前抽取了公共变量 String flag = “-” + option.value();
  • 理由:
    • DRY 原则
    • 此时徐昊老师的代码里有多处重复
  • 感觉出现问题:
    • 我的命名比较糟糕:SingleValueOptionParser 类中 parse(List arguments, String flag),参数和 Option 无关
    • 因为将 Option 转换成了 String,后续多参数解析,可能会出问题

第一版

Args.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class Args {
    private static Object parseOption(List<String> arguments, Parameter parameter) {
        Option option = parameter.getAnnotation(Option.class);
        Object value = null;

        // 提前抽取了公共变量,导致代码和徐昊老师的不一样
        String flag = "-" + option.value();
        if (parameter.getType() == boolean.class) {
            value = arguments.contains(flag);
        }
        if (parameter.getType() == int.class) {
            int index = arguments.indexOf(flag);
            value = Integer.parseInt(arguments.get(index + 1));
        }
        if (parameter.getType() == String.class) {
            int index = arguments.indexOf(flag);
            value = arguments.get(index + 1);
        }
        return value;
    }
}

第二版

Args.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Args {

    // 徐昊老师的
    private static Object parseOption(List<String> arguments, Parameter parameter) {
        return PARSERS.get(parameter.getType()).parse(arguments, parameter.getAnnotation(Option.class));
    }

    // 我的
    private static Object parseOption(List<String> arguments, Parameter parameter) {
        return PARSERS.get(parameter.getType()).parse(arguments, "-" + parameter.getAnnotation(Option.class).value());
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class SingleValueOptionParser<T> implements OptionParser {

    // 徐昊老师的
    @Override
    public T parse(List<String> arguments, Option option) {
        int index = arguments.indexOf("-" + option.value());
        String value = arguments.get(index + 1);
        return valueParser.apply(value);
    }

    // 我的
    @Override
    public T parse(List<String> arguments, String flag) {
        int index = arguments.indexOf(flag);
        String value = arguments.get(index + 1);
        return valueParser.apply(value);
    }
}
comments powered by Disqus