Benad's Web Site

In my previous post about dependency injection I skipped an important step. You should avoid class inheritance and use interfaces instead, to reduce coupling

Let's follow our previous example with the DataStore interface. For a moment, let's suppose that we have the class SQLDataStore that implements the base functionality of SQL-based data stores and MySQLDataStore that extends it to support MySQL:

SQLDataStore:
public class SQLDataStore implements DataStore {
  protected byte[] cache;
  //...
  protected void preFillCache(String sql) { /* ... */ }
  public void store(long key, byte[] value) { /* ... */ }
  //...
}
MySQLDataStore:
public class MySQLDataStore extends SQLDataStore {
  public void store(long key, byte[] value) {
    //...
    preFillCache(some_sql);
    // do something with this.cache...
  }
}

Notice the horrible coupling introduced here. If I change the cache to be using java.util.HashMap, then any *SQLDataStore class, that is anything that can extend SQLDataStore, might have to be changed.

Basically, subclassing a class with extends let's you "patch" a class, but it's still horrible a avoided as much as possible. The solution? Use interfaces:

SQLDataStore:
public interface SQLDataStore extends DataStore {    
  public static final class Factory {
    public static SQLDataStore create() {
      return SQLDataStoreImpl.create();
    }
  }
}

final class SQLDataStoreImpl implements SQLDataStore {
  private SQLDataStore() {}
  //...
  private void preFillCache(String sql) { /* ... */ }
  public static SQLDataStoreImpl create() {
    return new SQLDataStoreImpl();
}

Notice that now no one can use preFillCache, as it is not part of the "contract" of what others can use in SQLDataStore. If others want to use any behavior in SQLDataStore, they have to create a new instance from the Factory and be limited to what the "contract" defines, with no chance to subclass it, even by accident. Also notice that SQLDataStore, now simply an interface, extends another interface, and is thus safe from "hidden coupling".

To create the new instance of SQLDataStore you do SQLDataStore store = SQLDataStore.Factory.create(), or you can use Dependency Injection. And now you can figure out what I meant in my previous post...

References:

Published on September 18, 2005 at 17:19 EDT

Older post: Automating Java Builds with CruiseControl

Newer post: Ad hoc task list