# LSP - Liskov Substitution Principle

The Liskov Substitution Principle (LSP) applies to inheritance in such a way that the **derived classes must be completely substitutable for their base classes**. In other words, if class A is a subtype of class B, then we should be able to replace B with A without interrupting the behavior of the program.

Several circumstances may indicate that the LSP has been violated:

* A subclass changes the behavior of the parent class.&#x20;
* A subclass overrides the methods of the parent class but throws an exception.&#x20;
* A subclass overrides the methods of the parent class, but with an empty implementation.&#x20;
* A subclass inherits methods from the parent class but is not used.

There are two ways to solve this problem:&#x20;

* Breaking the hierarchy.&#x20;
* Tell, don’t ask.

## Example 1 - a subclass changes the behavior of the parent class&#x20;

<figure><img src="/files/TaQyXR0ptiGt7xEmim44" alt=""><figcaption></figcaption></figure>

To illustrate this, we will go with a classic example of squares and rectangles that people often use to explain LSP because it is very simple and easy to understand.

{% hint style="success" %}

```java
class Rectangle {
    private int width;
    private int height;
 
    public int calculateArea() {
        return this.width * this.height;
    }
 
    public void setWidth(int width) {
        this.width = width;
    }
 
    public void setHeight(int height) {
        this.height = height;
    }
}
```

{% endhint %}

{% hint style="danger" %}

```java
class Square extends Rectangle {
 
    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        super.setHeight(width);
    }
 
    @Override
    public void setHeight(int height) {
        super.setWidth(height);
        super.setHeight(height);
    }
}
```

{% endhint %}

{% hint style="success" %}

```java
public class LSPExample1 {

    public void example1() {
        Rectangle rect = new Rectangle();
        rect.setWidth(5);
        rect.setHeight(10);
        System.out.println(rect.calculateArea()); // 50
 
        Square square = new Square();
        square.setWidth(5);
        square.setHeight(10);
        System.out.println(square.calculateArea()); // 100
    }
}
```

{% endhint %}

Looking at the example above, we see that all operations are very reasonable. Since a square has 2 sides, every time we set the length of one side, we reset the length of remaining side.

However, the Square class inherits from the Rectangle class but the Square class has different shapes and it changed the behavior of the Rectangle class, resulting in the LSP violation.

As the example above, because class Square inherits from class Rectangle, we can use the following:

{% hint style="danger" %}

```java
public class LSPExample1 {
    public void example2() {
        Rectangle rect = new Square();
        rect.setWidth(5);
        rect.setHeight(10);
        System.out.println(rect.calculateArea()); // 100
    }
}
```

{% endhint %}

The result is obviously wrong, the area of the rectangle should be 5 \* 10 = 50.

According to this principle, we must ensure that when a class inherits from another class, it will not change the behavior of that class.

In this case, to avoid the LSP violation, we have to create a superclass, e.g. Shape class, and then for Square and Rectangle to inherit this Shape class.

**Solution: Break the hierarchy**

{% hint style="success" %}

```java
public interface Shape {
    int area();
}

class Rectangle implements Shape {
    private int width;
    private int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    @Override
    public int area() {
        return this.width * this.height;
    }
}

class Square implements Shape {

    private int side;

    public void setSide(int side) {
        this.side = side;
    }

    @Override
    public int area() {
        return this.side * this.side;
    }
}
```

{% endhint %}

## Example 2 - a subclass changes the behavior of the parent class

{% hint style="danger" %}

```java
class Product {
    protected double discount = 10;

    public double getDiscount() {
        return discount;
    }
}

class SecondHandProduct extends Product {
    public void applyExtraDiscount() {
        discount = discount * 1.5;
    }
}

class PricingCalculator {

    void showDiscount() {
        List<Product> products = Arrays.asList(
                new Product(),
                new SecondHandProduct()
        );
        products.forEach(product -> {
            if (product instanceof SecondHandProduct) { // Violate LSP
                SecondHandProduct secondHandProduct = (SecondHandProduct) product;
                secondHandProduct.applyExtraDiscount();
            }
            System.out.println(product.getDiscount());
        });
    }
}
```

{% endhint %}

As you can see, with this implementation we have to check the instance type and call another method before calling getDiscount(). It violates LSP.

**Solution: "Tell, don't ask"**

{% hint style="success" %}

```java
class Product {
    protected double discount = 10;

    public double getDiscount() {
        return discount;
    }
}

class SecondHandProduct extends Product {

    public double getDiscount() {
        applyExtraDiscount();
        return discount;
    }
    
    public void applyExtraDiscount() {
        discount = discount * 1.5;
    }
}

class PricingCalculator {

    void showDiscount() {
        List<Product> products = Arrays.asList(
                new Product(),
                new SecondHandProduct()
        );
        products.forEach(product -> {
            System.out.println(product.getDiscount());
        });
    }
}
```

{% endhint %}

## Example 3 - subclass throws an exception

<figure><img src="/files/0HGoPw0GHAi11V6WYELf" alt=""><figcaption></figcaption></figure>

Another case of LSP scope is a subclass that throws an exception.

{% hint style="success" %}

```java
interface FileService {
    void getFiles();
    void deleteFiles();
}
 
class ImageFileService implements FileService {
 
    @Override
    public void getFiles() {
        // Load image files
    }
 
    @Override
    public void deleteFiles() {
        // Delete image files
    }
}
 
class TempFileService implements FileService {
 
    @Override
    public void getFiles() {
        // Load temp files
    }
 
    @Override
    public void deleteFiles() {
        // Delete temp files
    }
}
```

{% endhint %}

The above implementation classes have no problem, everything runs fine. Now we add a new SystemFileService class. With the requirement not allowed to delete the file system, this class will generate an UnsupportedOperationException error. A method that is designed but not used is not a good design either.

{% hint style="danger" %}

```java
class SystemFileService implements FileService {
 
    @Override
    public void getFiles() {
        // Load temp files
    }
 
    @Override
    public void deleteFiles() {
        throw new UnsupportedOperationException();
    }
}
```

{% endhint %}

When executing the deleteFiles() method, the SystemFileService class throws an error at runtime. It cannot replace its parent FileService class, so it is already LSP violation.

{% hint style="danger" %}

```java
public class LSPExample2 {
    public void example2() {
        FileService fileService = new FileService(); 
        fileService.deleteFiles(); // Violate LSP
    }
}
```

{% endhint %}

**Solution: Create a new interface**

## Example 4 - subclass with an empty implementation

{% hint style="success" %}

```java
interface CrudRepository<T, ID> {

    Iterable<T> findAll();

    T findOne(ID id);

    T save(T entity);

    void update(T entity);

    void delete(ID id);

}

class UserRepository implements CrudRepository<User, Long> {

    @Override
    public Iterable<User> findAll() {
        // find all users ...
        return list;
    }

    @Override
    public User findOne(Long id) {
        // find user by id ...
        return user;
    }

    @Override
    public User save(User entity) {
        // save user ...
        return savedUser;
    }

    @Override
    public void update(User entity) {
        // update user ...
    }

    @Override
    public void delete(Long id) {
        // Set flag to delete user
    }
}
```

{% endhint %}

The above implementation looks good. However, we now have a MasterDataRepository, with requirements that the master data is read-only, cannot be added, modified, or deleted. We have the implementation as follows:

{% hint style="danger" %}

```java
class MasterDataRepository implements CrudRepository<MasterData, Long> {

    @Override
    public Iterable<MasterData> findAll() {
        // find master data ...
        return list;
    }

    @Override
    public MasterData findOne(Long id) {
        // find master data by id ...
        return masterData;
    }

    @Override
    public MasterData save(MasterData entity) {
        // Do nothing, don't allow to add master data
        return null;
    }

    @Override
    public void update(MasterData entity) {
        // Do nothing, don't allow to delete master data
    }

    @Override
    public void delete(Long id) {
        // Do nothing, don't allow to delete master data
    }
}
```

{% endhint %}

Because class MasterDataRepository inherits from class CrudRepository, we can use the following:

{% hint style="danger" %}

```java
class LSPExample3 {

    @Test
    public void example3() {
        CrudRepository<MasterData, Long> crudRepository = new MasterDataRepository();
        MasterData masterData = crudRepository.findOne(1);
        masterData.setDesc("Change description");
        crudRepository.update(masterData); // Do nothing, violate LSP
    }
}
```

{% endhint %}

**Solution: Create a new interface**


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gpcoder.gitbook.io/clean-code/bonus/solid-principle/lsp-liskov-substitution-principle.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
