Smell Detail

Speculative Generality

夸夸其谈通用性

Dispensables

Overly complex structures designed for potential future needs that may never arrive.

Define

Introduction

Speculative Generality appears when code becomes overly complex to handle scenarios that may never occur. This "just in case" mindset adds unnecessary abstraction and maintenance burden.

Key Identifiers

  • Excessive abstraction layers or deep inheritance hierarchies
  • Abstract classes or interfaces with only a single implementation
  • Over-parameterized designs (too many unused options/config hooks)
  • Complex structures reserved purely for anticipated future needs

Refactoring Suggestions

  1. Collapse Hierarchy: Remove unnecessary abstraction layers
  2. Inline Class: Merge over-abstracted classes back into concrete code
  3. Remove Parameters: Delete unused or speculative parameters
  4. Simplify Design: Favor the simplest viable solution (embrace YAGNI)

Explanation

Elaborate abstractions and configuration systems were added for potential future requirements, but only a simple implementation is actually used. Follow the YAGNI (You Aren't Gonna Need It) principle and strip away speculative complexity.

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
import java.util.*;
import java.util.function.*;
// Overly designed abstraction layers
abstract class AbstractDataProcessor<T> {
public abstract void process(T data);
public void preProcess(T data) {}
public void postProcess(T data) {}
public boolean shouldProcess(T data) { return true; }
public void onError(Exception e) {}
public void onSuccess(T data) {}
}
class ConfigurableDataProcessor<T> extends AbstractDataProcessor<T> {
private Map<String, Consumer<T>> processors;
private List<Predicate<T>> filters;
private List<Consumer<T>> preprocessors;
private List<Consumer<T>> postprocessors;
public ConfigurableDataProcessor() {
this.processors = new HashMap<>();
this.filters = new ArrayList<>();
this.preprocessors = new ArrayList<>();
this.postprocessors = new ArrayList<>();
}
public void addProcessor(String name, Consumer<T> processor) {
processors.put(name, processor);
}
public void addFilter(Predicate<T> filter) {
filters.add(filter);
}
public void addPreprocessor(Consumer<T> preprocessor) {
preprocessors.add(preprocessor);
}
@Override
public void process(T data) {
// Complex processing logic, but actually only used in one way
for (Consumer<T> preprocessor : preprocessors) {
preprocessor.accept(data);
}
for (Predicate<T> filter : filters) {
if (!filter.test(data)) return;
}
for (Consumer<T> processor : processors.values()) {
processor.accept(data);
}
}
}
// What we actually need - a simple implementation
class SimpleStringProcessor {
public void process(String data) {
System.out.println("Processing: " + data);
}
}