In my last article I described using composition in the design of an object based system. In that article I included a simplified UML class diagram of some work I did to a fictitious Product object to display the use of composition. Please see Figure 1 below. At the top of that diagram sits the class ProductBase. It is an abstract class (a class that has abstract methods and cannot be instantiated). But why is it abstract?

Figure 1 – Product UML

Let us first ask what could the class be marked as if it were not an abstract class? Here, there are two options. One, it could be a concrete class and two, it could be an interface. Let us look at the concrete class first.

A concrete class is any class that is not abstract; it is just a class. The following code snippet contains a concrete class followed by an abstract class.

Listing 1 – Concrete and Abstract

public class StockStatus
 {}

public abstract class ProductBase
 {
   abstract public StockStatus InventoryStatus{get;};
 }

When a class is concrete that means the only way to extend the class is by inheriting from it. That is often referred to as implementation inheritance or class inheritance because you are in essence inheriting all of the code and logic that went into the base class. That is not something I wanted to do with the Product object because it blows up encapsulation and is very inflexible.

The second option was to use an interface. Here, we would define an interface and then have ProductBase implement that interface. In the future, we could then substitute any class that implemented the interface.

Listing 2 – Using an interface

public interface IProduct
 {
   int GetNumberInStock();
 }
public class ProductBase : IProduct
 {
    public int GetNumberInStock()
    {
       return 3;
    }
}

In my first go around, this is basically what I had. Of course, that is why design reviews are important. It was pointed out by a coworker during a review that using an interface in this instance does not buy you anything more than using an abstract class, and if an abstract class were in fact used, we could mix in a little bit of implementation. Sounds good right? At first I did not like it at all.

“That breaks encapsulation!” I bellowed across the table, and let it reverberate off the walls and deaf ears. No one wanted to hear it. Why? Because using an abstract class here was simply a better design decision.

In this instance, a Product, no matter how many times subclassed, is a product. It has properties and some core methods. So in our abstract class we defined all of our entity properties implementation but left the “Load” (of data) methods as abstract so that the implementing class could define those. It ended up as an unintentional use of the template method, which is a good side effect of paying attention to your design. I believe that the decision is a mix of the best of both worlds.

Summary

We now have a good abstract class, which behaves as a well defined interface, plus when it comes time to substitute in another class of the same type, we have a lot of the plumbing already there. If we had used an interface it would mean we would have to rewrite a lot more code. This minimized our code creation and maintenance, but still keeps us in good object hierarchy.

Advertisements