OCP and LSP are very closely related. Implementing OCP would implicitly follow LSP in most of cases.
LSP states, If S is a subtype of T, any object of T can be substituted with an object of S leaving our software in stable state. Most of developers might know substitution already but let me explain a bit before moving forward. Consider a method void Notify (Campaign campaign). While calling this method we pass it the object of campaign Notify (new Campaign()); . Suppose our Campaign class has a child class called SummerCampaign then according to LSP calling this method as Notify (new SummerCampaign()); should also work for our software.
IS A relationship is a very common OOP concept. Every duck IS A bird, every Honda City IS A car, every square IS A rectangle and every SummerCampaign IS A Campaign etc. To represent these entities in object oriented way, we create a base class and then a child class which simply means child class IS A base class. I will take two examples out of the above I quoted for IS A relation and then see if they conform to LSP.
Let me code
A CampaignManager which takes in a Campaign object to launch it and send notifications to the subscribers of the system. Let’s have a look at the campaign class
class Campaign
{
public string Name { get; set; }
public void Save()
{
// Save campaign using to the repository
}
public void InActivate()
{
// Save campaign using to the repository
}
}
Campaign has a child class SummerCampaign to fulfil certain specialized functionality around campaigns that are launched in summer.
internal class SummerCampaign : Campaign
{
public void InitiateSummerActivities()
{
// Code here
}
}
And CampaignManager
public class CampaignManager
{
Logger logger;
public CampaignManager(Logger logger)
{
this.logger = logger;
}
public void LaunchCampaign(Campaign campaign)
{
try
{
// Code to launch the campaign
}
catch (Exception exception)
{
logger.Error("Exception occured", exception);
}
}
}
Let’s write some code to launch a campaign.
Campaign campaign = new Campaign();
// set properties for campaign
CampaignManager manager = new CampaignManager(new Logger());
manager. LaunchCampaign(campaign);
Here if we replace the campaign object with an object of SummerCampaign, our software will behave normally.
SummerCampaign summercampaign = new SummerCampaign();
// set properties for summercampaign
manager. LaunchCampaign(summercampaign);
Identifying LSP violations in code is very important. Sometimes real life scenarios which present IS A relationship violate the LSP. An example of this, Square IS A Rectangle. How??
Let me code
Every square is a rectangle having width same as height. A rectangle can have width different from its height.
internal class Rectangle
{
private double height;
private double width;
public void SetHeight(double height)
{
this.height = height;
}
public void SetWidth(double width)
{
this.width = width;
}
}
class Square : Rectangle
{
}
The above example seems pretty fine for IS A relationship but it’s bad one according to LSP. For a Square SetHeight and SetWidth don’t make sense as setting one would automatically change the other. So if we substitute an object of Rectangle with Square, it will introduce problems in our system.