Skip to main content

All the Abstraction

·4 mins

In Java, we have something called abstract classes, for the uninitiated it can be seen as a hybrid class, somewhere between an implementation and an interface. It is a way to share common functionality between a group of classes, where common functionality is placed in the abstract class and specific code is placed in its sub classes. It’s a joker, a jack of all trades and sucks big time. Let me tell you a story about abstract classes and why they are the scum of Java code.

The good #

Despite me being the grumpiest old geezer in the universe, there is some good to abstract classes. Like stated, the good comes from the ability to share functionality across a group of classes, like in the example below:

abstract class Scheduler {

  public abstract void initialize();
  public abstract handleTimeout();

  public void stop(Timer timer) {

    timer.stop();
  }
}

class SchedulerA extends Scheduler {

  @Override
  public void initialize() {
    // Create a scheduler repeating every 5 minutes
  }

  @Override
  public void handleTimeout() {
    // Run a cleanup job for the db
  }

  @Override
  public void stop(Timer timer) {
    super.stop(timer);
  }
}

class SchedulerB extends Scheduler {

  @Override
  public void initialize() {
    // Create a one time scheduler running only once
  }

  @Override
  public void handleTimeout() {
    // Check that default data has been loaded
  }

  @Override
  public void stop(Timer timer) {
    super.stop(timer);
  }
}

Well, this is the good of it. The only thing they really solve is to reduce code duplication in cases where the implementation of an interface method is the same for every implementation. Remember the previous sentence, because it’s important!

The bad #

The fact that you can group functionality together into a single class instead of having to implement the same functionality in all sub classes is great, but using abstract classes comes with a huge negative - grouping of unrelated concerns! Let me explain.

Interfaces is a way of grouping implementation or functionality under a common umbrella. A good example is the Serializable interface, that when implemented adds the Serializable quality to the class. Interfaces group together what is known as related concern, a fancy way of saying things that belong together, code that solves the same problem for related areas. One simple example is Serializeable that solves the issue of converting a class object into a string. A more complex example would be the interface Iterable, that when implemented gives the ability to be used in a foreach statement. The interface tells you only what methods you need to implement, not how to do it. The sub class then adds the functionality of the interface, by adding code that solves that problem for its specific type.

Okay, that’s a quick dive into an interface and related concerns, so then, what is unrelated concern? Let me illustrate with a weird example:

abstract class LittleBitOfThisAndThat {

  long area(long width, long height) {
    return width * height;
  }

  abstract String toString();
}

That would be grouping on unrelated concern and this is really the biggest issue with abstract classes. I’m sure you think the example is stupid and yes, it is wildly exaggerated, but my point is that grouping unrelated concerns into any class is bad. The reason of course being that it’s a slippery slope, one small deviation from its concern will just lead to further deviation down the road. Although deviation from concern is always a bad thing, doing it in an interface or an abstract class will always be worse, simply because there are sub classes. Instead of having to correct a single class with concern deviation, you may have to correct a lot of them.

Conclusion #

The abstract class is a specific tool to solve a specific problem. Use them only when your implementations of an interface creates code duplication. Convert your interface to an abstract class and merge the duplicated code into a single concrete method in the super class, while keeping the individual implementations in the sub classes.