话不多说,直接上这次要重构的目标:
public class Application {
private List<int[]> theList;
...
// 本次需要重点关注的代码
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<>();
for (int[] x : theList) {
if (x[0] == 4) {
list1.add(x);
}
}
return list1;
}
}
代码的坏味道
这是一段很短且真实逻辑并不复杂的代码,但是从这段小小的代码里面,我们却嗅到了不少坏味道:
总结来说:代码缺乏好的命名,无法让阅读者根据名字(函数名、变量名)来了解函数总体的逻辑。
第一步重构如果把代码改成下面这个样子,或许会好一点:
public class Application {
private static final int FLAGGED = 4;
private static final int STATUS_VALUE_INDEX = 0;
private List<int[]> gameBoard;
...
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<>();
for (int[] x : gameBoard) {
if (x[STATUS_VALUE_INDEX] == FLAGGED) {
flaggedCells.add(x);
}
}
return flaggedCells;
}
}
在这一步的重构中,我们仅仅是修改了函数名、变量名,定义出了常量。其中:
相信你看到这段代码,对它的作用已经能猜个七七八八了:
这大概是一个棋盘类游戏,棋盘由一个个格子构成,每个格子用一个int数组来表示。之所以用数组,是因为格子所包含的信息很多,比如颜色、值等等。其中,0这个下标代表格子的状态信息。如果被标记(flagged)过,数值为4。
第二步重构接下来,用面向对象的思想,再把一些信息“封装”一下。
public class Application {
private List<Cell> gameBoard;
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<>();
for (Cell cell : gameBoard) {
if (cell.isFlagged()) {
flaggedCells.add(cell);
}
}
return flaggedCells;
}
private static class Cell {
private static final int FLAGGED = 4;
private static final int STATUS_VALUE_INDEX = 0;
private int[] info;
...
private boolean isFlagged() {
return info[STATUS_VALUE_INDEX] == FLAGGED;
}
}
}
在这一步,通过定义Cell这个类(实际上不需要是内部类),部分实现细节屏蔽掉了。
上层应用不需要关注Cell内部是如何存储各种信息的,当前依然保留了int[]作为底层实现,但是数组是一种比较裸的用法,后续完全可以替换为更好的形式。
Cell提供了isFlagged方法,让上层应用可以只关心是否标记这样的业务逻辑,而不需要关注4和0这样的实现细节。
实际上有了这些之后,可通过Lambda表达式把getFlaggedCells化简到一行,你知道怎么做吗?
总结Phil Karlton说:
There are only two hard things in Computer Science: cache invalidation and naming things
大家平时都在讲工匠精神,有时候,匠心的体现并不需要多么花里胡哨的炫技,只需要好好的给代码取个见名知意的名字,或许就已经成功了一半。
说明:因为之前写的各种文章阅读量实在过于惨淡,所以这是一个新开的、比较偏实操一些的系列。其中代码的例子来自于《Clean Code》,文字部分为原创内容。
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved