Skip to content
Advertisement

Creating new instance of concrete implementation in interface – is this an antipattern?

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

  1. 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.
  2. 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.
  3. He refers that this is the same case like in Calendar class, because there is also getInstance() 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:

circular dependency

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:

enter image description here

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.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement