Monday, May 11, 2015

Writing Scalable Code Using Inheritance in Java part 2

In my last post I mentioned a few simple rules on how to write maintainable code using inheritance:
  1. Reduce method and member scope
  2. Use final for methods and members
  3. Make make overridable methods abstract
  4. Don't make deep inheritance hierarchies
Today I want to show you how this affects maintainability.

Let's imagine we see a method like this in a class we know has many subclasses:

public final int getFoo() {...}

Here the presence of the final tells us that that we don't need to worry about anyone over-riding the behavior. Which is nice. 

public abstract int getFoo();

This one tells us sub classes *must* override this method. So we know that every sub class defines what we must do here. If we're in luck we'll be able to refactor this into a composition using the strategy pattern.

public int getFoo() {...}

This is problematic because we don't know whether there is subclass overriding this so we need to check all subclasses before we can modify its implementation. 

If we want to over-ride this method in a subclass we need to check the superclass to make sure that it doesn't have any side effects or being called at an unexpected time. Since it's a getXXX() method, it shouldn't be doing any side effects.

protected final int calculateFoo(...) {...}

This method can be accessed by itself and sub classes but not-over-ridden by sub classes. This method is probably part of an API offered to sub classes for them to call.

protected abstract int calculateFoo(...);

This method can be accessed by itself and sub classes and *must* be overridden by sub-classes. If we're in luck we'll be able to refactor this into a composition using the strategy pattern.

protected int calculateFoo(...) {...}

This is ambiguous. It might be a method that should be used as an API or it might define behavior like in the Strategy pattern. A third alternative is that it is meant to be over-ridden by sub-classes but provides a default implementation. Given the ambiguity I recommend adding a comment like this if it's the third case:

protected int calculateFoo(...) {
    /* Can be overriden by sub classes
     * default implementation: calculates foo using the bar algorythm 
     */
    ...
}

Although, again, using the strategy pattern with a default strategy is often the best way to go.

private something() {..}

It can only be called in this subclass and can't be overriden so we're good.

public int foo;

Don't do that unless it's final, the class is final and your object doesn't have any methods in it. Something like this:

public final class Foo {
  public final int foo;

  public Foo(int foo) { this.foo = foo }
}

This is a standard best practice.

protected final int foo;

Don't do this.

protected int foo;

Don't do this either.. but more so.

Next week: how to brush your teeth!

No comments: