Smell Detail

Loops

循环语句

Dispensables

Traditional loop statements are outdated, modern functional programming provides clearer pipeline operations to replace loops.

Define

Introduction

Since the earliest programming languages, loops have been a core element of programming. However, we feel that loops are now somewhat outdated, like bell-bottom pants and flocked wallpaper. With widespread support for functions as first-class citizens, we can use "Replace Loop with Pipeline" to retire these old relics.

Key Identifiers

  • Traditional for/while loops that iterate over collections
  • Loops that perform filtering, mapping, or reducing operations
  • Multiple nested loops that could be simplified
  • Loops with complex logic that obscure the main intent
  • Accumulator patterns that build up results step by step

Refactoring Suggestions

  1. Replace Loop with Pipeline: Use stream operations like filter, map, reduce
  2. Extract Method: Break complex loop logic into smaller, named operations
  3. Use Collection Methods: Leverage built-in collection operations
  4. Functional Composition: Chain operations together for clarity
  5. Parallel Processing: Use parallel streams when appropriate

Explanation

Pipeline operations like filter and map help us see more clearly what elements are being processed and what actions are being performed on them. Modern functional programming constructs make code more readable, less error-prone, and often more performant than traditional imperative loops. The intent becomes clearer when we express operations as a series of transformations rather than step-by-step instructions.

Code Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import java.util.*;
class Product {
private String name;
private double price;
private String category;
private boolean inStock;
public Product(String name, double price, String category, boolean inStock) {
this.name = name;
this.price = price;
this.category = category;
this.inStock = inStock;
}
// Getters
public String getName() { return name; }
public double getPrice() { return price; }
public String getCategory() { return category; }
public boolean isInStock() { return inStock; }
}
class ProductService {
// Traditional loop-based approach - outdated style
public List<String> getExpensiveProductNames(List<Product> products) {
List<String> result = new ArrayList<>();
for (int i = 0; i < products.size(); i++) {
Product product = products.get(i);
if (product.getPrice() > 100 && product.isInStock()) {
result.add(product.getName().toUpperCase());
}
}
return result;
}
// Another traditional loop with complex logic
public double calculateTotalValue(List<Product> products) {
double total = 0.0;
for (Product product : products) {
if (product.isInStock()) {
if (product.getCategory().equals("Electronics")) {
total += product.getPrice() * 1.1; // Add 10% markup
} else if (product.getCategory().equals("Books")) {
total += product.getPrice() * 0.9; // 10% discount
} else {
total += product.getPrice();
}
}
}
return total;
}
// Nested loops - hard to understand
public Map<String, Integer> getCategoryProductCount(List<Product> products) {
Map<String, Integer> result = new HashMap<>();
List<String> categories = new ArrayList<>();
// First loop to collect unique categories
for (Product product : products) {
boolean found = false;
for (String category : categories) {
if (category.equals(product.getCategory())) {
found = true;
break;
}
}
if (!found) {
categories.add(product.getCategory());
}
}
// Second loop to count products in each category
for (String category : categories) {
int count = 0;
for (Product product : products) {
if (product.getCategory().equals(category) && product.isInStock()) {
count++;
}
}
result.put(category, count);
}
return result;
}
// Traditional loop for finding items
public List<Product> findProductsByPriceRange(List<Product> products,
double minPrice, double maxPrice) {
List<Product> result = new ArrayList<>();
for (Product product : products) {
if (product.getPrice() >= minPrice &&
product.getPrice() <= maxPrice &&
product.isInStock()) {
result.add(product);
}
}
return result;
}
// Loop with side effects and accumulation
public void generateReport(List<Product> products) {
int totalProducts = 0;
int inStockProducts = 0;
double totalValue = 0.0;
List<String> expensiveItems = new ArrayList<>();
for (Product product : products) {
totalProducts++;
if (product.isInStock()) {
inStockProducts++;
totalValue += product.getPrice();
if (product.getPrice() > 200) {
expensiveItems.add(product.getName());
}
}
}
System.out.println("Total products: " + totalProducts);
System.out.println("In stock: " + inStockProducts);
System.out.println("Total value: " + totalValue);
System.out.println("Expensive items: " + expensiveItems);
}
// Traditional loop for data transformation
public List<String> createProductSummaries(List<Product> products) {
List<String> summaries = new ArrayList<>();
for (Product product : products) {
if (product.isInStock()) {
String summary = product.getName() + " (" + product.getCategory() +
") - $" + product.getPrice();
summaries.add(summary);
}
}
return summaries;
}
}