如何重构重复代码?
how to refactor the duplicate code?
我知道重复的是气味
但是如何重构代码?
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;
}
由于唯一变化的部分是循环内部,您可以重构循环部分,任何只有循环内部变化的部分。
如果您使用 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
和 return 的方法,然后编写 List<HighWay>
2 种不同的实现方式
为了去除重复,我使用了下一个算法:
确保你有验证这些功能是否有效的测试(在重构之前你的所有测试应该是 运行 绿色)
将重复的逻辑复制并粘贴到新方法中(例如 tobeReused,稍后您可以将其重命名为更好的方法)
参数化使用 lambda 的变化(Java 支持下一种类型的 lambda:Runnable、Consumer、Predicate、Function)
重命名方法 1. 一个更好的名字。为了不卡在分析-麻痹,理解了再重命名是个好主意
一个好的测试套件应该
专注于行为测试
涵盖正面和负面场景
覆盖边缘案例
覆盖异常
测试名称应该描述什么将被测试而不是如何
每个测试的格式应遵循 Given-When-Then 或 Arrange-Act-Assert。
可能的测试套件的示例代码结构:
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() {
}
}
将重复的逻辑复制并粘贴到新方法中
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;
}
参数化使用 lambda 的变化(在本例中 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;
}
因为找到一个好名字可能很棘手,所以最好在你很好地掌握业务领域之后再命名。然后函数 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)
您可能需要考虑重构您的函数
updateAllNewHighWays
和 getAllNewHighWays
因为他们有不止一项责任(更新 pageParam
和 return 高速公路)。
我知道重复的是气味
但是如何重构代码?
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;
}
由于唯一变化的部分是循环内部,您可以重构循环部分,任何只有循环内部变化的部分。
如果您使用 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
和 return 的方法,然后编写 List<HighWay>
2 种不同的实现方式
为了去除重复,我使用了下一个算法:
确保你有验证这些功能是否有效的测试(在重构之前你的所有测试应该是 运行 绿色)
将重复的逻辑复制并粘贴到新方法中(例如 tobeReused,稍后您可以将其重命名为更好的方法)
参数化使用 lambda 的变化(Java 支持下一种类型的 lambda:Runnable、Consumer、Predicate、Function)
重命名方法 1. 一个更好的名字。为了不卡在分析-麻痹,理解了再重命名是个好主意
一个好的测试套件应该
专注于行为测试
涵盖正面和负面场景
覆盖边缘案例
覆盖异常
测试名称应该描述什么将被测试而不是如何
每个测试的格式应遵循 Given-When-Then 或 Arrange-Act-Assert。
可能的测试套件的示例代码结构:
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() { } }
将重复的逻辑复制并粘贴到新方法中
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; }
参数化使用 lambda 的变化(在本例中
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; }
因为找到一个好名字可能很棘手,所以最好在你很好地掌握业务领域之后再命名。然后函数
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)
您可能需要考虑重构您的函数
updateAllNewHighWays
和 getAllNewHighWays
因为他们有不止一项责任(更新 pageParam
和 return 高速公路)。