Published On: September 5th, 20144.6 min read

Contributor: Sajid Rabbani, Software Engineer, Nascenia

It has been a long gap since I have written the part I. In the last part, I mentioned the necessity of developing a system with low coupling, high cohesion, and strong encapsulation. And how the SOLID object-oriented principles are assisting the developers on that purpose.

Walking on water and developing software from a specification are easy if both are frozen.” – Edward Berard

Change in the software specification is a very common property of software development. Starting from the initial specification to the last end product the specification of a software will change. So the system has to be robust and agile so that it can be extended or modified easily. SOLID object-oriented principles guide developers to develop a good software.

Open-Closed Principle

The mantra of open-closed principle is – ‘A class should be open for extension, but closed for modification.’ Sounds contradictory? Well in simple words, you will be able to change the behavior of the class easily without having to modify it. In a hardware store, you will notice that there is a wide range of saw blades that can attach to a single saw. One blade compared to another may look different with different behaviors. One blade may cut through wood extremely quickly, but leave the edges rough. Another blade may cut wood more slowly and leave the edges smooth. Some may be suited for cutting metal. The wide variety of blades, combined with the common method of attaching them to the saw, allows you to change the behavior of the saw without having to modify the mechanical portion of the saw.

Now how do you construct a class whose behavior to be modified without actually modifying the class? The answer is simple and it has several solutions. One easy way is to use interface.

Suppose, you are told to design a payment section of a whole system. Currently, there are two ways of payment, Visa/Mastercard & Paypal. But there is a possibility of adding new payment methods to the system. So now how will you design the system?

First of all, create the interface for payment.

public interface IPayment{
public void pay(int amount);

You can create concrete implementations of algorithms for payment using a credit card and PayPal.

Credit Card:

public class CreditCard : IPayment{

public void pay(int amount){
Console.writeline('Paid with Credit Card');


public class Paypal : IPayment{

public void pay(int amount){
Console.writeline('Paid with Paypal');

You can design the shopping cart for the system using that payment.

public class ShoppingCart{

public void payment(IPayment paymentMethod){
int amount = 50;;

Now, let’s test our setup with a sample program.

public class PaymentTest{

public static void Main(string [] args){
ShoppingCart cart = new ShoppingCart();

cart.payment(new Paypal());
cart.payment(new CreditCard());

The output of the above program is:

Paid with Paypal
Paid with Credit Card

The solution code has maintained the open-closed principle very well. So now if there is a requirement for adding a new payment method like Payoneer says for example, in the current design just adding a concrete class Payoneer implementing the IPayment interface will solve the problem. So the ShoppingCart class is not needed to be modified. But a new behavior is added to it. This is the open-closed principle.

Liskov Substitution Principle

The Liskov Substitution Principle says that an object inheriting from a base class, interface or other abstraction must be semantically substitutable for the original abstraction. Even if the original abstraction is poorly named, the intent of the abstraction should not be changed by the specific implementations. And this requires a good understanding of the context of the interface.

To illustrate the principle we can consider two classes; one is a rectangle and another one is a square. In geometry, we know that all squares are rectangles. But all rectangles are not squares. Since a square is a rectangle, it seems intuitive that we could create a rectangle base class and have square inherit from that.

public class Rectangle{

public virtual int Height { get; set; }
public virtual int Width { get; set; }

public int Area(){
return Height * Width;
public class Square : Rectangle{

public override int Height{
get {return base.Height;}
set { base.Height = value;
         base.Width = value;

public override int Width{
get {return base.Width;}
set { base.Height = value;
         base.Width = value;

Now, let’s test our sample program.

public class LSPTest{

public static void Main(string [] args){
Rectangle rectangle = new Rectangle();
rectangle.Height = 4;
rectangle.Width = 6;
Console.writeline('The area of rectangle is:' + rectangle.Area());

Rectangle square = new Square();
square.Width = 6;
Console.writeline('The area of square is:' + square.Area());

The Output will be

The area of rectangle is: 24
The area of square is: 36

The square-rectangle issue illustrates a violation of the Liskov Substitution Principle. It clearly has the wrong abstraction to represent both a square and a rectangle. This is evidenced by the square overriding the height and width properties of the rectangle, and changing the expected behavior of the rectangle.

So what could be the solution to the problem? Well, in this case, we may use a simple Shape abstraction and provide only an Area method. Each specific implementation-square and rectangle would then provide their own data and implementation for area allowing us to create additional shapes such as circles, triangles, polygons. By limiting the abstraction to only what is common among all of the shapes, and ensuring that no shape has a different meaning for the area we can prevent Liskov Substitution Principle Violation.

Share it, Choose Your Platform!

More to Explore

The Quest for Knowledge Continues. Fuel Your Curiosity.