“But, grandmother, what big eyes you have”

Introduction

The third article of the SOLID series is going to deal with derived classes

The Liskov Substitution Principle originally states that

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

Simply speaking this means that derived classes MUST be substitutes for their base class.

The Problem with this problem

What is Batman famous most for? The darkest voice in the DC universe? His only superpower beeing money? Having the best gadgets? Yes, that’s it. Gadgets. What would Batman be without his Batarang, his Batmobile, his Grappling gun or his Batflyswatter? Lets take his Batarang as an example. Even his throwable Ouch-devices come along in a huge variety. But they all have a couple of things in common. They are all throwable, bat shaped (only for aerodynamic reasons) and have some special effects when they hit the enemy. I wonder where he stores them all? Probably in his Batbutt.

In an object-oriented Batworld it would look like this

[pastacode lang=”java” manual=”public%20class%20Batman%20%7B%0A%0A%09private%20Belt%20belt%20%3D%20new%20Belt()%3B%0A%09%0A%09public%20void%20throwAtEnemy()%20%7B%0A%09%09Batarang%20classicBatarang%20%3D%20getFromBelt(new%20ClassicBatarang())%3B%0A%09%09classicBatarang.throwAtEnemy()%3B%0A%0A%09%09Batarang%20explosiveBatarang%20%3D%20getFromBelt(new%20ExplosiveBatarang())%3B%0A%09%09explosiveBatarang.throwAtEnemy()%3B%0A%0A%09%09Batarang%20solarBatarang%20%3D%20getFromBelt(new%20SolarBatarang())%3B%0A%09%09solarBatarang.throwAtEnemy()%3B%0A%09%7D%0A%0A%09private%20Batarang%20getFromBelt(Batarang%20batarangType)%20%7B%0A%09%09return%20belt.get(batarangType)%3B%0A%09%7D%0A%0A%7D” message=”Batman” highlight=”” provider=”manual”/]

[pastacode lang=”java” manual=”public%20interface%20Batarang%20%7B%0A%09%0A%09void%20throwAtEnemy()%3B%0A%0A%7D” message=”Batarang interface” highlight=”” provider=”manual”/]

[pastacode lang=”java” manual=”public%20class%20ClassicBatarang%20implements%20Batarang%20%7B%0A%0A%09%40Override%0A%09public%20void%20throwAtEnemy()%20%7B%0A%09%09%2F%2F%20fly%20at%20100km%2Fh%0A%09%7D%0A%0A%7D” message=”Classical Batarang” highlight=”” provider=”manual”/]

First Batman throws his classic Batarang which flies at 100km/h and knocks out one enemy instantly. Next up is the explisive Batarang. Boom! 5 enemies with one hit.

[pastacode lang=”java” manual=”public%20class%20ExplosiveBatarang%20implements%20Batarang%7B%0A%0A%09%40Override%0A%09public%20void%20throwAtEnemy()%20%7B%0A%09%20%2F%2F%20since%20they%20are%20a%20bit%20heavier%20they%20only%20fly%20with%20an%20average%20speed%20of%2099%20km%2Fh%0A%09%09%0A%09%7D%0A%7D%0A” message=”Explosive Batarang” highlight=”” provider=”manual”/]

But wait until you see Alfreds newest spin on the Batarangs. Since global warming is a real thing Alfred thought about solar powered ultra high speed Batarangs. Use the energy of the sun to bring down crime.

[pastacode lang=”java” manual=”public%20class%20SolarBatarang%20implements%20Batarang%7B%0A%0A%09%40Override%0A%09public%20void%20throwAtEnemy()%20%7B%0A%09%09if(isDaytime())%20%7B%0A%09%09%09%2F%2F%20not%20throwable%20…%09%09%0A%09%09%7D%20else%20%7B%0A%09%09%09%2F%2F%20fly%20at%20100%20km%2Fh%09%0A%09%09%7D%0A%09%7D%0A%7D” message=”Solar Batarang” highlight=”” provider=”manual”/]

While chasing his dream of a Gotham City without air pollution, Alfred forgot that Batman actually only fights during the nighttime. Making his solar powered Batarangs completely useless. Since they only work during daytime they are no substitutes for the one and only Batarang. You guessed right buddy. The SolarBatarang.class clearly violated the Liskv Substitution Principle!

Let the now disappointed Batman behind and go back to the object-oriented world. What exactly happened? The problem is, that the substitute for the Batarang interface is only working under specific circumstances.

Refactoring baby

I know of two different ways of solving or helping out in this situation. Although to be honest with you, I do not like them very much. I will explain why in a bit. First let me show you the solution that is worse.

Including the isDaytime method into the Batarang interface
This is a really crappy way of solving this problem. Not only does every single implementation need to overwrite this method. Even if it has absolutely no impact on the implementation whatsoever. But also this can lead to complete confusion. Say a coworker of Alfred wants to implement another Batarang and stumbles over the isDaytime method. Completely baffeled of what to do he tries to integrate this method in his new Batarang under all cost. Just to get ballskicked by Alfred because he did not need it at all. Try to avoid integrating methods into interfaces that not all implementations need! This would also violate the Open/Closed Principle.

Introducing a new DayTimeBatarang extending the Batarang interface.
By doing so you kind of shift the problem out of the implementations. A good thing is that you separate all the Batarangs that only work during the daytime into their own group. This makes sure that Batman can no longer get pissed if he tries to throw a solar powered Batarang during nighttime. The object orientation clearly forbids that. Lets see why:

[pastacode lang=”java” manual=”public%20interface%20DayTimeBatarang%20extends%20Batarang%20%7B%0A%0A%09public%20void%20activateDaylightSuperpowers()%3B%0A%09%0A%7D” message=”DayTimeBatarang interface” highlight=”” provider=”manual”/]

[pastacode lang=”java” manual=”public%20class%20SolarBatarang%20implements%20DayTimeBatarang%7B%0A%0A%09%40Override%0A%09public%20void%20throwAtEnemy()%20%7B%0A%09%09activateDaylightSuperpowers()%3B%0A%09%09%0A%09%09%2F%2F%20fly%20at%20200%20km%2Fh%09%09%09%0A%0A%09%7D%0A%0A%09%40Override%0A%09public%20void%20activateDaylightSuperpowers()%20%7B%0A%09%09%2F%2F%20Activate%20solar%20panels%20to%20boost%20speed%0A%09%09%0A%09%7D%0A%7D” message=”SolarBatarang” highlight=”” provider=”manual”/]

[pastacode lang=”java” manual=”public%20class%20Batman%20%7B%0A%0A%09private%20Belt%20belt%20%3D%20new%20Belt()%3B%0A%0A%09public%20void%20throwAtEnemy()%20%7B%0A%09%09Batarang%20classicBatarang%20%3D%20getFromBelt(new%20ClassicBatarang())%3B%0A%09%09classicBatarang.throwAtEnemy()%3B%0A%0A%09%09Batarang%20explosiveBatarang%20%3D%20getFromBelt(new%20ExplosiveBatarang())%3B%0A%09%09explosiveBatarang.throwAtEnemy()%3B%0A%0A%09%09Batarang%20solarBatarang%20%3D%20getFromBelt(new%20SolarBatarang())%3B%0A%09%09solarBatarang.throwAtEnemy()%3B%0A%09%7D%0A%0A%09private%20Batarang%20getFromBelt(Batarang%20batarangType)%20%7B%0A%0A%09%09boolean%20isDayTimeBatarang%20%3D%20batarangType%20instanceof%20DayTimeBatarang%3B%0A%0A%09%09if%20(!isDayTimeBatarang)%20%7B%0A%09%09%09%2F%2F%20No%20DayTimeBatarang%20…%20No%20problem%20here%0A%09%09%09return%20belt.get(batarangType)%3B%0A%09%09%7D%20else%20if%20(isDayTimeBatarang%20%26%26%20!isDayTime())%20%7B%0A%09%09%09%2F%2F%20Returning%20a%20DayTimeBatarang%20during%20nighttime%20…%20Not%20gonna%20happen%20bro.%0A%09%09%09throw%20new%20InvalidBatarangException()%3B%20%2F%2FBEST.EXCEPTION.EVER.%0A%09%09%7D%20else%20if%20(isDayTimeBatarang%20%26%26%20isDayTime)%20%7B%0A%09%09%09%2F%2F%20Throwing%20DayTimeBatarangs%20during%20daytime%20…%20that’s%20fun!%0A%09%09%09return%20belt.get(batarangType)%3B%0A%09%09%7D%0A%0A%09%7D%0A%0A%7D” message=”Batman” highlight=”” provider=”manual”/]

Although this works fine and does not violate the Open/Closed Principle the solution isn’t perfect at all. As stated before you simply shift the problem elsewhere.

What have we learned?

The Liskov Substitution Principle makes sure that every baseclass can be substituted by every subclass under all circumstances. This is also paraphrased by Design by Contract. Every subclass has to fulfil the contract that it has with its baseparent class.

Categories: Blog Posts