Derived classes must be substitutable for their base class. When a class
method accepts an object, the method should be able to accept children
of that object without knowing anything about the children.
Likov’s Substitution Principle states that if for each object m1 of type S there is an object m2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when m1 is substituted for m2 then S is a subtype of T.
Now let’s see the simple example of LSP Violation of code
In this example we have one common interface “ICustomer” which is inherits to two classes i.e.
ClsPaidCustomerSalesEnity, ClsFreeCustomerSalesEnity . two different
classes which set and get properties and do calculation and print
invoice as per customer status
For ClsPaidCustomerSalesEnity we have calculated special discount and printed the Invoice with customer name and amount
For ClsFreeCustomerSalesEnity we have 0 discount and in PrintInvoice we return something
throw new NotImplementedException();
Means nothing was implemented in this method
//Interface Codepublic interface ICustomer
{
string CustomerName { set; get; }
int CustomerCode { set; get; }
int ProductQuantity { set; get; }
double ProductRate { set; get; }
double CalculateDiscountRate();
string PrintInvoice(double _amount);
}
//Paid Customer Code
public class ClsPaidCustomerSalesEnity : ICustomer
{
private string _customername;
private int _customercode;
private int _productquantity;
private double _productrate;
public string CustomerName
{
set { _customername = value; }
get { return _customername; }
}
public int CustomerCode
{
set { _customercode = value; }
get { return _customercode; }
}
public int ProductQuantity
{
set { _productquantity = value; }
get { return _productquantity; }
}
public double ProductRate
{
set { _productrate = value; }
get { return _productrate; }
}
public double CalculateDiscountRate()
{
double rate = ProductQuantity * ProductRate;
double discountamount = 0;
double disrate = 20;
discountamount = (disrate / 100) * rate;
rate = rate – discountamount;
return rate;
}
public string PrintInvoice(double _amount)
{
return “Product Invoice For Customer ” + CustomerName + ” with Total Amount ” + _amount;
}
}
//Free Customer Code
public class ClsFreeCustomerSalesEnity : ICustomer
{
private string _customername;
private int _customercode;
private int _productquantity;
private double _productrate;
public string CustomerName
{
set { _customername = value; }
get { return _customername; }
}
public int CustomerCode
{
set { _customercode = value; }
get { return _customercode; }
}
public int ProductQuantity
{
set { _productquantity = value; }
get { return _productquantity; }
}
public double ProductRate
{
set { _productrate = value; }
get { return _productrate; }
}
public double CalculateDiscountRate()
{
return 0;
}
public string PrintInvoice(double _amount)
{
throw new NotImplementedException();
}
}
Both code is almost similar but only difference in implementation and inherits with common interface
Now let’s come to our window form application code
ICustomer objIcust;Now let’s come to our window form application code
List<ICustomer> listcust = new List<ICustomer>();
objIcust = new ClsPaidCustomerSalesEnity();
objIcust.CustomerName = “Paid Customer”;
objIcust.CustomerCode = 001;
objIcust.ProductQuantity = 5;
objIcust.ProductRate = 20;
listcust.Add(objIcust);
objIcust = new ClsFreeCustomerSalesEnity();
objIcust.CustomerName = “Free Customer”;
objIcust.CustomerCode = 002;
objIcust.ProductQuantity = 5;
objIcust.ProductRate = 20;
listcust.Add(objIcust);
string printinvoice = “”;
foreach (ICustomer iCust in listcust)
{
double amount = iCust.CalculateDiscountRate();
printinvoice = iCust.PrintInvoice(amount);
listBox1.Items.Add(“Invoice Report –> ” + printinvoice);
}
Problem with this code when loop our Icustomer collections it will work fine with
ClsPaidCustomerSalesEnity calculates Discount Rate and and print invoice properly
But when it loads ClsFreeCustomerSalesEnity CalculateDiscountRate
returns 0 and for PrintInvoice it throws new NotImplemented Exception
which is totally wrong and on UI we don’t need any exception error and
writing Hacks and like if conditions is not proper way of design
architecture
This is the problem with code
As it says it Free Customer code like paid customer code but unlike
paid customer code there is not implementation of DiscountRate and Print
Invoice method
So now let’s see the solution for above code using Likov’s Substitution Principle
A simple solutions but Customer Type i.e. paid customer class and
free customer are not same difference in discount rate and print invoice
implementation
If it looks like a duck, quacks like a duck, but needs batteries – wrong abstraction
So we need to create different interface for paid customer and for free customer
public interface IFreeCustomer{
string CustomerName { set; get; }
int CustomerCode { set; get; }
int ProductQuantity { set; get; }
double ProductRate { set; get; }
}
//use this for paid customer for printing invoice
public interface IPaidPrintCustomer
{
string CustomerName { set; get; }
int CustomerCode { set; get; }
int ProductQuantity { set; get; }
double ProductRate { set; get; }
double CalculateDiscountRate();
string PrintInvoice(double _amount);
}
public class ClsPaidCustomerSalesEnity : IPaidPrintCustomer
{
//do the implementation as same as we did for customer
}
public class ClsFreeCustomerSalesEnity : IFreeCustomer
{
//do the implementation as same as we did for customer
}
Then create separate objects and call it in UI
So Problem is solved successfully
Another Example
Recently we’ve run across a violation to that principle in a project. We have an interface defined like so:
It’s a very small interface that represents resources that can be loaded in memory, and persisted afterwards in case there were changes to it.
Let’s pretend we have the following implementations of that interface:
The actual implementation of those methods doesn’t matter here; just assume that the real implementation loads and persists application and user settings.
Somewhere in the application we have some way to retrieve a list of instances of implementations of that interface, kind of like this:
Some place else, we have a method that takes in a list of those objects, and call Persist on them:
And somewhere else we may use those methods, like so:
Everything works great, until a new class is added to the system in order to handle, let’s say, some “special settings”:
It looks like the Load method does whatever stuff it’s supposed to do in order to handle loading these special settings. The Persist method, on the other hand, throws a NotImplementedException. As it turns out, those settings are meant to be read-only, therefore, the Persist method can’t really do anything.
The system is told to load the new class along with the other ones that implement that same interface:
Now when we run the app everything should still work fine, until we hit the code that tries to persist all of those loaded resources, at which point we get a big and fat “NotImplementedException”.
One (horrible) way to address this would be to change the SaveAll method:
If the specific resource being processed is of type SpecialSettings, we skip that one. Brilliant! Well, maybe not. Let’s look back at a simplified definition of the Liskov Substitution Principle:
“An object should be substitutable by its base class (or interface).”
Looking at the SaveAll method it should be clear that “SpecialSettings” is NOT substitutable by its “IPersistedResource” interface; if we call Persist on it, the app blows up, so we need change the method to take that one problem into consideration. One could say “well, let’s change the Persist method on that class so it won’t throw an exception anymore”. Hmm, having a method on a class that when called won’t do what its name implies is just bad… really, really bad.
Write this down: anytime you see code that takes in some sort of baseclass or interface and then performs a check such as “if (someObject is SomeType)”, there’s a very good chance that that’s an LSP violation. I’ve done that, and I know so have you, let’s be honest.
Another great definition for LSP comes from this motivational poster that the folks at Los Techies put together:
The SaveAll takes in something tailored to its needs, IPersistResource’s, and the same goes for LoadAll, which only cares about ILoadResource’s (in the real app, the actual instantiation of these classes happen somewhere else). This is what the granular new interfaces look like:
Yup, it’s pretty much the former “IPersistedResource” split up into two separate interfaces, tailored to their client needs. Both the UserSettings and ApplicationSettings classes can implement these two interfaces, whereas the SpecialSettings class would only implement ILoadResource; this way, it isn’t forced to implement interface members it can’t handle.
Very often people ask what’s the most appropriate number of members in an interface. In the real world example I gave here, the original interface had only 2 members; one could say that was small enough, but as it turns out, it wasn’t. The IPersistedResource interface was doing too much (both loading *and* persisting stuff) based on the clients that use its implementers. In the end, two interfaces with a single method on them fit the bill a lot better.
Another Example
Recently we’ve run across a violation to that principle in a project. We have an interface defined like so:
It’s a very small interface that represents resources that can be loaded in memory, and persisted afterwards in case there were changes to it.
Let’s pretend we have the following implementations of that interface:
The actual implementation of those methods doesn’t matter here; just assume that the real implementation loads and persists application and user settings.
Somewhere in the application we have some way to retrieve a list of instances of implementations of that interface, kind of like this:
Some place else, we have a method that takes in a list of those objects, and call Persist on them:
And somewhere else we may use those methods, like so:
Everything works great, until a new class is added to the system in order to handle, let’s say, some “special settings”:
It looks like the Load method does whatever stuff it’s supposed to do in order to handle loading these special settings. The Persist method, on the other hand, throws a NotImplementedException. As it turns out, those settings are meant to be read-only, therefore, the Persist method can’t really do anything.
The system is told to load the new class along with the other ones that implement that same interface:
Now when we run the app everything should still work fine, until we hit the code that tries to persist all of those loaded resources, at which point we get a big and fat “NotImplementedException”.
One (horrible) way to address this would be to change the SaveAll method:
If the specific resource being processed is of type SpecialSettings, we skip that one. Brilliant! Well, maybe not. Let’s look back at a simplified definition of the Liskov Substitution Principle:
“An object should be substitutable by its base class (or interface).”
Looking at the SaveAll method it should be clear that “SpecialSettings” is NOT substitutable by its “IPersistedResource” interface; if we call Persist on it, the app blows up, so we need change the method to take that one problem into consideration. One could say “well, let’s change the Persist method on that class so it won’t throw an exception anymore”. Hmm, having a method on a class that when called won’t do what its name implies is just bad… really, really bad.
Write this down: anytime you see code that takes in some sort of baseclass or interface and then performs a check such as “if (someObject is SomeType)”, there’s a very good chance that that’s an LSP violation. I’ve done that, and I know so have you, let’s be honest.
Another great definition for LSP comes from this motivational poster that the folks at Los Techies put together:
So what’s the fix?
The fix here is to tailor the interface based on what each client needs (Interface Segregation Principle, or ISP). The LoadAll method (which is one client of those classes) is really only concerned about the “Load” capability, whereas the “SaveAll” method (another client) is only concerned about the “Persist” capability. In other words, these is what those clients need:The SaveAll takes in something tailored to its needs, IPersistResource’s, and the same goes for LoadAll, which only cares about ILoadResource’s (in the real app, the actual instantiation of these classes happen somewhere else). This is what the granular new interfaces look like:
Yup, it’s pretty much the former “IPersistedResource” split up into two separate interfaces, tailored to their client needs. Both the UserSettings and ApplicationSettings classes can implement these two interfaces, whereas the SpecialSettings class would only implement ILoadResource; this way, it isn’t forced to implement interface members it can’t handle.
Very often people ask what’s the most appropriate number of members in an interface. In the real world example I gave here, the original interface had only 2 members; one could say that was small enough, but as it turns out, it wasn’t. The IPersistedResource interface was doing too much (both loading *and* persisting stuff) based on the clients that use its implementers. In the end, two interfaces with a single method on them fit the bill a lot better.
No comments:
Post a Comment