SOLID Principles - Liskov Substitution Principle

The third of the SOLID Principles to cover is the letter L which is the Liskov Substitution Principle (LSP).

SOLID

What is the Liskov Substitution Principle (LSP)?

The Liskov Substitution Principle (LSP) states that “Derived classes must be substitutable for their base classes".

Barbara Liskov introduced the principle during her conference keynote “Data Abstraction” in 1987.

At a fundamental level, the LSP is stating that you should be able to substitute a superclass object with any object of its subclass.

Put it another way, a subclass must behave the same was as it’s superclass in both contract and functionality.

You may also notice that the Liskov Substitution Principle (LSP) extends upon the Open-Closed Principle (OCP).

Also due to how C# and .NET CLR are designed you will find that you will not run into the most common substitution violations: contravariance of method arguments (changing the number or types of input parameters) and covariance of return types (changing return type).

An Example

    class User
    {
        public virtual string GetPermissions()
        {
            return "normal";
        }
    }

    class Admin : User
    {
        public override string GetPermissions()
        {
            return "full";
        }
    }

    class Guest : User
    {
        public override string GetPermissions()
        {
            throw new NotImplementedException("Guest does not have permissions!");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var users = new List<User>
            {
                new User(), 
                new Admin(), 
                new Guest()
            };

            foreach (var user in users)
            {
                var permission = user.GetPermissions();
            }
        }
    }

The above example violates the Liskov Substitution Principle (LSP) due to the fact that when the GetPermission() function on the Guest class is called it throws an exception.

The expectation is that all subclasses (Admin & Guest) of the superclass (User) behave the same, they would return permissions.

How do we make sure to satisfy the Liskov Substitution Principle (LSP)?

We will need to break out into seperate interfaces and then only call GetPermissions() on objects that support that functionality.

    interface IUser
    {
    }

    interface IUserWithPermission
    {
        string GetPermissions();
    }

    class User : IUser, IUserWithPermission
    {
        public string GetPermissions()
        {
            return "normal";
        }
    }

    class Admin : IUser, IUserWithPermission
    {
        public string GetPermissions()
        {
            return "full";
        }
    }

    class Guest : IUser
    {

    }

    class Program
    {
        static void Main(string[] args)
        {
            var users = new List<IUser>
            {
                new User(), 
                new Admin(), 
                new Guest()
            };

            foreach (var user in users.OfType<IUserWithPermission>())
            {
                var permission = user.GetPermissions();
            }
        }
    }

You can see from this example we now have a list of users but only work with those that are capable of supporting the GetPermissions() function as defined in the IUserWithPermission interface.

Avatar
Alan P. Barber
Software Developer, Computer Scientist, Scrum Master, & Crohn’s Disease Fighter

I specialize in Software Development with a focus on Architecture and Design.

comments powered by Disqus
Next
Previous

Related