Running this piece of code will print null
public class Weird {
static class Collaborator {
private final String someText;
public Collaborator(String text) {
this.someText = text;
}
public String asText() {
return this.someText;
}
}
static class SuperClass {
Collaborator collaborator;
public SuperClass() {
initializeCollaborator();
}
protected void initializeCollaborator() {
this.collaborator = new Collaborator("whatever");
}
public String asText() {
return this.collaborator.asText();
}
}
static class SubClass extends SuperClass {
String someText = "something";
@Override
protected void initializeCollaborator() {
this.collaborator = new Collaborator(this.someText);
}
}
public static void main(String[] arguments) {
System.out.println(new Weird.SubClass().asText());
}
}
(Here is also the GitHub Gist)
Now, I know why this happens (it’s because the field of the superclass is initialized and then the constructor of the superclass is called, before the field of the subclass is initialized)
The questions are:
- What is the design issue here? What is wrong with this design, from an OOP point of view, so that the result looks weird to a programmer? What OOP principles are being broken through this design?
- How to refactor so it does not work ‘weird’ and is proper OOP code?
My issue with the design is the
someTextstring should either be an explicit dependency (or “collaborator”) of the Collaborator object, or of SubClass, or explicitly a part of the global context (so a constant or a property of a shared context object).Ideally, either Collaborator should be responsible for retrieving its dependencies; or, if SubClass is responsible for this, it should have
someTextas a dependency (even if it’s always initialised to the same value), and only initialise Collaborator when someText is set.Conceptually speaking, the dependency relation between objects in the design imposes a partial ordering of the initialisation. The mechanism to implement this ordering should always be explicit in your design, instead of relying on implementation details of the Java language.
An (overengineered) example:
(Now excuse me, I need to go stand under a waterfall after writing something called
ConstantCollaboratorTextLocator.)