Let’s say I have the interface AuthorDao
with two different implementation classes for example MyAuthorDaoImpl1
and MyAuthorDaoImpl2
.
In my interface AuthorDao
I have some basic crud methods and one extra method which is static
for getting a new instance of MyAuthorDaoImpl1
.
It looks like this:
public interface AuthorDao { void methodA(); void methodB(); ... static MyAuthorDaoImpl getInstance() { return new MyAuthorDaoImpl1(); } }
Questions
- Is this static method
getInstance()
is not an anti-pattern? Because in my opinion we shouldn’t depend our interface on concrete implementation class, but my friend says it’s okay, and he is pretty sure, that this should be work like this. He says it is factory method. - He says we can create instances of this interface by constructor, we don’t have to use this
static
method, therefore this is nothing bad. Is it true, that this is nothing bad? I think it is example of tight coupling and interface should not depend on concrete implementation, but he says this is not the case. - He refers that this is the same case like in
Calendar
class, because there is alsogetInstance()
method.
Edit
Also, in his opinion this static method will simplify refactoring if we will decide to change MyAuthorDaoImpl1
to MyAuthorDaoImpl2
.
Because the only change will be in getInstance()
method.
Advertisement
Answer
This implementation is a circular dependency, which roughly looks like:
While it will likely work in Java, imagine what would happen if you no longer included the class MyAuthorDaoImpl
when later you decided to implement ABetterAuthorDaoImpl
. Now you have to change the interface. It is a minor change in this case, but imagine it on a larger scale.
Normally a factory method returns the interface type rather than the implementation type. Example:
class AuthorDaoFactory { static AuthorDao getInstance() { return new MyAuthorDaoImpl1(); } }
This avoids the circular dependency, demonstrated by this diagram:
You’ll note that there is no circular path in the dependencies. This may not matter for this simple example, but what if your factory method created an instance of a class that was dynamically loaded based on a config? This is a common example of Inversion of Control (IoC) and is often used to do things like provide a common interface to different hardware. The implementation is hidden behind the interface.
You’ll notice that the Java Calendar
class method getInstance
returns the type Calendar
. The underlying implementation may be locale-specific. If you look at the description of the method in the Java documentation it says:
Gets a calendar using the default time zone and locale. The Calendar returned is based on the current time in the default time zone with the default locale.
What is the implementation then? You don’t know and don’t care, you only know it is of type Calendar
.