Summary
Principle Name | Liskov Substitution Principle |
Acronym | LSP |
Other Names | Principle of type conformance(or substitutability) Strong Behavioral Subtyping |
Principle Type | SOLID |
Tagline | Subtypes must be substitutable for their base types. |
Implementing Design Patterns | Adapter Pattern Command Pattern Composite Pattern Decorator Pattern Factory Method Pattern Observer Pattern Proxy Pattern State Pattern Strategy Pattern Template Method Pattern |
Related Principles | Single Responsibility Principle(SRP) Open/Closed Principle(OCP) Interface Segregation Principle(ISP) Dependency Inversion Principle(DIP) |
Definition
Liskov Substitution Principle(LSP) is the third(3rd) principle of the SOLID Principles.
This principle instructs how subclasses should relate to the superclasses. The idea of this principle is that the behavior of a subclass should be as correct(consistent) as the behavior of the superclass.
Liskov Substitution Principle(LSP) says-
Objects of superclass should be replaceable by the objects of a subclass, without affecting the correctness of the program.
When subclass object replaces super class object, the result might not be exactly same, but rather it –
- should be consistent
- has the same intended behavior
- should not break any functionality
A derived class should be able to replace/substitute its base class, without altering the behavior of the implemented system. This way we can introduce new subclasses, without any risk of breaking existing functionality.
This principle is important for writing robust and maintainable code. As it ensures that a subclass will not behave unexpectedly, compared to the superclass.
What You Should Do
What You Cannot Do
Implementation
Here we have a base class Shape, and some classes extending Shape.
The Shape declares a function for calculating area. So in the subclasses in the same function definition, we should implement the calculation for calculating area(and nothing else).
This way the subclasses, maintain the same behavior as the parent class.
import java.lang.Math;
abstract class Shape {
abstract double getArea();
}
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double getArea() {
return width * height;
}
}
class Triangle extends Shape {
private double side1, side2, side3;
public Triangle(double side1, double side2, double side3) {
this.side1 = side1;
this.side2 = side2;
this.side3 = side3;
}
@Override
public double getArea() {
double s = (side1 + side2 + side3) / 2;
return Math.sqrt(s * (s - side1) * (s - side2) * (s - side3));
}
}
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * Math.pow(radius, 2);
}
}
public class Main {
public static void printArea(Shape shape) {
System.out.println("The area is: " + shape.getArea());
}
public static void main(String[] args) {
Rectangle rectangle = new Rectangle(5, 10);
Triangle triangle = new Triangle(3, 4, 5);
Circle circle = new Circle(7);
printArea(rectangle);
printArea(triangle);
printArea(circle);
}
}
Javafrom abc import ABC, abstractmethod
import math
class Shape(ABC):
@abstractmethod
def get_area(self) -> float:
"""Calculate the area of the shape"""
pass
class Rectangle(Shape):
def __init__(self, width: float, height: float) -> None:
self.width = width
self.height = height
def get_area(self) -> float:
return self.width * self.height
class Triangle(Shape):
def __init__(self, side1: float, side2: float, side3: float) -> None:
self.side1 = side1
self.side2 = side2
self.side3 = side3
def get_area(self) -> float:
# Calculate the semi-perimeter
s = (self.side1 + self.side2 + self.side3) / 2
# Apply Heron's formula
area = math.sqrt(s * (s - self.side1) * (s - self.side2) * (s - self.side3))
return area
class Circle(Shape):
def __init__(self, radius: float) -> None:
self.radius = radius
def get_area(self) -> None:
return math.pi * self.radius**2
def print_area(shape: Shape):
print(f"The area is: {shape.get_area()}")
# Demo usage
# Instances of Rectangle and Circle
rectangle = Rectangle(5, 10)
triangle = Triangle(3, 4, 5)
circle = Circle(7)
# Calling print_area() with both subclasses
print_area(rectangle)
print_area(triangle)
print_area(circle)
Pythonpackage main
import (
"fmt"
"math"
)
type Shape interface {
GetArea() float64
}
type Rectangle struct {
width, height float64
}
func (r Rectangle) GetArea() float64 {
return r.width * r.height
}
type Triangle struct {
side1, side2, side3 float64
}
func (t Triangle) GetArea() float64 {
s := (t.side1 + t.side2 + t.side3) / 2
return math.Sqrt(s * (s - t.side1) * (s - t.side2) * (s - t.side3))
}
type Circle struct {
radius float64
}
func (c Circle) GetArea() float64 {
return math.Pi * math.Pow(c.radius, 2)
}
func printArea(shape Shape) {
fmt.Printf("The area is: %.2f\n", shape.GetArea())
}
func main() {
rectangle := Rectangle{width: 5, height: 10}
triangle := Triangle{side1: 3, side2: 4, side3: 5}
circle := Circle{radius: 7}
printArea(rectangle)
printArea(triangle)
printArea(circle)
}
Go<?php
abstract class Shape
{
abstract public function getArea(): float;
}
class Rectangle extends Shape
{
private float $width;
private float $height;
public function __construct(float $width, float $height)
{
$this->width = $width;
$this->height = $height;
}
public function getArea(): float
{
return $this->width * $this->height;
}
}
class Triangle extends Shape
{
private float $side1;
private float $side2;
private float $side3;
public function __construct(float $side1, float $side2, float $side3)
{
$this->side1 = $side1;
$this->side2 = $side2;
$this->side3 = $side3;
}
public function getArea(): float
{
$s = ($this->side1 + $this->side2 + $this->side3) / 2;
return sqrt($s * ($s - $this->side1) * ($s - $this->side2) * ($s - $this->side3));
}
}
class Circle extends Shape
{
private float $radius;
public function __construct(float $radius)
{
$this->radius = $radius;
}
public function getArea(): float
{
return pi() * pow($this->radius, 2);
}
}
function printArea(Shape $shape)
{
echo "The area is: " . $shape->getArea() . PHP_EOL;
}
# Demo usage
$rectangle = new Rectangle(5, 10);
$triangle = new Triangle(3, 4, 5);
$circle = new Circle(7);
printArea($rectangle);
printArea($triangle);
printArea($circle);
PHPabstract class Shape {
abstract getArea(): number;
}
class Rectangle extends Shape {
private width: number;
private height: number;
constructor(width: number, height: number) {
super();
this.width = width;
this.height = height;
}
getArea(): number {
return this.width * this.height;
}
}
class Triangle extends Shape {
private side1: number;
private side2: number;
private side3: number;
constructor(side1: number, side2: number, side3: number) {
super();
this.side1 = side1;
this.side2 = side2;
this.side3 = side3;
}
getArea(): number {
const s = (this.side1 + this.side2 + this.side3) / 2;
return Math.sqrt(s * (s - this.side1) * (s - this.side2) * (s - this.side3));
}
}
class Circle extends Shape {
private radius: number;
constructor(radius: number) {
super();
this.radius = radius;
}
getArea(): number {
return Math.PI * Math.pow(this.radius, 2);
}
}
function printArea(shape: Shape): void {
console.log(`The area is: ${shape.getArea()}`);
}
const rectangle = new Rectangle(5, 10);
const triangle = new Triangle(3, 4, 5);
const circle = new Circle(7);
printArea(rectangle);
printArea(triangle);
printArea(circle);
TypeScript