I've been reading about the Liskov Substitution Principle (LSP) and I'm a little confused on how you adhere to it correctly. Especially when interfaces and subclasses are being used.
For example, if I have a base class:
public abstract class AccountBase
{
private string primaryAccountHolder;
public string PrimaryAccountHolder
{
get { return this.primaryAccountHolder; }
set
{
if (value == null) throw ArgumentNullException("value");
this.primaryAccountHolder = value;
}
}
public string SecondaryAccountHolder { get; set; }
protected AccountBase(string primary)
{
if (primary == null) throw new ArgumentNullException("primary");
this.primaryAccountHolder = primary;
}
}
Now let's say I have two accounts that inherit from the base class. One that REQUIRES the SecondaryAccountHolder. Adding a null guard to the sub-class is a violation of LSP, correct? So how would I design my classes in such a way that they don't violate LSP but one of my sub-classes requires a secondary account holder and one does not?
Compound the question with the fact that there could be tons of different types of accounts and they'll need to be generated through a factory or factory that returns a builder or something.
And I have the same question with interfaces. If I have an interface:
public interface IPrintsSomething
{
void PrintSomething(string text);
}
Wouldn't it be a violation of LSP to add a null guard clause for text on any class that implements IPrintsSomething? How do you protect your invariants? That is the correct word right? :p
The way out of this problem is by surfacing this variability to the contract of the base class. It may look like this (unnecessary implementation details left out):
Then you are not violating the LSP, because the user of
AccountBase
can determine whether they have to or have not to provide the value ofSecondaryAcccountHolder
.Make the validation an obvious part of the interface's contract. How? Document, that the implementor must chek the value of
text
fornull
.