Consider passing parameter through a method:
class Fibo {
int fibo(int n) {...}
}
Versus passing through a constructor:
class Fibo {
int n;
Fibo(int n) {
this.n = n;
}
int fibo() {...}
}
Observe how names are wrong. In the first case (method) we tend to have the name in the method.
class Math {
int fibo(int n) {...}
}
In the second (constructor) we tend to have the name in the class.
class Fibo
int n;
Fibo(int n) {
this.n = n;
}
int compute() {...}
}
As an exercise we can design a service that takes 2 parameters to do its job.
Consider all variations of method/constructor initialisation of those 2 parameters.
Construct(a, b); m();
Construct(); m(a, b);
Construct(a); m(b);
Construct(b); m(a);
There is obviously no good design in itself. We should choose depending on consumer responsability, lifecycle and LSP (Liskov substitution principle).
class Echo {
void echo() {
while (true) {
String line = reader.readLine();
writer.write(line);
}
}
}
class PdfWriter {
File output;
void write(String text) {...}
}
Here we choose output as a field, because it has a longer lifecycle (the same for each iteration). But text has a shorter lifecycle (change for each iteration).
class Main {
void extractErrors(Path errorFile) {
ExactSearcher searcher = ...
for (String line: Files.readAll(errorFile)) {
if (searcher.matches("[ERROR]")) {
extract(line);
}
}
}
}
class RegexSearcher {
boolean matches(String regex) {
text.matches(regex);
}
}
class ExactSearcher {
boolean matches(String exact) {
text.equals(exact);
}
}
In this case, something is wrong with method matches(String)
.
In one case the contract is that regex should conform to Pattern.compile(String).
In the second case there is no such constraint.
Here we violate LSP because we cannot substitute ExactSearcher by RegexSearcher. Notice that LSP rely on the usage ("the programs").