Can somebody help a novice programmer to understand if his solution is correct?
My question is similar to the the following two:
What's wrong with overridable method calls in constructors?
Factory pattern in C#: How to ensure an object instance can only be created by a factory class?
Problem: I want to have subclasses which will differ only in their initialisation method. However, I also want to prevent instantiating these classes without initialization. In other words, I want to ensure, that some “initialize()” method will always be called after instantiation of a subclass:
public abstract class Data {
protected Parameter dataSource;
Data(parameter1){
this.dataSource = parameter1;
loadData(); // should be called to initialise class fields and ensure correct work of other class methods
}
protected abstract loadData(){
... //uses dataSource
}
}
So I decided to perform initialization on a constructor. It worked (now I know that it’s a very bad practice) until I created a subclass where the initialize method used some additional parameters:
public class DataFromSpecificSources extends Data {
private Parameter dataSource2;
public DataFromSpecificSources(parameter1, parameter2){
this.dataSource2 = parameter2; // I can't put it here because the constructor is not called yet
super(parameter1); // this, of course, will not work
}
@Override
private void loadData(){
... // uses both dataSource 1 and 2
// or just dataSource2
}
}
This, of course, is not going to work. And I started searching for a right pattern… After I read the answers on questions posted before, I decided to use the factory and limit visibility of the subclass constructor to the package:
My solution:
// factory ensures that loadData() method will be called
public class MyDataFactory(){
public Data createSubClass(parameter1,parameter2){
Data subClass;
if (parameter2 != null){
subClass = new DataFromSpecificSources(parameter1, parameter2);
subClass.loadData();
} else {
subClass = new AnotherSubClass(parameter1);
subClass.loadData()
}
return subClass;
}
}
public abstract class Data {
protected Parameter dataSource;
Data(parameter1){
this.dataSource = parameter1;
}
// I don't call it in constructor anymore - instead it's controlled within the factory
protected abstract loadData(){
... //uses dataSource
}
}
public class DataFromSpecificSources {
private Parameter dataSource2;
protected DataFromSpecificSources(){}
// now this constructor is only visible within package (only for the factory in the same package)
DataFromSpecificSources(parameter1, parameter2){
super(parameter1); // it does not initialise data anymore
this.dataSource2 = parameter2;
}
@Override
protected void loadData(){
... // uses dataSources 1 and 2
}
}
Now factory ensures that subclasses will be initialized (data will be loaded) and instantiation of subclasses is not allowed in other packages. Other classes have no access to constructor of subclasses and are forced to use factory to get an instance of a subclass.
I just wanted to ask if my solution is correct (logically) and Factory method with subclass constructor visibility limited to the package is right choice here?! Or there is any other more effective pattern solving the problem?!
Using a factory is definitely a step in the right direction. The issue I see is that what happens when you want to add a third class that takes a third parameter. Now your Factory is either going to have to have a second overloaded
createSubClassmethod taking the third parameter, or all your code is going to have to be rewritten to provide the third parameter. Additionally you are forcing anyone using the Factory to specifynullfor the second parameter even if they only want the single parameter class…. when you get to the class that takes 15 parameters how are you going to remember which parameter is whichThe solution to this is to use the Builder pattern instead.
Now the code creating the Data instances can work like so:
or
And that code is future-proofed for when you add parameter3… and you can even have the builder with a non-null default for parameter3 if you so need that.
The next thing you notice is that you now have this nice Builder object that contains all the required parameters… so now you can add getters to the Builder and just pass the Builder as the constructor parameter, e.g.
So that you now almost have a standard constructor signature
Now for some Java specific improvements. We can make the builder not need to know about the sub-classes at all!
Using a DI framework we can inject the classes that implement the
Datainterface / abstract class into the Builder and then just iterate through each class until we find a class that supports the configuration of the Builder instance.The poor-man’s DI framework is the
/META-INF/servicescontract and theServiceLoaderclass available since JRE 1.6 (though the core logic has been in Java since 1.2)Your builder’s create method will then look a little something like
Whether you want to go to that extreme is questionable… but since you might come across it at some point in time when looking at other people’s code, it is probably a good time to point it out to you now.
Oh, the reason why we have to add a Factory class to be looked up by the ServiceLoader is because ServiceLoader expects to call the default constructor, and we have hidden the default constructor so we use a Factory class to do the work for us and allow us to keep the constructor hidden.
There is nothing preventing the Factory classes from being static inner classes in the Data classes (which gives them great visibility on the class they are creating), e.g.
As we can then list in
META-INF/services/com.mypackage.DataFactoryThe best bit about this type of solution is it allows adding additional implementations just by adding those implementations to the classpath at run-time… i.e. very loose coupling