Dec 17, 2014

SOLID architecture principles using C# with examples



SOLID architecture principles help to create good software architecture; basically it has five principles known as SOLID.  SOLID is a short form you can see below the full form of SOLID:

S:  stands for SRP (Single responsibility principle)
O: stands for OCP (Open closed principle)
L:  stands for LSP (Liskov substitution principle)
I:   stands for ISP (Interface segregation principle)
D: stands for DIP (Dependency inversion principle)
Below I have started to describe each principle with c# examples

 “S”- SRP (Single responsibility principle)

It would be better if first we understand problem and try to resolve it using SOLID architectureprinciple.
See below code and think what may be exact problem here.
    public class Orders
    {
        public void SaveOrders()
        {
            try
            {
                // Do your database code to save order
                    
            }
            catch (Exception ex)
            {
                System.IO.File.WriteAllText(@"D:\Elog.txt", ex.ToString());
            }
        }
    }
The above Order class should do customer data validations, call the Order data access layer etc, but if you notice the catch block it is maintaining log activity. It means Order class over loaded with lot of responsibility.

If in catch blog we need to add a new logger like event viewer I need to go and change the “Order” class, that’s not a good practice.

If we make separate class for the logging “System.IO.File.WriteAllText(@"D:\Elog.txt", ex.ToString());” it would be better for future changes in log, It will not disturb to Order class.

SRP says class should be responsible for only one activity.
    class Log
    {
        public void ErrorHandle(string ex)
        {
            File.WriteAllText(@"D:\Elog.txt", ex.ToString());
        }
    }
Now Order class can easily use the logging activity to the “Log” class and he can give attention to Order related activities.

    public abstract class Order
    {
        Log oLog = new Log();
        public virtual void SaveOrder(decimal price)
        {
            try
            {
                //Save Order Goes Here
            }
            catch(Exception ex)
            {
                oLog.ErrorHandle(ex.ToString());
            }
        }
    }

 “O” - Open closed principle
I will continue with Order Example I have added a simple code regarding discount voucher, add voucher property to order class. This property decided user has “GoldCoupon” or “SilverCoupon” regarding this property we have to decide we give discount or not.


class Order
    {
        private string _voucherType;

        public string VoucherType
        {
            get { return _voucherType; }
            set { _voucherType = value; }
        }

        public double Discount(decimal totalPrice)
        {
            if (VoucherType == “GoldCoupon”)
            {
                return totalPrice - (totalPrice*10)/100;
            }
            else
            {
                return totalPrice - (totalPrice*5)/100;
            }
        }
       public double SaveOrder(decimal totalPrice)
{
           //Database Code here
}
    }
The problem is if we need to add a new Voucher type we need to go and add one more “IF” condition in the “Discount” function, in other words we need to change the Order class.

If we are changing the Order class again and again, we need to ensure that the previous conditions with new one’s are working fine or not and test again , also need to ensure existing user’s or client’s which are referencing this class are working properly as before.
Now we can see modification of current “Order” class code for every change and every time and also ensure that all previous functionalities are working as before.
Question is arise here,
How can we go for extension except modification of Order class?
Answer is we can add a new class for a voucher type, see below like this current code will untouched and in test we just analyse new class only.
public abstract class Order
    {
       
        public virtual decimal Discount(decimal totalPrice)
        {
            return totalPrice;
        }
        public virtual decimal VoucherEnquiry(decimal totalPrice, string VoucherType)
        {
            return totalPrice;
        }
        public abstract void SaveOrder(decimal price);
    }

    class GoldCoupon : Order
    {
        public override decimal Discount(decimal totalPrice)
        {
            return base.Discount((totalPrice * 10) / 100);
        }
        public override void SaveOrder(decimal price)
        {
            decimal totalPrice = Discount(price);
            //Database Code here
        }
    }
    class SilverCoupon : Order
    {
        public override decimal Discount(decimal totalPrice)
        {
            return base.Discount((totalPrice * 5) / 100);
        }
        public override void SaveOrder(decimal price)
        {
            decimal totalPrice = Discount(price);
            //Database Code here
        }
    }
Now one new Voucher type “BronzeCoupon ” need to add here, without any code change in existing classes we can add easily. See below.
    class BronzeCoupon : Order
    {
        public override decimal Discount(decimal totalPrice)
        {
            return base.Discount((totalPrice * 2) / 100);
        }
        public override void SaveOrder(decimal price)
        {
            decimal totalPrice = Discount(price);
            //Database Code here
        }
    }


 L - LSP (Liskov substitution principle)
I continuing with same example, Suppose discount Enquiries need to add in our system. If any user demanded for discount enquiry so we will not save it to our database we just give him info about discount.
Now we will create a new class named as “VchrEnquiry” which inherits from the “Order” class.
See bellow
class VchrEnquiry : Order
    {
        public override decimal VoucherEnquiry(decimal totalPrice, string VoucherType)
        {
            //code here for discount enquiry like GoldCoupon, SilverCoupon etc.

            return base.Discount((totalPrice * 2) / 100);
        }
        public override void SaveOrder(decimal price)
        {
            throw new Exception("This is not allowed");
        }
    }
Above code is for new class VchrEnquiry one problem we are facing here, we don’t need to save order in data base but as per abstract method rule we have to implement SaveOrder method so we will code like same.



So as per polymorphism rule my parent “Order” class object can point to any of it child class objects i.e. “GoldCoupon”, “SilverCoupon” or “VchrEnquiry” during runtime without any issues.

So for instance in the below code you can see I have created a list collection of “Customer” and thanks to polymorphism I can add “GoldCoupon” , “SilverCoupon” and “VchrEnquiry” user to the “Order” collection without any issues.

List<Order> Orders = new List<Order>();
            Orders.Add(new GoldCoupon());
            Orders.Add(new SilverCoupon());
            Orders.Add(new VchrEnquiry());
            Orders.Add(new BronzeCoupon());

            foreach (Order o in Orders)
            {
                o.SaveOrder(10);
            }
When you will run the code you will be find an error at SaveOrder method in VchrEnquiry class.

In other words the VchrEnquiry has discount calculation; it looks like Order class but it is not an Order. So the parent cannot replace the child object seamlessly. In other words Order class is not the actual parent for the VchrEnquiry class. VchrEnquiry is a different entity altogether. So LISKOV principle says the parent should easily replace the child object, so we need to create two interfaces one is for discount and other for save order into database, see below.

public interface ISaveOrder
    {
        void SaveOrder(decimal price);
    } 

public interface IDiscount
    {
        decimal Discount(decimal totalPrice);
    }
Now the VchrEnquiry class will only implement IDiscount.

class VchrEnquiry : IDiscount
    {
        public override decimal VoucherEnquiry(decimal totalPrice)
        {
            //your discount code here
            return 10;
        }
    }

Order class will implement both IDiscount as well as ISaveOrder as it also save the order to the database.

public class Order:IDiscount,ISaveOrder   
    {
       
        public virtual decimal Discount(decimal totalPrice)
        {
            return totalPrice;
        }
        Log oLog = new Log();
        public virtual void SaveOrder(decimal price)
        {
            try
            {
                //Save Order Goes Here
            }
            catch(Exception ex)
            {
                oLog.ErrorHandle(ex.ToString());
            }
        }
    }

And other class will be same like bellow

class GoldCoupon : Order
    {
        public override decimal Discount(decimal totalPrice)
        {
            return base.Discount((totalPrice * 10) / 100);
        }
        public override void SaveOrder(decimal price)
        {
            decimal totalPrice = Discount(price);
            //Database Code here
        }
    }
    class SilverCoupon : Order
    {
        public override decimal Discount(decimal totalPrice)
        {
            return base.Discount((totalPrice * 5) / 100);
        }
        public override void SaveOrder(decimal price)
        {
            decimal totalPrice = Discount(price);
            //Database Code here
        }
    }
    class BronzeCoupon : Order
    {
        public override decimal Discount(decimal totalPrice)
        {
            return base.Discount((totalPrice * 2) / 100);
        }
        public override void SaveOrder(decimal price)
        {
            decimal totalPrice = Discount(price);
            //Database Code here
        }
    }

Now without confusion we can create list of SaveOrder .
List<Order> Orders = new List<Order>();
            Orders.Add(new GoldCoupon());
            Orders.Add(new SilverCoupon());
            //Orders.Add(new VchrEnquiry()); this line will show error
            Orders.Add(new BronzeCoupon());

            foreach (Order o in Orders)
            {
                o.SaveOrder(10);
            }
I - ISP (Interface Segregation principle)
Now your application going well and you have more clients, some new client’s demanded for view of Orders after Order Make.
Now what will you do, if you decided to change existing code may be existing client don’t want this facility, now what you will do, see below
public interface IOrderViewV1
    {
        void ViewOrder();
    }

Add a interface with name IOrderViewV1 implement it in new class OrderView see below
public class OrderView : ISaveOrder,IOrderViewV1
    {
        Log oLog = new Log();
        public virtual void SaveOrder(decimal price)
        {
            try
            {
                //Save Order Goes Here
            }
            catch (Exception ex)
            {
                oLog.ErrorHandle(ex.ToString());
            }
        }

        public void ViewOrder()
        {
            //your Order view logic will go here
        }
    }

So here old client will use ISaveOrder Interface and new will use IOrderView Interface.
     ISaveOrder oI = new Order();
            oI.SaveOrder(10);
            IOrderViewV1 oIV1 = new OrderView();
            oIV1.ViewOrder();


D- Dependency inversion principle
Guys this very important topic for the interview and ask me many times to me here I tried to explain about Dependency inversion Principle.
See below code, in SRP we just discussed about this code make a separate class for the error log. 
public class Order
    {
        Log oLog = new Log();
        public virtual void SaveOrder(decimal price)
        {
            try
            {
                //Save Order Goes Here
            }
            catch (Exception ex)
            {
                oLog.ErrorHandle(ex.ToString());
            }
        }
    }
    class Log
    {
        public void ErrorHandle(string ex)
        {
            File.WriteAllText(@"D:\Elog.txt", ex.ToString());
        }
    }

Now we have requirement to error should be send on user mail or sms also and it will be choose by user if he want mail or sms now what we will do
We implement an interface for log and put code in catch blog, see below.

class EmailLog:ILog
    {
        public void ErrorHandle(string ex)
        {

            //Email code here
        }

    }

    class smsLog:ILog
    {

        public void ErrorHandle(string ex)
        {
            //Sms code here
        }
    }
    class FileLog : ILog
    {
        public void ErrorHandle(string ex)
        {

            //Email code here
        }

    }

public class Order
    {
        private ILog oLog;
        public virtual void SaveOrder(decimal price,string exType)
        {
            try
            {
                //Save Order Goes Here
            }
            catch (Exception ex)
            {
                if (exType == "File")
                {
                    oLog = new FileLog();
                    oLog.ErrorHandle(ex.ToString());
                }
                if (exType == "Email")
                {
                    oLog = new EmailLog();
                    oLog.ErrorHandle(ex.ToString());
                }
                if (exType == "Sms")
                {
                    oLog = new smsLog();
                    oLog.ErrorHandle(ex.ToString());
                }
            }
        }
    }

The See catch block here you find SRP violate but aspect is different, it’s about deciding which object should be instantiate.  
Now it’s not the work of Order object to decide which instances to be created, he should be concentrating only on Order class related functionalities.
The biggest problem is the new keyword. He is taking extra responsibilities of which object needs to be created.
If we give these responsibilities to other someone else except Order class our problem may be solved.
See below code
public class Order:ISaveOrder
    {
        private ILog oLog;
        public Order(ILog iLog)
        {
            oLog = iLog;
        }
        public Order()
        {
        
        }
        public virtual void SaveOrder(decimal price)
        {
            try
            {
                int i = 10, j = 0;
                i = i / j;
                //Save Order code Goes Here
            }
            catch (Exception ex)
            {
                oLog.ErrorHandle(ex.ToString());
            }
        }
    }
    class EmailLog:ILog
    {
        public void ErrorHandle(string ex)
        {

            //Email code here
        }

    }

    class smsLog:ILog
    {

        public void ErrorHandle(string ex)
        {
            //Sms code here
        }
    }
    class FileLog : ILog
    {
        public void ErrorHandle(string ex)
        {

            //Email code here
        }

    }

public class MakeOrder
    {
        public void MyOrder()
        {
            List<Order> Orders = new List<Order>();
            ISaveOrder oI = new Order(new smsLog());
            oI.SaveOrder(10);
           
        }

    }
When you call MyOrder Method you will get an error, and catch block in Order class call the smsLog class method.