Exploring SOLID Principles in .NET Core Applications — part 3

Abiodun Maborukoje
3 min readSep 24, 2023

--

New to .NET Core development? Let’s delve into SOLID principles to enhance your coding skills. This is part three of a series of five see the previous part here

Liskov Substitution Principle (LSP)

The Liskov Substitution Principle (LSP) is one of the SOLID principles, and it focuses on the proper usage of inheritance in object-oriented programming. It states that objects of derived classes should be substitutable for objects of their base classes without affecting the correctness of the program. In simpler terms, if a class is derived from another class, it should be able to be used interchangeably with its base class without causing unexpected behaviour or violating the contract established by the base class. Here’s an explanation and code samples that first violate LSP and then adhere to it.

Explanation:

Violating the LSP can lead to unexpected and incorrect behavior when substituting derived class objects for base class objects.

Adhering to LSP means that derived classes should extend the behavior of base classes without altering their fundamental contract (methods, properties, behavior).

Inheritance should not break the substitutability of derived classes for their base classes.

Let’s start with a code sample that violates the LSP:

// Violating LSP: Derived class behavior is not substitutable for the base class.

public class User
{
public int UserId { get; set; }
public string Username { get; set; }
}

public class AdminUser : User
{
public void GrantAdminPrivileges()
{
// Logic to grant admin privileges
Console.WriteLine($"{Username} is now an admin user.");
}
}

public class RegularUser : User
{
public void PerformRegularTask()
{
// Logic for a regular user task
Console.WriteLine($"{Username} is performing a regular user task.");
}
}

In this code, we have a User base class, and two derived classes: AdminUser and RegularUser. The violation of LSP occurs because AdminUser and RegularUser have added behaviour (GrantAdminPrivileges and PerformRegularTask, respectively) that is not substitutable for the base class User. This means you cannot use an AdminUser or RegularUser object interchangeably with a User object without encountering issues.

Let’s refactor the code to adhere to LSP:

// Adhering to LSP: Derived classes extend behavior without breaking substitutability.

public class User
{
public int UserId { get; set; }
public string Username { get; set; }
}

public abstract class UserType
{
public abstract void PerformUserSpecificTask(User user);
}

public class AdminUser : UserType
{
public override void PerformUserSpecificTask(User user)
{
// Logic to grant admin privileges
Console.WriteLine($"{user.Username} is now an admin user.");
}
}

public class RegularUser : UserType
{
public override void PerformUserSpecificTask(User user)
{
// Logic for a regular user task
Console.WriteLine($"{user.Username} is performing a regular user task.");
}
}

In this refactored code:

  • We’ve introduced an UserType base class that acts as an abstraction for user-specific tasks.
  • Both AdminUser and RegularUser classes now inherit from UserType and provide their own implementations for the PerformUserSpecificTask method, which accepts a User object as a parameter.

This adheres to the Liskov Substitution Principle because AdminUser and RegularUser can be used interchangeably with the base class UserType without breaking the substitutability. You can now call the PerformUserSpecificTask method on any UserType object and it will execute the appropriate behaviour for that user type without unexpected behaviour or violations of the contract.

Click here for part — 4

--

--

Abiodun Maborukoje

A seasoned Software Engineer with years of experience specializing in crafting advanced software solutions.