Split Loop -- 分离循环

来源:互联网 发布:淘宝梦幻西游账号 编辑:程序博客网 时间:2024/05/04 16:34

Split Loop

Refactoring contributed by Martin Fowler

You have a loop that is doing two things

一个循环做了两件事情。

Duplicate the loop

复制循环。

void printValues() {double averageAge = 0;double totalSalary = 0;for (int i = 0; i < people.length; i++) {averageAge += people[i].age;totalSalary += people[i].salary;}averageAge = averageAge / people.length;System.out.println(averageAge);System.out.println(totalSalary);}

void printValues() {double totalSalary = 0;for (int i = 0; i < people.length; i++) {totalSalary += people[i].salary;}double averageAge = 0;for (int i = 0; i < people.length; i++) {averageAge += people[i].age;}averageAge = averageAge / people.length;System.out.println(averageAge);System.out.println(totalSalary);}

Motivation

You often see loops that are doing two different things at once, because they can do that with one pass through a loop. Indeed most programmers would feel very uncomfortable with this refactoring as it forces you to execute the loop twice - which is double the work.

你经常会看到一个循环做了两件事情,因为这两件事情可以通过一个循环来完成。对于大多数程序员来说这种重构手段不太适应,因为两个循环势必会造成两倍的工作量。

But like so many optimizations, doing two different things in one loop is less clear than doing them separately. It also causes problems for further refactoring as it introduces temps that get in the way of further refactorings. So while refactoring, don't be afraid to get rid of the loop. When you optimize, if the loop is slow that will show up and it would be right to slam the loops back together at that point. You may be surprised at how often the loop isn't a bottleneck, or how the later refactorings open up another, more powerful, optimization.

像这种优化,一个循环中做两件事情会降低清晰度。分离循环会引起一些问题,如引入一些临时变量。但是重构的时候不要害怕去除循环。你经常会吃惊由于循环造成的性能瓶颈并不是经常发生。

Mechanics

  • Copy the loop and remove the differing pieces from each loop
  • 复制循环,使得每个循环做一件事情。
  • Compile and test.
  • 编译测试。
  • Reorganize the lines to group the loop with related code from outside the loop
  • 重新组织循环外相关的代码
  • Compile and test.
  • 编译测试
  • Consider applying extractMethod or replaceTempWithQuery on each loop
  • 对于每个循环考虑使用extractMethod和replaceTempWithQuery

Example

Start with this code.

private Person [] people;void printValues() {double averageAge = 0;double totalSalary = 0;for (int i = 0; i < people.length; i++) {averageAge += people[i].age;totalSalary += people[i].salary;}averageAge = averageAge / people.length;System.out.println(averageAge);System.out.println(totalSalary);}

The loop is calculating both the total salary and the average age. This is two separate tasks, which just happen to use the same data structure. So I'll split the loops for now, knowing I can optimize later.

这段循环代码计算总薪水和平均年龄。这是两个不同的任务,仅仅因为使用了相同的数据结构people数组。所以我们现在分离它。

The first move is to copy the loop and remove from each leg one part of the calculation.

首先复制循环,然后分离计算任务。

void printValues() {double averageAge = 0;double totalSalary = 0;for (int i = 0; i < people.length; i++) {totalSalary += people[i].salary;}for (int i = 0; i < people.length; i++) {averageAge += people[i].age;}averageAge = averageAge / people.length;System.out.println(averageAge);System.out.println(totalSalary);}

I can now compile and test this.

编译测试

Then I can reorganize the text to group things together.

然后重新组织代码进行分组。

void printValues() {double totalSalary = 0;for (int i = 0; i < people.length; i++) {totalSalary += people[i].salary;}double averageAge = 0;for (int i = 0; i < people.length; i++) {averageAge += people[i].age;}averageAge = averageAge / people.length;System.out.println(averageAge);System.out.println(totalSalary);}

One thing that has bothered me about this refactoring is that there's a lot of duplicate loop setup code - and duplication is the worst smell of all. On the whole, however, I don't feel that concerned about it. Fundamentally the duplication is due to the godawful way that Java does loops. Other modern languages with a foreachstatement avoid the duplication completely.

这个重构有一点困扰我,重复的循环代码并且重复是万恶之首。总体上来说,我不会太关注这个。可以以foreach 语句替换掉。

Officialy that's the end of the refactoring. But the important thing here is what it leads you to do. In this case I'm inclined to apply Replace Temp with Query to each loop.

到此重构就算完成了。

void printValues() {System.out.println(averageAge());System.out.println(totalSalary());}private double averageAge() {double result = 0;for (int i = 0; i < people.length; i++) {result += people[i].age;}return result / people.length;}private double totalSalary() {double result = 0;for (int i = 0; i < people.length; i++) {result += people[i].salary;}return result;}

原创粉丝点击