EL as Exercises of Liskov Substitution Principle

By Karol Bocian | March 7, 2020

Liskov substitution principle

The application should work correctly when we put in the base class place every derived class.

How to use the LSP principle in practice? 

Think carefully before taking advantage of the inheritance. Usually, a better option is to use composition. Try to do a simple experiment:  put in the classes using our class tree, all posterity classes and the base class, and see if the program still behaves properly, or if it only expects one particular type of class.

Example

Let’s consider this task:

We have an application, which shows a video from a camera. To our application, we can connect many camera types. Not every we can handle. Fortunately, we knot it at the beginning. We create base class Camera and inherit its in special camera classes (and in not handled camera too). 

class Program
   {
       static void Main(string[] args)
       {
           var cameras = new List<Camera>();
           var cameraNC1 = new CameraNC1("Front camera");
           cameras.Add(cameraNC1);
           var cameraShower = new CameraShower(cameraNC1);
           cameraShower.Show();
 
 
           var cameraNC2 = new CameraNC2("Back camera");
           cameras.Add(cameraNC2);
           cameraShower = new CameraShower(cameraNC2);
           cameraShower.Show();
 
           var unsupportedCamera = new UnsupportedCamera(null);
           cameras.Add(unsupportedCamera);
           cameraShower = new CameraShower(unsupportedCamera);
           cameraShower.Show();
       }
   }
 
   internal class CameraShower
   {
       private Camera camera;
 
       public CameraShower(Camera camera)
       {
           this.camera = camera;
       }
 
       internal void Show()
       {
           if (!(camera is UnsupportedCamera))
               Console.WriteLine($"Camera name: {camera.GetName()}");
       }
   }
 
   internal class CameraNC2 : Camera
   {
       public CameraNC2(string name) : base(name)
       {
       }
   }
   internal class CameraNC1 : Camera
   {
       public CameraNC1(string name) : base(name)
       {
       }
 
   }
   internal class UnsupportedCamera : Camera
   {
       public UnsupportedCamera(string name) : base(name)
       {
       }
 
       internal override string GetName()
       {
           throw new Exception("This camera does not have name. It is not supported.");
       }
   }
 
   internal abstract class Camera
   {
       protected readonly string name;
 
       public Camera(string name)
       {
           this.name = name;
       }
       internal virtual string GetName()
       {
           return name;
       }
   }

Everything working well. But, when for not handling a camera we should make a special checking. We can improve it and implement GetName method in  different way.

class Program
{
    static void Main(string[] args)
    {
        var cameras = new List<Camera>();
        var cameraNC1 = new CameraNC1("Front camera");
        cameras.Add(cameraNC1);
        var cameraShower = new CameraShower(cameraNC1);
        cameraShower.Show();
 
 
        var cameraNC2 = new CameraNC2("Back camera");
        cameras.Add(cameraNC2);
        cameraShower = new CameraShower(cameraNC2);
        cameraShower.Show();
 
        var unsupportedCamera = new UnsupportedCamera();
        cameras.Add(unsupportedCamera);
        cameraShower = new CameraShower(unsupportedCamera);
        cameraShower.Show();
    }
}
 
internal class CameraShower
{
    private Camera camera;
 
    public CameraShower(Camera camera)
    {
        this.camera = camera;
    }
 
    internal void Show()
    {
        Console.WriteLine($"Camera name: {camera.GetName()}");
    }
}
 
internal class CameraNC2 : Camera
{
    public CameraNC2(string name) : base(name)
    {
    }
}
internal class CameraNC1 : Camera
{
    public CameraNC1(string name) : base(name)
    {
    }
 
}
internal class UnsupportedCamera : Camera
{
    public UnsupportedCamera() : base("Unknown - this camera is not supported")
    {
    }
}
 
internal abstract class Camera
{
    protected readonly string name;
 
    public Camera(string name)
    {
        this.name = name;
    }
    internal virtual string GetName()
    {
        return name;
    }
}

All posts from mini project: Learn SOLID and OOP principles:

Sources

Main image

Materials