Sonntag, 14. Oktober 2012

Armed against breaking Liskov

Software is generally based on contracts which are declared by interfaces and abstract base classes. The danger in OOP is to break the Liskov Substitution Principle (LSP) during implementing a contract in a class hierarchy. We remember: Liskov provides the correct way to implement subtyping without "the must" to modify classes during extending the class hierarchy. This is only the short version of the story. We know that Liskov has a deeper sight which describes the expected behaviour of subclasses in relationship to their base classes. Extensions of a class hierarchy are a cornerstore of OOP which is based on inheritance and ploymorphism. Therefore, be careful at design time of a class hierarchy by following Liskov.

There are several solutions to avoid breaking Liskov. The first solution is to use delegate instead of inheritance. Favour Composition over Inheritance (FCoI) is  the proper CCD principle for this case. Another practical way to ensure that Liskov is satisfied is to use Design By Contract (DBC) which is not a CCD principle. In the CCD community we discussed hard whether DBC should be part of the CCD universe. The Result of the discussions was that DBC is not a typical CCD  principle these days. The pronounciation lays on “these days”. The range of opinions were from "we need DBC at runtime to ensure the correct implementation of contracts" to "DBC is not necessary since we use Unit-Tests in combination with agile methods (TDD) in the software development process". My personal opinion was and still is that DBC is worth to be a CCD principle. With  a DBC framework on board the conditions to fulfill a contract are expressed at runtime (such as during testing) which in my opinion is a honest benefit. 

Anyway, DBC helps to avoid Liskov violation using preconditions, postconditions and invariants. If you think about conditions you’ll notice that preconditions are requirements to the user of a method, while the postconditions are the requirements to the method themselves.

The simple way to avoid breaking Liskov is to use inheritance in combination with design patterns. Template Method is the pattern that provides a useful constraint in subtyping. Errors during subtyping may occur by overriding concrete methods and forgetting calling the parent method. Another interesting subject is that the Template Method Pattern helps to avoid duplication (DRY). To avoid duplication is important to reduce the danger of messy software. Template Method Pattern is the vehicle during refactoring software to eliminate duplication which is a strong part of the TDD mantra.

With the Template Method Pattern we do not override a concrete method. Instead we implement a method as a template in the base class and let the subclass decide whether to implement the method or not. This technique helps us to avoid breaking Liskov. Therefore, the Template Method Pattern can be treated as an aid against breaking Liskov.

Simple class hierarchy implementing the Template Method Pattern:

abstract class Cooking {
   
    /**
     * No Source code duplication.
     * Let the subclasses decide the cooking strategy.
     */
    public void prepareMeal() {
       
        // buy food
        // prepare food
        cook();
        // serve food
    }
   
    protected abstract void cook();
}

class CookInOven extends Cooking {

    @Override
    protected void cook() {/* in oven */}
}

class CookOnGrill extends Cooking {

    @Override
    protected void cook() {/* on grill */}
}

class CookingBean {

    public void prepareMeal(final Cooking cooking) {
      
        cooking.prepareMeal();
    }
}


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.