> For the complete documentation index, see [llms.txt](https://ethan-lin.gitbook.io/refactoring/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ethan-lin.gitbook.io/refactoring/dan-wei-zhuan-huan/7-refactor-secrets.md).

# 重构精髓：十六字心法

上一步替换常量、改变函数签名的过程中，有没有同学一上来就将整个构造函数或者`as()`方法的签名替换掉？如果这么操作，在你一口气把 10+个函数的引用点修改过来之前，软件将一直处于不可工作的状态。这节内容，我们要向你介绍一种更精细的做法，也就是《重构》一书里反复展演的重构手法：如何让代码在重构的过程一直保持可工作。

这套手法背后的原理，**Thought**Works 的王健将之总结为一句[十六字心法](http://insights.thoughtworks.cn/principles-of-refactoring/)，那就是：

> 旧的不变
>
> 新的创建
>
> 一步切换
>
> 旧的再见

![重构十六字心法](/files/-Lr58_tQFeykyvzit1VX)

什么？你说心法太难懂？那下面我们以替换`as()`方法签名为例(`as(String targetUnit)` -> `as(Unit targetUnit)`)来理解一下这个过程：

## 旧的不变，新的创建

*“旧的不变，新的创建”*，指的是对于你要重构的编程元素（这里是指`as()`方法的`String unit`字符串变量），先不去做直接的修改或替换，也不直接删除它，而是先创建一份新的拷贝，并传入你期望的参数类型。做完以后，运行一下测试，因为新函数并没有任何调用点，所以此时测试应该都能够正常通过。

```java
    ...
    public Length as(String unit) { ... }
    public Length temp_as(String unit, Units temp_unit) { ... }
    ...
```

创建完新方法后，你需要将旧方法内的实现直接委托给新方法：

```java
    ...
    public Length as(String unit) {
        return this.temp_as(unit, null);
    }
    public Length temp_as(String unit, Units temp_unit) { ... }
    ...
```

做完这步后，同样需要运行一下测试。由于新函数内仍然是原来的方法体，而旧方法的对外接口并没有改变，因此测试仍然不太应该挂掉。如果挂了，你应该能很快地撤销，并发现问题所在，修改以后再运行测试。

这一步的作用是建立一个中间层：对旧方法的引用点仍然不改变，因此你不需要一下改变方法的所有调用点。这一步的作用很关键，因为它创建了一种“新旧共存”的机制，这使得你能逐步完成替换，随之带来的变化是：你可以在重构的任意一步中停止，同时保证系统仍然处于可用的状态。

事实上，在做完这步后，你已经可以提交代码，然后再慢慢地一步步将旧方法的引用点切换到新的方法上去，系统在重构的整个过程中将仍然保持可用的状态。

接下来，我们要在`temp_as`中使用`Unit`类型的`temp_unit`取代原来的`unit`，可以在`as()`方法中完成这个调整：

```java
class Length {
    ...
    public Length as(String unit) {
        Unit temp_unit = unit == Length.YARD ? Unit.YARD : null;
        return this.temp_as(unit, temp_unit);
    }
    public Length temp_as(String unit, Units temp_unit) { ... }
    ...
}

enum Unit {
    YARD,
}
```

做完这步以后，运行测试。测试应该也不会挂，因为我们只是新增了一个尚未被使用到的变量。接下来，我们再把这个变量在`temp_as`中用上：

```java
    ...
    public Length as(String unit) { }
    public Length temp_as(String unit, Units temp_unit) {
        ...
        if (this.unit.equals("f")) {
-            if (unit.equals("yard")) {
+            if (temp_unit == Unit.YARD) {
                len = new Length(this.value / 3, unit);
            } else { ... }
        }
        ...
    }
    ...
```

运行测试。

## 一步切换

搭建好新旧函数之间的桥梁以后，下一步要做的是找到所有的引用点并一一替换。我们在老的`as()`上使用快捷键`cmd + B`查找它的所有引用点如下：

![find-references](/files/-Lqv4_bpGDNmiWdCd_Jw)

好家伙，还真不少，引用点一共有 11 个。但不要紧，因为我们已经搭建好了新旧方法的桥梁，可以慢慢替换。首先找到第一处引用点，将它替换为直接调用`temp_as()`，运行测试。测试应该能完全通过。

```java
- Length length = new Length(1, Length.INCH).as(Length.INCH)
+ Length length = new Length(1, Length.INCH).as(Length.INCH, Units.INCH)
```

对于剩余的引用点，我们如法炮制，直至把所有的`as(String unit)`都替换成为`temp_as(String unit, Units temp_unit)`。这样，我们就完成了“替换`as()`方法参数类型”的重构。后续`as()`方法仍然在其他地方使用了`unit`参数，对这些地方的替换，就留给你去练习了。

这里也就显示出真正的重构技巧与一般的刀砍斧劈之间的差别了：我们重构的每一步都以极小的步子前进，随时都能保证测试通过、系统正常。更重要的是，这个过程是随时可以停止的，在真实的项目中，你可能没有大段的时间重构，可能会不时出现优先级更高的事情打断手头的工作。有了随时停止的能力，你就可以将宏大的重构目标分解成许多细小的动作，日拱一卒，哪怕每天空闲的时候花上 5 分钟重构、提交，最终也能安全地、聚沙成塔地完全大的重构目标。

## 旧的再见

完成所有的引用点替换后，IDE 随即也会提示我们：`as(String unit)`已经没有引用点了。此时，我们就可以把这个函数安全地删除，运行一下测试。测试应该依然能通过，我们也完成了一次简单的重构。

最后，我们把那个临时的方法名`temp_as(Unit temp_unit)`重新命名回来，为它重新正名：`as(Unit unit)`，此时，我们甚至可以将它改成更加表意的`as(Unit target)`。

这节的信息量有点大，所以，我录了一个视频，你可以放松地看一下，直观感受一下这个心法的实操过程。

&#x20;[![](/files/-LrEmtlXk08HnjCKvU3V)](https://github.com/linesh-simplicity/refactoring-course/tree/584195717663db01b5acb8fbe3bdf5e11d4049ba/content/units/videos/1-renaming-steps-480p.mov)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ethan-lin.gitbook.io/refactoring/dan-wei-zhuan-huan/7-refactor-secrets.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
