Skip to main content

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.

Popular posts from this blog

What is difference between UNION and UNION ALL in SQL Server

We use UNION and UNION ALL operator to combine multiple results set into one result set.
UNION operator is used to combining multiple results set into one result set but removes any duplicate rows. Basically, UNION is used to performing a DISTINCT operation across all columns in the result set. UNION operator has the extra overhead of removing duplicate rows and sorting result.
UNION ALL operator use to combine multiple results set into one result set but it does not remove any duplicate result. Actually, this does not remove duplicate rows so it is faster than the UNION operator. If you want to combine multiple results and without duplicate records then use UNION otherwise UNION ALL is better.
Following some rules for using UNION/UNION ALL operator
1.The number of the column should be the same in the query's when you want to combine them. 2.The column should be of the same data type. 3.ORDER BY clause can be applied to the overall result set not within each result set.
4.Column name of …

Dropdownlist selectedindexchanged event is not firing

<asp:DropDownList ID="ddlSource" runat="server" DataSourceID="SqlDataSource1" DataTextField="vcSuplierNm" ViewStateMode="Enabled DataValueField="vcSuplierCode" EnableViewState="true" AppendDataBoundItems="true" OnSelectedIndexChanged="ddl_OnSelectedIndexChanged" AutoPostBack="true"></asp:DropDownList>

Add property ViewStateMode="Enabled" and EnableViewState="true"
in drop DropDownList

Remove special characters from string in SQL server

I faced many times an issue to remove special characters from a string. Suppose you are working on searching concept and you have to remove the special characters from search string due to query performance, there are many solution are available but T-SQL is easily resolved this issue.
Following query may help you to resolve your issue.

DECLARE@strVARCHAR(400) DECLARE@expresVARCHAR(50)='%[~,@,#,$,%,&,*,(,),.,!]%' SET@str='(remove) ~special~ *characters. from string in sql!' WHILEPATINDEX(@expres,@str)> 0 BEGIN SET@str=Replace(REPLACE(@str,SUBSTRING(@str,PATINDEX(@expres,@str), 1 ),''),'-',' ') END SELECT@str



Add day to ISODate in MongoDB

We can use $add operator to add days in ISODate in mongodb, $add is the Arithmetic Aggregation Operator which adds number and date in mongodb.
Syntax:

{ $add: [ <expression1>, <expression2>, ... ] }

Note:  If one of the argument is date $add operator treats to other arguments as milliseconds to add to the date.
Example: Suppose we have a Test collection as below.

{"Title" : "Add day to ISODate in MongoBD","CreatedDate" : ISODate("2016-07-07T08:00:00.000Z")}

Query to add 2 days in CreatedDate

db.Test.aggregate([      { $project: { Title: 1, AddedDate: { $add: [ "$CreatedDate", 2*24*60*60000 ] } } }    ])

Result:

{ "_id" : ObjectId("579a1567ac1b3f3732483de0"), "Title" : "Add day to ISODate in MongoBD", "AddedDate" : ISODate("2016-07-09T08:00:00.000Z") }

Note: As mentioned in above note we have to convert days in millisecond because $add operator treat to other arg…

Remove special character from string in MongoDB

Problem: Suppose wehave a collection and one field is type string contains some special character (like !@#$%) and we don’t want these special character.
Solution: We can easily remove the special character from field using script “replace(/[^a-zA-Z 0-9 ]/g, '')” in our query.  How can we remove special character from string using this script please see following example.
Example: Suppose we have a collection “EduSurvey “where we are collecting information from institutions.

{Name:"JB institute”, About:"This is good one collage for MBA", Information:"This $%%institute ##has good faculty etc$$"}
{Name:"MK institute”, About:"This is good one collage for MCA", Information:"This$$%# is the dummy text12"}
{Name:"MG institute”, About:"This is good one collage for B,Tech", Information:"This# institute@ has&* good infrastructure"}

Did you notice Information fields contains some special character so we…

$group (aggregation) in MongoDB

IF we want to group document by specific expression and want to output for each distinct grouping of document here we have to use $group. IF you familiar with Relational Database like SQL Server, It's work same like GROUP BY clause.
Output document contains the _id field which contains the distinct group by key also output document contains computed fields which grasp the value of some accumulator expression grouped by the _id.
Syntax:

{ $group: { _id: <expression>, <field1>: { <accumulator1> : <expression1> }, ... }

Accumulator Operator:
$num : It's returns sum of the numeric value. It can be use in $project also in mongodb 3.2 version.
$avg  : It's returns the average of numeric values. It can be use in $project also in mongodb 3.2 version.
$first : It's returns a value from the first document for each group.
$last : It's returns a value from the first document for each group.
$max : It's returns the highest expression value for each group.…

Add a column, with a default value, to an existing table in SQL Server

You can do it by edit of table design, if you want do it with query see following.
Syntax:
ALTERTABLE{TABLENAME} ADD{COLUMNNAME}{TYPE}{NULL|NOTNULL} CONSTRAINT{CONSTRAINT_NAME}DEFAULT{DEFAULT_VALUE} [WITH VALUES]
Note: Use WITH VALUES to update existing null-able rows.
Example:
ALTERTABLEMyTable ADDMyTableTypeIDINTNOTNULL CONSTRAINTConstraint_MyTableTypeIDDEFAULT0 GO
Note: WITH VALUES handles the NOT NULL part
ALTERTABLEMyTable ADDMyTableTypeIDINT CONSTRAINTConstraint_MyTableTypeIDDEFAULT

Merge and Merge join transformation in SSIS