Question

I have searched for this question but could not find a good answer for my case.

Suppose I have a SuperClass for a bank loan with properties Amount and InterestRate.

public class BankLoan{
   public double Amount {get; set;}
   public double InterestRate {get; set;}
}

Then I create a derived class called SmallLoan, which will have a variable InterestRate based on its amount.

public class SmallLoan{
   public SmallLoan(){
     InterestRate = CalculateInterest();
   }

   private double CalculateInterest(){
      if(Amount < 100)
         return 0.10;
      else
         return 0.05;
   }
}

Would this be violating the LSP? I would think that it doesn't, because even if an instance of SmallLoan tries to call CalculateInterest outside of the class definition, it's going to throw an error, not related to the substitution but simply because it has a different access modifier. Even if only BankLoan existed and it had the same private method, a call to it from the outside would also result in an error.

But I can see how some people would say that the presence of the error in general is a violation of the principle.

Lastly: I am aware that these classes might not be the best and brightest implementations of inheritance or even just basic class design, but the question isn't about that, I merely want to know if private methods are a violation of LSP, and I chose to illustrate it with this very simple example.

Was it helpful?

Solution

Let's start with a definition: 'if S is a subtype of T, then objects of type T may be replaced with objects of type S'

The fact that S or T may or may not have a private method that handles part of the behavior for the public interface in itself can not be a violation of the above principle. Since (normally) private methods are invisible and cannot be called from the outside, they are not part of the interface and clients cannot depend on it. Therefor the fact that one type has delegated some of it's inner workings to a private method, does not hinder the substitution of objects of this type for objects of it's super or derived type.

Note that I'm not saying that your code example isn't violating LSP. LSP is also about correct behavior and the way you set InterestRate in the constructor of SmallLoan with a reference to Amount which is null at that point could be considered a violation. But let's ignore this, since you said the question is not about the implementation of your example, but simply whether private methods are a LSP violation.

OTHER TIPS

If you were to use a BankLoan object in code somewhere, the reference could be replaced with an implementation of SmallLoan (since a SmallLoan is a BankLoan), and the application itself would not know any difference. The private methods inside of the implementation would not matter.

However, if you have a SmallLoan object in code, you would not be able to replace the reference with a BankLoan implementation (since a BankLoan is not a SmallLoan). This is where the violation would occur. The two instances cannot equally replace each other.

In order to take advantage of the LSP, you would instead want something along the lines of an interface, such as an ILoan, that would describe the behavior/properties that all implementations would need. At that point, a ILoan reference could be replaced with anything that implements the ILoan interface. If SmallLoan and BankLoan both implemented the interface, they could easily replace each other at the reference level. Private methods would have no effect on that.

I may be wrong on my interpretation of it, but the LSP cares about having expected behaviors/properties, rather than on how they are implemented.

Clearly if I have an object with a property “InterestRate” with a getter and setter then I expect that calling the setter to change the value returned by the getter and the interest calculation. Your “SmallLoan” class either doesn’t meet that behaviour are it behaves very strangely. That’s where your violation is.

You could have a baseclass “GenericLoan” with only a public getter and a protected setter for the interest rate, two subclasses “BankLoan” and “SmallLoan”, with “BankLoan” having a public setter. You can’t set the interest rate on a generic loan because not all subclasses understand it.

PS. Private methods or properties are just an implementation detail. They don’t have anything to do with LSP at all. Public methods in a subclass still have nothing to do with LSP, because they can only be called if you have a reference to a subclass object. At some point there must be a difference, otherwise why would you want a subclass if it is the exact same as the base class?

No private methods do not break LSP because the subclass still contains the base class methods and attributes, so we can still use SmallLoan as BankLoan but not BankLoan as SmallLoan without using a cast

Licensed under: CC-BY-SA with attribution
scroll top