Friday, May 16, 2014

SOLID


Introduction:   
As we all know that using Object Oriented Programming System (OOPS) concepts and using classes, objects will not show that we are writing efficient code in our applications. Without knowing OOPS principles we will be using OOPS and facing problems in maintaing and enhancing the code in our applications. So we should know how to implement OOPS and use them in right manner, that is where 5 Object Oriented Principles (also called as SOLID Principles) comes into picture. Let us go through each principle and understand how to implement classes and objects in our real time applications.

Single Responsibility Principle:

One Class should be responsible for one task.

E.g.
Want to Insert data into database and want to log the details.
If we create a class to represent DataAccess, it should not be used to save to the database (task 1), as well as log the details (task 2).
Problem:
If we want to change database type or we want to change the logger type/location, If both tasks present in single class, one task changes affects to another.
Solution:
Create one class for saving into database and another class for logging the details.
  
Exmple:

C#
//1. Single Responsibility Principle 
 
// Data access class is only responsible for data base related operations 
class DataAccess 
{ 
    public static void InsertData() 
    { 
        Console.WriteLine("Data inserted into database successfully"); 
    } 
} 
// Logger class is only responsible for logging related operations 
class Logger 
{ 
    public static void WriteLog() 
    { 
        Console.WriteLine( "Logged Time:"  + DateTime.Now.ToLongTimeString() + " Log  Data insertion completed successfully"); 
    } 
}

Open Closed Principle:
Classes, modules, functions, etc. should be open for extension, but closed for modification.
Create a Base class with Required functionality, and ensure we will not modify that class. (Closed for modification)
Create a Derived class by inheriting the Base class for extension (Open for modification)
Example:

C#
//2. Open Close Principle 
// Here DataProvder is open for extension (extends to Sql, Oracle, Oledb Providers and so on..) and closed for manipulation 
abstract class DataProvider 
{ 
    public abstract  int OpenConnection(); 
    public abstract  int CloseConnection(); 
    public abstract int ExecuteCommand(); 
} 
class SqlDataProvider : DataProvider 
{ 
    public override int OpenConnection() 
    { 
        Console.WriteLine("\nSql Connection opened successfully"); 
        return 1; 
    } 
    public override int CloseConnection() 
    { 
        Console.WriteLine("Sql Connection closed successfully"); 
        return 1; 
    } 
    public override int ExecuteCommand() 
    { 
        Console.WriteLine("Sql Command Executed successfully"); 
        return 1; 
    } 
} 
class OracleDataProvider : DataProvider 
{ 
    public override int OpenConnection() 
    { 
        Console.WriteLine("Oracle Connection opened successfully"); 
        return 1; 
    } 
    public override int CloseConnection() 
    { 
        Console.WriteLine("Oracle Connection closed successfully"); 
        return 1; 
    } 
    public override int ExecuteCommand() 
    { 
        Console.WriteLine("Oracle Command Executed successfully"); 
        return 1; 
    } 
} 
 
class OledbDataProvider : DataProvider 
{ 
    public override int OpenConnection() 
    { 
        Console.WriteLine("OLEDB Connection opened successfully"); 
        return 1; 
    } 
    public override int CloseConnection() 
    { 
        Console.WriteLine("OLEDB Connection closed successfully"); 
        return 1; 
    } 
    public override int ExecuteCommand() 
    { 
        Console.WriteLine("OEDB Command Executed successfully"); 
        return 1; 
    } 
} 
class OpenClosePrincipleDemo 
{ 
    public static void OSPDemo() 
    { 
        Console.WriteLine("\n\nOpen Close Principle Demo "); 
 
        DataProvider DataProviderObject = new SqlDataProvider(); 
        DataProviderObject.OpenConnection(); 
        DataProviderObject.ExecuteCommand(); 
        DataProviderObject.CloseConnection(); 
 
        DataProviderObject = new OracleDataProvider(); 
        DataProviderObject.OpenConnection(); 
        DataProviderObject.ExecuteCommand(); 
        DataProviderObject.CloseConnection(); 
    } 
}

Liskov Substitution Principle:
Subtypes must be substitutable for their base types.

http://www.abhishekshukla.com/net-2/solid-design-principles-liskov-substitution-principle-lsp/
http://www.codeproject.com/Articles/595160/Understand-Liskov-Substitution-Principle-LSP
This principle is just an extension of the Open Close Principle. 
It means that we must make sure that new derived classes are extending the base classes without changing their behavior.
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. 
 Or   
If any module is using a Base class then the reference to that Base class can be replaced with a Derived class without affecting the functionality of the module. 
Or 
While implementing derived classes, need to ensure that the derived classes just extend the functionality of base classes without replacing the functionality of base classes. 
E.g.
If we are calling a method defined at a base class upon an abstracted class, the function must be implemented properly on the subtype class. 
// If any module is using a Base class then the reference to that Base class can be replaced with a Derived class without affecting the functionality of the module.  
// Or  
// While implementing derived classes, one needs to ensure that, derived classes just extend the functionality of base classes without replacing the functionality of base classes.     
4
public abstract class Shape
{
    public abstract double GetArea();
}
public class Rectangle : Shape
{
    private double _height;
    private double _width;
    public double Height
    {
        get { return _height; }
        set { _height = value; }
    }
    public double Width
    {
        get { return _width; }
        set { _width = value; }
    }
    public override double GetArea()
    {
        return Height * Width;
    }
}
public class Square : Shape
{
    private double _sideLength;
    public double SideLength
    {
        get    {   return _sideLength;        }
        set    {   _sideLength = value;      }
    }
    public override double GetArea()
    {
        return SideLength*SideLength;
    }
}
static void Main()
        {
            Shape shape = new Rectangle { Height = 40, Width = 60 };
            Console.WriteLine("Area of the rectangle shape = " + shape.GetArea());
            shape = new Square { SideLength = 40 };
            Console.WriteLine("Area of the square shape = " + shape.GetArea());
            Console.ReadLine();
        }
Area of the rectangle shape = 2400
Area of the square shape = 1600
So now the parent class is substitutable by the child classes without changing any existing functionality and so we are not violating the Liskov Substitution Principle.




Interface Segregation Principle:
Clients should not be forced to depend upon interfaces that they do not use.
E.g.
When a client depends upon a class that contains interfaces that the client does not use, but that other clients do use, then that client will be affected by the changes that those other clients force upon the class
Base Interface:                                 IDataProvider
Derived Interfaces:         ISqlDataProvider, IOracleDataProvider
Derived Classes:                SqlDataClient, OracleDataClient
Each Derived class should implement functions from their respective interfaces.
No derived interface force other derived classes to implement the functionalities which they won't use.
Example:

C#
// Only common generic methods exists for all derived classes. 
interface IDataProvider 
{ 
    int OpenConnection(); 
    int CloseConnection(); 
} 
// Implement methods specific to the respective derived classes 
interface ISqlDataProvider : IDataProvider 
{ 
    int ExecuteSqlCommand(); 
} 
// Implement methods specific to the respective derived classes 
interface IOracleDataProvider : IDataProvider 
{ 
    int ExecuteOracleCommand(); 
} 
// Client 1 
// Should not force SqlDataProvider client to implement ExecuteOracleCommand, as it wont required that method to be implemented. 
// So that we will derive ISqlDataProvider not IOracleDataProvider 
class SqlDataClient : ISqlDataProvider 
{ 
    public int OpenConnection() 
    { 
        Console.WriteLine("\nSql Connection opened successfully"); 
        return 1; 
    } 
    public int CloseConnection() 
    { 
        Console.WriteLine("Sql Connection closed successfully"); 
        return 1; 
    } 
 
    // Implemeting ISqlDataProvider, we are not forcing the client to implement IOracleDataProvider 
    public int ExecuteSqlCommand() 
    { 
        Console.WriteLine("Sql Server specific Command Executed successfully"); 
        return 1; 
    } 
} 
// Client 2 
// Should not force OracleDataProvider client to implement ExecuteSqlCommand, as it wont required that method to be implemented. 
// So that we will derive IOracleDataProvider not ISqlDataProvider 
class OracleDataClient : IOracleDataProvider 
{ 
    public int OpenConnection() 
    { 
        Console.WriteLine("\nOracle Connection opened successfully"); 
        return 1; 
    } 
    public int CloseConnection() 
    { 
        Console.WriteLine("Oracle Connection closed successfully"); 
        return 1; 
    } 
    // Implemeting IOracleDataProvider, we are not forcing the client to implement ISqlDataProvider 
    public int ExecuteOracleCommand() 
    { 
        Console.WriteLine("Oracle specific Command Executed successfully"); 
        return 1; 
    } 
} 
class InterfaceSegregationPrincipleDemo 
{ 
    public static void ISPDemo() 
    { 
        Console.WriteLine("\n\nInterface Inversion Principle Demo "); 
 
        // Each client will implement their respective methods no base class forces the other client to implement the methods which dont required. 
        // From the above implementation, we are not forcing Sql client to implemnt orcale logic or Oracle client to implement sql logic. 
 
        ISqlDataProvider SqlDataProviderObject = new SqlDataClient(); 
        SqlDataProviderObject.OpenConnection(); 
        SqlDataProviderObject.ExecuteSqlCommand(); 
        SqlDataProviderObject.CloseConnection(); 
 
        IOracleDataProvider OracleDataProviderObject = new OracleDataClient(); 
        OracleDataProviderObject.OpenConnection(); 
        OracleDataProviderObject.ExecuteOracleCommand(); 
        OracleDataProviderObject.CloseConnection(); 
    } 
}


Dependency Inversion Principle:
A: High level modules should not depend upon low level modules. Both should depend upon abstractions.
B: Abstractions should not depend upon details. Details should depend upon abstractions.
By passing dependencies to classes as abstractions, you remove the need to program dependency specific. 
Also referred as IoC Inversion of Control principle and implements as DI Dependency Injection Pattern in programming world.
E.g:
An Employee class that needs to be able to save to XML and a database.
Problem 1:
If we placed ToXML() and ToDB() functions in the class, we'd be violating the single responsibility principle.
Problem 2:
If we created a function that took a value that represented whether to print to XML or to DB, we'd be hard-coding a set of devices and thus be violating the open closed principle.
Solution:
1. Create an abstract class named 'DataWriter' that can be inherited from 'XMLDataWriter' and 'DbDataWriter'.
2. Then Create a class named 'EmployeeWriter' that would expose an Output 'DataWriter saveMethod' that accepts a dependency as an argument.
See how the Output method is dependent upon the abstractions just as the output types are?
The dependencies have been inverted.
Now we can create new types of ways for Employee data to be written, perhaps via HTTP/HTTPS by creating abstractions, and without modifying any of our previous code!
Read more about Inversion of Control (IoC) with Dependency Injection (DI) Pattern here
http://www.codeproject.com/Articles/872007/Dependency-Inversion-Principle-What-you-always-kne  
http://www.aspnphp.com/inversion-of-control-ioc/#


Example:

C#
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
 
namespace SOLIDPrinciplesDemo 
{ 
//DIP Violation 
// Low level Class 
public class BankAccount 
{ 
    public string AccountNumber { getset; } 
    public decimal Balance { getset; } 
         
    public void AddFunds(decimal value) 
    { 
        Balance += value; 
    } 
    public void RemoveFunds(decimal value) 
    { 
        Balance -= value; 
    } 
} 
// High level Class 
public class TransferAmount 
{ 
    public BankAccount Source { getset; } 
    public BankAccount Destination { getset; } 
    public decimal Value { getset; } 
 
    public void Transfer() 
    { 
        Source.RemoveFunds(Value); 
        Destination.AddFunds(Value); 
    } 
} 
/*  
Problem with above design: 
  
1. The high level TransferAmount class is directly dependent upon the lower level BankAccount class i.e. Tight coupling. 
2. The Source and Destination properties reference the BankAccount type.So impossible to substitute other account types unless they are subclasses of BankAccount.  
3. Later we want to add the ability to transfer money from a bank account to pay bills, the BillAccount class would have to inherit from BankAccount.  
 As bills would not support the removal of funds,  
 3.A. This is likely to break the rules of the Liskov Substitution Principle (LSP) or  
 3.B. Require changes to the TransferAmount class that do not comply with the Open/Closed Principle (OCP). 
  
4. Any extension functionality changes be required to low level modules.  
 4.A. Change in the BankAccount class may break the TransferAmount.  
 4.B. In complex scenarios, changes to low level classes can cause problems that cascade upwards through the hierarchy of modules.  
5. As the software grows, this structural problem can be compounded and the software can become fragile or rigid. 
6. Without the DIP, only the lowest level classes may be easily reusable. 
7. Unit testing should be redone when there is a change in high level or low level classes. 
8. Time taken process to change the existing functionality and extending the functionality 
*/  
 
//Applying DIP resolves these problems by removing direct dependencies between classes.  
public interface ITransferSource 
{ 
    long AccountNumber { getset; } 
    decimal Balance { getset; } 
    void RemoveFunds(decimal value); 
} 
public interface ITransferDestination 
{ 
    long AccountNumber { getset; } 
    decimal Balance { getset; } 
    void AddFunds(decimal value); 
} 
public class BOABankAccount : ITransferSource, ITransferDestination 
{ 
    public long AccountNumber { getset; } 
    public decimal Balance { getset; } 
 
    public void AddFunds(decimal value) 
    { 
        Balance += value; 
    } 
    public void RemoveFunds(decimal value) 
    { 
        Balance -= value; 
    } 
} 
public class TransferAmounts 
{ 
    public decimal Amount { getset; } 
    public void Transfer(ITransferSource TransferSource, ITransferDestination TransferDestination) 
    {             
        TransferSource.RemoveFunds(Amount); 
        TransferDestination.AddFunds(Amount); 
    } 
}     
/* 
Advantage in above example after applying DIP: 
 
1. Higher level classes refer to their dependencies using abstractions, such as interfaces or abstract classes i.e. loose coupling.  
2. Lower level classes implement the interfaces, or inherit from the abstract classes. 
3. This allows new dependencies can be substituted without any impact.  
4. Lower levels classes will not cascade upwards as long as they do not involve changing the abstraction. 
5. Increases the robustness of the software and improves flexibility.  
6. Separation of high level classes from their dependencies raises the possibility of reuse of these larger areas of functionality.  
7. Minimized risk to affect old funtionallity present in Higher level classes. 
8. Testing applies only for  newly added low level classes. 
9. Though using this principle implies an increased effort and a more complex code, but it is more flexible.  
  
Note: 
In that case the creation of new low level objects inside the high level classes(if necessary) can not be done using the operator new.  
Instead, some of the Creational design patterns can be used, such as Factory Method, Abstract Factory, Prototype. 
 
The Template Design Pattern is an example where the DIP principle is applied.    
   
This principle cannot be applied for every class or every module.  
If we have a class functionality that is more likely to remain unchanged in the future there is not need to apply this principle. 
*/ 
class DependencyInversionPrincipleDemo 
{ 
    public static void DIPDemo() 
    { 
            Console.WriteLine("\n\nDependency Inversion Principle Demo "); 
 
        //Created abstract class/interfaces objects for low level classes. 
        ITransferSource TransferSource = new BOABankAccount(); 
        TransferSource.AccountNumber = 123456789; 
        TransferSource.Balance = 1000; 
        Console.WriteLine("Balance in Source Account : " + TransferSource.AccountNumber + " Amount " + TransferSource.Balance);  
 
        ITransferDestination TransferDestination = new BOABankAccount(); 
        TransferDestination.AccountNumber = 987654321; 
        TransferDestination.Balance = 0; 
        Console.WriteLine("Balance in Destination Account : " + TransferDestination.AccountNumber + " Amount " + TransferDestination.Balance); 
             
             
        TransferAmounts TransferAmountsObject = new TransferAmounts(); 
        TransferAmountsObject.Amount = 100; 
 
        // High level class using abstract class/interface objects  
        TransferAmountsObject.Transfer(TransferSource, TransferDestination); 
        Console.WriteLine("Transaction successful"); 
 
        Console.WriteLine("Balance in Source Account : " + TransferSource.AccountNumber + " Amount " + TransferSource.Balance); 
        Console.WriteLine("Balance in Destination Account : " + TransferDestination.AccountNumber + " Amount " + TransferDestination.Balance); 
    } 
}

I have explained each principle in detail with example in the sample. Download the sample and go through the code with comments.
Open/Close Principle

                                        LiskovSubstitutionPrinciple


http://code.msdn.microsoft.com/silverlight/OOPS-Principles-SOLID-7a4e69bf

http://www.codeproject.com/Articles/703634/SOLID-architecture-principles-using-simple-Csharp

LSP

namespace LiscovSubstitution
{
    class Program
    {
        static void Main(string[] args)
        {

            //In book at page: 141
            Area sq = new Square();
            Area rect = new Rectangle();
            sq.Height = 7;
            sq.Width = 3;
            rect.Height = 7;
            rect.Width = 3;
            Console.Write("Square :");
            Console.WriteLine(sq.Areas()); ;
            Console.Write("Rectangle :");
            Console.WriteLine(rect.Areas());
            Console.ReadKey();
        }
    }
}
namespace LiscovSubstitution
{
    public class Square:Area
    {
        public override int Height
        {
            get
            {
                return base.Height;
            }
            set
            {
                CalculateArea(value);
            }
        }

        public override int Width
        {
            get
            {
                return base.Width;
            }
            set
            {
                CalculateArea(value);
            }
        }

        public void CalculateArea(int value)
        {
            base.Height = value;
            base.Width = value;
        }
    }
}
namespace LiscovSubstitution
{
    public class Area
    {
        public virtual int Height { get; set; }
        public virtual int Width { get; set; }
        public int Areas()
        {
            return Height * Width;
        }
    }
}
 namespace LiscovSubstitution
{
    //Here Base class is the area
    //The derived class is Rectangle
    public class Rectangle:Area
    {
        public override int Height
        {
            get
            {
                return base.Height;
            }
            set
            {
                base.Height = value;
            }
        }

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

    }
}
********************************Another LSP Ex************************
namespace LiscovSubstitution
{
   public abstract class Shape
    {
       public abstract int Area();
    }

   public class Rectangle2 : Shape
   {
       public virtual int Height { get; set; }
       public virtual int Width { get; set; }
       public override int Area()
       {
           return Height * Width;
       }
   }
   public class Square2 : Shape
   {
       public int SideLength;

       public override int Area()
       {
           return SideLength * SideLength;
       }
   }

}

namespace LiscovSubstitution
{
    class Program
    {
        static void Main(string[] args)
        {          
            Show();
            Console.ReadKey();
        }

        static void Show()    
        {
            var shapes = new List<Shape>
                        {
                            new Rectangle2{Height=3,Width=7},
                             new Square2{SideLength=3},

                        };
            var areas = new List<int>();
            foreach (Shape shape in shapes)
            {
                areas.Add(shape.Area());
            }
            Console.Write("Rectangle :");
            Console.WriteLine(areas[0]);
            Console.Write("Square :");
            Console.WriteLine(areas[1]);            
        }
    }

}
**************************************************************************



No comments:

Post a Comment