Liskov Substitution Principle (lsp)
#🖋️DesignPrinciples #💡OOP #💻SoftwareDevelopment
My third post is about LSP, I am on the third principle of SO-L-ID with this blog post.
Please find my other blog post about Dependency Inversion Principle and Interface Segregation Principle
Background Barbara Liskov defined the subtypes as below in 1988, Data Abstraction and Hierarchy What is unwanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
I found the above statement complicated, hard to understand, to have better understanding I read the 3.3 Type Hierarchy from the paper.
Why do we need Liskov Substitution Principle? By using LSP you can use a single interface, take advantage of substitutability and represent two or more subtypes. I’ll try to come up with a coding example using Swift while trying to stay away from the common, by the book I read and blogs, example of square & rectangle.
I decided to demonstrate a car example by using Swift. On the first part of the code I am coupling concrete types and exampling LSP Violation by two class without sharing any interface while there are common functionalities and bussiness logic for the program.
Tesla and BMWHybrid both have
func checkTirePressure() -> Int
func start() -> Bool
func stop() -> Bool
func checkBatteryLevel() -> Int vs (Int, Int) // sign is different
class CarOwner {
let car: BMWHybrid
To follow LSP, I’ll come up with a Interface, by doing that I’ll share the common functionalities of two car classes and decouple the CarOwner class from BMWHybrid.
protocol Car {
func checkTirePressure() -> Int
func start() -> Bool
func stop() -> Bool
func preStartBatteryLevelCheck() -> Bool
}
By creating the above Car protocol, we have decoupled the CarOwner class from BMW, now we our carowner is ready to change her car form hybrdo to fully electric vehicle. I’ll demonstrate exact same example with using superclass rather than interface to have less similarity with my other blog/code example of Dependency Inversion Princple.
class Car {
private var battery: Int = 100
private var tirePressure: Int = 34
private var carStarted: Bool = false
func checkTirePressure() -> Int {
return tirePressure
}
func start() -> Bool {
carStarted = true
return carStarted
}
func stop() -> Bool {
carStarted = false
return carStarted
}
func preStartBatteryLevelCheck() -> Bool{
return battery > 10
}
}
class Tesla: Car {
//no override needed at this point
}
class BMWHybrid: Car {
private var fuel: Int = 100
func checkFuelLevel() -> Int {
return fuel
}
override func preStartBatteryLevelCheck() -> Bool{
return super.preStartBatteryLevelCheck() && checkFuelLevel() > 8
}
}
By using superclass we reused Tesla’s code and for the fuel specific part of the BMWHybrid, we called super.preStartBatteryLevelCheck() for battery and continued with our custom property and method for fuel.
Find the swift file here
Clean Architecrute by Robert C. Martin