SOLID : Liskov Substitution Principle (Part 2)

In the first part of the post, we visited the core definition of Liskov Substitution principle and took time to understand the problem using an example. In this second part, we would take time to understand some of the governing rules of the principle.

The rules that needs to be followed for LSP compliance can be broadly categorized into mainly two categories.

  • Contract Rules
  • Variance Rules

Let us examine the Contract Rules first.

Contact Rules
The three contact rules are defined as follows.
* Preconditions cannot be strengthened in a subtype.
* Postconditions cannot be weakened in a subtype.
* Invariants—conditions that must remain true—of the supertype must be preserved in a subtype

Let us examine each of them breifly.

Preconditions cannot be strengthend in a subtype.

Preconditions are conditions that are necessary for a method to run reliably. For example, consider a method in a school application, which returns the count of students above specified age.

public virtual int GetStudentCount(int minAge)
{
if(minAge <=0)
throw new ArgumentOutOfRangeException("Invalid Age");
return default; // For demo purpose
}

As observed in the above method, a non-positive value for age needs to be flagged as an exception and method should stop executing. The method does this check as the first step before it executes any logic related to the funcationlity of the method and thereby preventing the method execution unless the parameters are valid. This is a typical example of preconditions. There are other ways to add preconditions, but that would be out of scope of the current context.

So what happens if the precondition is strengthed. Let us assume that the application for Kindergarden for the school derieves from the same class, but adds another precondition.

public override int GetStudentCount(int minAge)
{
if(minAge <=0) throw new ArgumentOutOfRangeException("Invalid Age"); if(minAge >=5)
throw new ArgumentOutOfRangeException("Invalid Age");
return default; // For demo purpose
}

This creates a conflict in the calling class. The caller was invoking the method from the base Class, it would not throw an exception if the value is greater then 5. However, if the caller was invoking the method from the Child Class, it would now throw an exception, thanks to the new precondition we have added. Let’s demonstrate the same using Unit Tests.

[Test]
[TestCaseSource(typeof(Preconditions), nameof(TestCases))]
public void GetStudentCount(School school,int age)
{
Assert.GreaterOrEqual(school.GetStudentCount(age), 0);
}

public static IEnumerable TestCases
{
get
{
yield return new TestCaseData(new School(), 7);
yield return new TestCaseData(new Kindergarden(), 7);
}
}
}

The Unit Tests show that the strengthening of Preconditions would now indirectly force the caller to violate the fact that it should not make any assumptions about the type it is acting on (and thereby violating Open Closed Principle).

Post Conditions cannot be weakend in a subtype

During the execution of the method, the state of the object is most likely to be mutated, opening the door of possiblity that the method might leave the object in an invalid state due to errors in the method. Post Conditions ensures that the state of the object is in a valid state when the method exits.

Much similiar to the Preconditions, the Post Conditions are commonly implemented using Guard clauses, which ensure the state is valid after the execution of logic (any code that might alter the state of the object) associated with the method. For the same reason, the Post Conditions are placed at the end of the method.

The behavior of Post Conditions in sub-classes is the complete opposite of what happens with Pre Conditions. The Post Conditions, if weakened, could l;ead to existing clients to behave incorrectly. Let us explore a scenario. We will write the Unit Tests first.

[Test]
[TestCaseSource(typeof(Postconditions), nameof(TestCases))]
public void TestLabFeeForSchool(School school, long studentId)
{
Assert.Greater(school.CalculateLabFees(studentId),0);
}

public static IEnumerable TestCases
{
get
{
yield return new TestCaseData(new School(), 7);
}
}

The above Test Case ensures that the CalculateLabFees method returns a positive and non-zero value as result. Let us assume that School.CalculateLabFees method was defined as following.

public class School
{
public virtual decimal CalculateLabFees(long studentId)
{
var labFee = MockMethodForCalculatingLabFees(studentId);

if (labFee <= 0)
return 1000;
return labFee;
}

private decimal MockMethodForCalculatingLabFees(long studentID)
{
return default;
}
}

The Post Condition in the base class ensures that if the labFee is less than or equal to zero, a default value of 1000 is returned. The Test Case would pass successfully with the instance of class. The CalculateLabFees method would return a positive, non-zero value as expected by the Unit Test.

Now, let us assume the Kindergarden Implementation. The Kindergarden doesn’t have Lab Fees and hence, would return a default value 0 for the CalculateLabFee method.

public class Kindergarden : School
{
public override decimal CalculateLabFees(long studentId)
{
return 0;
}
}

As you can notice the Post Condition has been weakened in the Child Class. This would cause issues with client which would expect a positive response, as with the Unit Test Case. This highlights another instance where significance of honoring LSP is highlighted.

Invariants must be maintained

An invariant describes a condition of a process that remains true before the process begins and after the process ends, until ofcourse the object is out of scope.

Consider the following base class.

public class Student
{
public string Name{ get; set; }

public void CheckInvariants()
{
if (string.IsNullOrEmpty(Name))
Name = "Name Not Assigned";
}
public virtual void AssignName(string name)
{
CheckInvariants();
Name = name;
CheckInvariants();
}
}

The class requires the Name property to be always have a value. The CheckInvariants method assigns a default value if the clause is violated. Let us assume we have an sub class NurseryStudent.

public class NurseryStudent : Student
{
public override void AssignName(string name)
{
Name = name;
}

}

The author of NurseryStudent forgots to ensure the Invariants holds true after the AssignName exits. This is a violation of LSP as a client using the SuperClass would expect the Name to always have a value. Consider the following code.

[Test]
[TestCaseSource(typeof(Invariants), nameof(TestCases))]
public void TestInvariants(Student student,string name)
{
student.AssignName(name);
Assert.Greater(student.Name.Length, 0);
}

public static IEnumerable TestCases
{
get
{
yield return new TestCaseData(new Student(), string.Empty);
yield return new TestCaseData(new NurseryStudent(), string.Empty);
}
}

The Unit Test Case would fail

Let’s write the Unit Test Case before writing the example for sub class.

[Test]
[TestCaseSource(typeof(Invariants), nameof(TestCases))]
public void TestInvariants(Student student,string name)
{
student.AssignName(name);
Assert.Greater(student.Name.Length, 0);
}

public static IEnumerable TestCases
{
get
{
yield return new TestCaseData(new Student(), null);
yield return new TestCaseData(new NurseryStudent(), null);
}
}

The Test Case would fail when an instance of NurserStudent is passed to the method as the Name property would have an unexpected value of null when leaving the AssignName method. This would cause exceptions in all clients which expects the behavior of invariants are retained by any subclass of the Student.

In this post we explored the Contract Rules for LSP. In the part, we would explore the variance rules involved for conforming with LSP. All code samples given in the post are available in my Github

SOLID : Liskov Substitution Principle (Part 1)

The “L” in SOLID stands for Liskov Substitution Principle, named after Barbara Liskov, who initially introduced in 1987 keynote address. LSP provides guidelines for creating inheritence heirachy where a client can use any class or subclass without breaking the expected behavior.

We will begin by looking at the official definition first.

If S is a subtype of T, then objects of type T may be replaced with objects of type S,without breaking the program.

It would be unwise to explain this theoratically, hence like always, let hit the Visual Studio and write some code. I would like borrow an example given by Tim Correy in his famous video.

Assume we have an Employee class defined as follows.

public class Employee
{
public string Name{get;set;}
public Employee AssignedManager{get;protected set;}
public virtual double GetPay()
{
var currentSalary = 500;
Console.WriteLine($"Current Pay {currentSalary}");
return currentSalary;
}

public virtual void AssignManager(Employee manager)
{
Console.WriteLine("Manager Assigned");
AssignedManager = manager;
}
}

Now let’s assume we have a Manager class, which inherits from Employee

public class Manager:Employee
{
public override double GetPay()
{
var currentSalary = 750;
Console.WriteLine($"Current Pay {currentSalary}");
return currentSalary;
}

public override void AssignManager(Employee manager)
{
Console.WriteLine("Manager Assigned");
AssignedManager = manager;
}
}

Let’s write some client code now which consumes these classes.

var manager = new Manager();
manager.Name = "John Doe";
manager.GetPay();

var programmer = new Employee();
programmer.Name = "Roel Doel";
programmer.GetPay();
programmer.AssignManager(manager);

Output

Current Pay 750
Current Pay 500
Manager Assigned

As you can observe, the output is perfectly fine. Let us try making some changes to the Client Code now. Let us replace the Employee class (programmer instance) with Manager.

var manager = new Manager();
manager.Name = "John Doe";
manager.GetPay();

var programmer = new Manager();
programmer.Name = "Roel Doel";
programmer.GetPay();
programmer.AssignManager(manager);

Output

Current Pay 750
Current Pay 750
Manager Assigned

This has turned out fine as well. Yes the output is different, which is expected considering the Pay for Manager and Employee are different, but the client hasn’t behaved unexpectedly. Let’s write one more class, inheriting from Employee and call it CEO.

public class CEO:Employee
{
public override double GetPay()
{
var currentSalary = 750;
Console.WriteLine($"Current Pay {currentSalary}");
return currentSalary;
}

public override void AssignManager(Employee manager)
{
throw new Exception("Am the CEO, no Manager for me !!");
}
}

The difference this time is of course the CEO doesn’t have a Manager and it raises an exception in the AssignManager method. Let’s head back to out Client code and replace Employee with CEO in our original code.

var manager = new Manager();
manager.Name = "John Doe";
manager.GetPay();

var programmer = new CEO();
programmer.Name = "Roel Doel";
programmer.GetPay();
programmer.AssignManager(manager);

Output

Exception:Am the CEO, no Manager for me !!

Aha, we have an exception here. When replaced the Employee (Base type) with a subtype (CEO), the expected behavior of program has been broken. This is a violation of LSP.

Let’s examine the violation further. It is often said that Inheritence is the IS-A relation. However, if you truely examine CEO and Employee, do they follow a IS-A relation in every sense ? Not really, not according the current definition of Employee. Yes, that is right. The problem doesn’t quite lie in CEO, but it originate from the definition of Employee class. While designing the Employee class, we “assumed” that all Employees would have a manager.

So what is the best solution for this scenario ? What if we were to ensure the Employee class had only those functionalities which are common to every employee ? Obviously, the Manager is not applicable for everyone, so let’s remove it and create an interface which contains only the basic employee functionalities.

public interface IBaseEmployee
{
string Name{get;set;}
double GetPay();
}

public abstract class BaseEmployee:IBaseEmployee
{
public string Name{get;set;}
public virtual double GetPay(){ return 0;}
}

Remember, we still have to consider the Manager assignment. What we would do here is define another interface IHasManager, which would wrap the functionalities required for Employees who has Manager.

public interface IHasManager
{
IBaseEmployee AssignedManager{get;set;}
void AssignManager(IBaseEmployee manager);
}

Let’s now define our Employee and Manager classes.

public class Employee:BaseEmployee,IHasManager
{
public IBaseEmployee AssignedManager{get; set;}
public override double GetPay()
{
var currentSalary = 500;
Console.WriteLine($"Current Pay {currentSalary}");
return currentSalary;
}

public virtual void AssignManager(IBaseEmployee manager)
{
Console.WriteLine("Manager Assigned");
AssignedManager = manager;
}

}

public class Manager:BaseEmployee,IHasManager
{
public IBaseEmployee AssignedManager{get; set;}
public override double GetPay()
{
var currentSalary = 750;
Console.WriteLine($"Current Pay {currentSalary}");
return currentSalary;
}

public void AssignManager(IBaseEmployee manager)
{
Console.WriteLine("Manager Assigned");
AssignedManager = manager;
}
}

As you can see, the Manager and Employee inherits from BaseEmployee and implements the IHasManager interface. The next step is to define our CEO Class, and this time, we know that he doesn’t need a Manager.

public class CEO:BaseEmployee
{

public override double GetPay()
{
var currentSalary = 750;
Console.WriteLine($"Current Pay {currentSalary}");
return currentSalary;
}

}

That’s it. Let’s rewrite our Client code now.

var manager = new Manager();
manager.Name = "John Doe";
manager.GetPay();

var programmer = new Employee();
programmer.Name = "Roel Doel";
programmer.GetPay();
programmer.AssignManager(manager);

The above code compiles and executes as expected. Let’s replace the Employee with a Manager first.

var manager = new Manager();
manager.Name = "John Doe";
manager.GetPay();

var programmer = new Manager();
programmer.Name = "Roel Doel";
programmer.GetPay();
programmer.AssignManager(manager);

That works fine as well. Let’s try again, and this time replace with CEO.

var manager = new Manager();
manager.Name = "John Doe";
manager.GetPay();

var programmer = new CEO();
programmer.Name = "Roel Doel";
programmer.GetPay();
programmer.AssignManager(manager);

Not surprisingly, that doesn’t compile at all. CEO doesn’t have the AssignManager method as it doesn’t implement the IHasManager interface. Your client no longer have unexpected behavior as such errors are caught during compilation.

That was pretty easy, isn’t it ? But we are not done yet. There is more to the definition of LSP. In the next part of this series, we will examine the Contact and Variance rules which must be followed for LSP compliance.

GOF: Composite Pattern

Composite Pattern, in a way, literally takes of from where Decorator Pattern ends. One might be inclined to think of it as a specialized case of Decorator Pattern which composes one or more instances of similiar objects and manipulate them uniformly. This allows the client to work on a single entity or composition of entity uniformily, or in other words, it eliminates the need of programmers to discriminate between leaf and branch, and thereby avoid writing specific logic to handle both scenarios.

The key components of Composite Pattern can be defined as follows

Component
Components defines the interface that specifies the behavior that needs to be uniformly executed by primitive object or composite object.

Leaf
The non-decomposible leaf object, which defines the behavior for the primitive object

Composite
Holds the collection of Child components, and implements the behavior of components that have children.

That would be enough of theory, let’s write some code now. For the sake of example, we will write a little program that would print the size of given File/Directory. Let’s begin by defining the Component interface.

public interface IMetaInfo
{
double GetSize();
}

The next step would be define the behavior of the primitive object, which in this case would be the File.

public class FileObject : IMetaInfo
{
private string _fileName;
public FileObject(string fileName)
{
_fileName = fileName;
}
public double GetSize()
{
Console.WriteLine($"Calculating size of {_fileName}");
return 0;
}
}

As one can understand, the actual behavior is different for File and Directory. The size of Directory needs to be cumiliative size of all the individual Files in it. Let’s define the behavior for Directory Class now.

public class DirectoryObject : IMetaInfo
{
private string _directoryName;
private List<IMetaInfo> _fileCollection;
public DirectoryObject(string directoryName)
{
_directoryName = directoryName;
_fileCollection = new List<IMetaInfo>();
}

public void Add(IMetaInfo file)
{
_fileCollection.Add(file);
}

public void Remove(IMetaInfo file)
{
_fileCollection.Remove(file);
}
public double GetSize()
{
var directorySize = 0d;
foreach (var file in _fileCollection)
{
directorySize += file.GetSize();
}
return directorySize;
}
}

As one could see in the example code, the Directory Structure uses the bahavior of the individual File Structure to calculate the cumiliative Size. Let’s write the code for Client now and see how client can work without having the knowledge of what exactly it is dealing with.

class Client
{
static void Main(string[] args)
{
Console.WriteLine("When using Composite Object");
var directoryInstance = new DirectoryObject("BaseDirectyory");
for(var i = 0; i < 10; i++)
{
directoryInstance.Add(new FileObject($"{i.ToString()}_File"));
}

GetSize(directoryInstance);

Console.WriteLine("When using Component Object");
var fileInstance = new FileObject("Another File Instance");
GetSize(fileInstance);
Console.ReadLine();
}

private static void GetSize(IMetaInfo instance)
{
instance.GetSize();
}
}

The Client.GetSize() method accepts an instance of IMetaInfo as the parameter. It doesn’t really know whether it is actually dealing with a Composite object or a Primitive one.

The Composite pattern is a highly useful pattern when one needs to deal with a irregular structure of objects and composite of objects, enabling the client to deal with them uniformly.

The entire source code shown in this example are available in my GitHub.

Law Of Demeter

If you ask any Software Developer, he would vouch that separation of concerns is the fundamental principles for maintaining any complex system and one of the vital links for achieving Separation of Concerns is decoupling.

Law of Demeter (LoD), also known as Principle of least knowledge, intends to reduce coupling between objects as proposed by Ian Holland.

Definition

The original version is outlined as follows.

Each unit should have only limited knowledge about other units: only units “closely” related to the current unit. Or: Each unit should only talk to its friends; Don’t talk to strangers.

The gist of the definition lies in the simple fact that the class should speak only to objects that it directly knows, or in other words, do not talk to strangers.

Let’s examine a real life scenario. You are out to stroll with your favourite pet. You do not order your pet’s legs to walk. Instead, you order your pet, which inturns commands its legs to move.

If we were to examine the rule from the perspective of an Object Oriented Programming Language,

A Method M, of an Object of Type C, may only call methods of
a) Class C
b) Parameters of method M
c) Instance variables of C
d) Objects created by M
e) Global Variables

Let’s write some code to replicate the Pet Scenario we discussed earlier.

public class Owner:IHuman,IHasPet
{
public string Name{get;set;}
public Pet Pet{get;set;}
public void MovePet()
{
Pet.GetLegs().Move();
}
}

public class Pet
{
private Legs _legs;
public Legs GetLegs ( )=>_legs;
}

public class Legs
{
public void Move()
{

}
}

As observed, the Owner Class uses instance of Pet to retrieve instance of Legs, and then request them to move.But, there is no reason the owner needs to be have access or knowledge of the legs or how the Pet commands the legs to move. This is the responsibility of the Pet class and should not be exposed the owner class.

In the above scenario, the Owner class has knowledge of something, which it doesn’t instantiate or should knowledge of and is a clear violation of Law of Demeter.

We can rewrite the classes to overcome the problem.

public class Owner:IHuman,IHasPets
{
public string Name{get;set;}
public Pet Pet{get;set;}
public void MovePet()
{
Pets.Move();
}
}

public class Pet
{
private Legs _legs;
public void Move()
{
_legs.Move();
}
}

In fact, if you look back at Gang Of Four patterns, many of the patterns discretely follows the Law Of Demeter.

A Word of Caution.

While the benefits of following the rule is evident, one also needs to be beware of focusing way too much on adherence to the rule, rather than fulfilling the purpose of the rule, which is to reduce coupling.

Code Smells : Dispensable

Continuing on our discussion on Code Smells and Refactoring techniques, we will discuss Dispensable in this post.

Dispensables are avoidable components whose absence would make the code much more cleaner, readable and efficient.

Comments

A joke is not a good one if needed to be explained. A similar philosophy holds for code as well. If you feel that the code requires comments to understand, then it is better to refactor  it such a way that it eliminates the need of comments.

You could start with refactoring techniques like Rename Method, Rename Variable, along Extract Variable and Extract Method. Quite often using asserts (introduce assertions) can also aid in increasing the readability of the code.

Duplicate Code

When you have a larger team, there is a distinct possibility that two developers working different parts of the project might end up developing similar/same method. The similarity could could be subtle as well, where similarity is visible to only in certain parts of code, while performing the same job.

Extract Method, Extract superclass and Extract class are some of the refactoring methods that can be applied. If two methods do same job but using different algorithms, refactoring techniques like Substitute algorithm (Strategy Pattern) can be applied. Similarly, if the duplicated code is only visibly similar, but isn’t quite identical, Form Template Method technique (Template Method Pattern) can be applied.

Lazy Class

There could exist classes in your code which has become obsolete or the functionalities are near-useless, you could employee techniques like Inline Class to remove it.

Maintaining code is expensive and any unused code should be carefully eliminated.

Data Class

Classes are identified by two factors – Properties and Behaviours.  If you stumble upon a class that has only properties, then it is a good candidate for Data Class code smell.

 The first step you need to take is to review the client code that uses the class, and review if the functionality could exist in the Data Class itself. You could also review if the conventional collection such as Arrays could be used to store the Data in the class, thus eliminating the need for class.

Dead Code

Dead Code is often resultant of changing requirements/specifications, and a development team that was unsure/lazy to clean up the code. A good IDE such as the Visual Studio can aid in detecting such code and can be eliminated.

Remember, it doesn’t need to be a method, the dead code could also be a unused parameter, which can be eliminated using the Remove Parameter refactoring technique.

Speculative Generality

How often have we been guilty of creating functionality in anticipation of future requirements, but ended up never having to use them ? It is a common trait in teams, which is highly infectious and needs to be avoided. XP Principles like YAGNI, KISS can be highly useful in this regard in educating the team to avoid such pitfalls.

Refactoring techniques such as Collapse Hierarchy an Inline class can be used for eliminating such code.

Design Pattern : Memento Pattern

Memento Design Pattern allows to capture internal state of an object in an external object and restore it to the desired checkpoint when required, all the while being in compliance with encapsulation.

The Memento Design Pattern comprises of 3 actor classes.

  • Originator : The class which needs to have the ability to store its internal state and restore itself to checkpoint when required.
  • Memento : Contains the internal state of Originator.
  • Caretaker : The shepherd class who is in charge of storing and restoring Originator from Memento.

Let’s go ahead and implement the actor classes

Classic Approach

public class Memento
    {
        public string FName { get; set; }
        public string LName { get; set; }

    }

 public class Caretaker<TSource>
    {

        private IList<Memento> _stateMemory;

        public Caretaker()
        {
            _stateMemory = new List<Memento>();
        }

        public Memento GoBack(int level) =>  _stateMemory.Reverse().Skip(level-1).First();

}

 public class Originator: IOriginator
    {
        public string FName { get; set; }
        public string LName { get; set; }

        public void RestoreState(Memento memento)
        {
            this.FName = memento.FName;
            this.LName = memento.LName;
        }

        public Memento Save()
        {
            return new Memento() {
                LName = this.LName,
                FName = this.FName
            };
        }

    }

The above code showcases the implementation of Memento in its simplest form. You could step up things by making the Memento generic so that it can be reused with other classes as well.

Generic Memento with Serialization.

Instead of defining each properties of the Originator class, we could create a completely generic version by utilizing the powers of serialization.

In the following example implementation of Memento class, I have used ProtoBuf for serializing the class.

public class Memento<TSource>
    {
        private byte[] _state;

        public Memento(TSource data)
        {
            using(var stream = new MemoryStream())
            {
                ProtoBuf.Serializer.Serialize<TSource>(stream, data);
                _state = stream.ToArray();
            }
        }

        public TSource Value
        {
            get
            {
                using(var stream = new MemoryStream(_state))
                {
                    return ProtoBuf.Serializer.Deserialize<TSource>(stream);
                }
            }
        }
    }

The entire source code shown in this example are available in my GitHub.

Stairway Pattern

How do you feel when you invite your friend for a private family function and he brings along a dozen strangers (his friends) ?

 

Entourage Anti-Pattern

This is similar to the scenario when you want to add a library as reference and in turn ends up referring a different set of libraries which you are not directly dependent. This scenario is perfectly defined by the Entourage Anti-Pattern. The Entourage pattern also describes the (avoidable) practice of packaging the abstraction and implementation in the same assembly.

The Stairway Pattern

The stairway pattern describes a better alternative by organizing your abstractions and implementations in separate assemblies, which in turn allows both to evolve and vary independently.

It also means the references now doesn’t bring an entire entourage of assemblies along. One of the thumb rules you can adhere too is to ensure the abstraction assembly doesn’t depend on any external references.