I have been reading about inversion of control and dependency injection and I was wondering the following.
Is there a good way to tell when it is okay to initialise an object inside a method body of a class and when it's better to pass it as a constructor dependency?
For instance I was doing refactoring after finishing a TDD kata about generating greetings with some processing of some arguments. The code had some intermediate results that I used to construct the final string.
During refactoring I extracted some of the logic in their own classes by creating an instance at the point in the method where I needed them and passing the intermediate structures in their constructor and then call the method that operated on them.
Are there considerations in choosing to write a class that has one method that acts on its parameters vs writing a class that instead takes the same parameters in the constructor and the methods acts on its fields?
Here is what the code looked like after the first refactor
public class GreeterGenerator {
private static final Greeter NO_NAME_GREETER = () -> "Hello, my friend."
private final NameProcessor nameProcessor;
public GreeterGenerator(NameProcessor nameProcessor) {
this.nameProcessor = nameProcessor;
}
public String buildGreeting(String name) {
return isEmpty(name)
? NO_NAME_GREETER.buildGreeting()
: buildGreeting(new String[]{name});
}
private boolean isEmpty(String name) {
return name == null || name.isEmpty();
}
public String buildGreeting(String[] names) {
String[] processedNames = nameProcessor.splitAnyCommaSeparatedEntriesToSingleNames(names);
var nameDifferentiator = new NameFilter(processedNames);
var normalGreeter = new NormalGreeter(nameDifferentiator.filterNormalNames());
var shoutingGreeter = new ShoutingGreeter(nameDifferentiator.filterShoutedNames());
return new GreeterJoiner(normalGreeter, shoutingGreeter).joinGreetings();
}
}
and this is after trying I moved the helper classes to constructor dependencies. The filtered names is just a wrapper around the two arrays that resulted from the methods in nameDifferentiator being called. The normalGreeter and shoutingGreeter were moved into the GreeterJoiner as constructor dependencies
public class GreeterGenerator {
private static final String NO_NAME_GREETING = "Hello, my friend.";
private final NameProcessor nameProcessor;
private final NameFilter nameFilter;
private final GreeterJoiner greeterJoiner;
public GreeterGenerator(NameProcessor nameProcessor,
NameFilter nameFilter,
GreeterJoiner greeterJoiner) {
this.nameProcessor = nameProcessor;
this.nameFilter = nameFilter;
this.greeterJoiner = greeterJoiner;
}
public String buildGreeting(String name) {
return isEmpty(name)
? NO_NAME_GREETING
: buildGreeting(new String[]{name});
}
private boolean isEmpty(String name) {
return name == null || name.isEmpty();
}
public String buildGreeting(String[] names) {
String[] processedNames = nameProcessor.splitAnyCommaSeparatedEntriesToSingleNames(names);
var filteredNames = nameFilter.filterNames(processedNames);
return greeterJoiner.joinGreetings(filteredNames);
}
}
The code and commits (just filter out Greeter) can be seen here --> https://github.com/PhoenixRe32/tdd-exercises/tree/master/greeter It's a repo where I do my TDD katas as an exersice/refresher/or just pass the time (equivalent of doing sudoku I guess)