Friday, January 3, 2020

SOLID Principles

The principles that SOLID outlines are important to writing clear maintainable code. SOLID is an acronym. It describes five principles that developers should always consider when writing code. Those five principles are:
S – Single Responsibility
O – Open for Extension / Closed for Modification
L – Liskov Substitution
I – Interface Segregation
D – Dependency Inversion


Single Responsibility – A class should have a single responsibility. Generally, responsibilities are potential points of change. So, it follows that the more a class is responsible for the more likely it will need to change as the application utilizing the class evolves changes. Keeping the classes focused on a specific responsibility ensures that they perform their task effectively and efficiently.


Open for Extension / Closed for Modification – Classes are generally instantiated in several different parts of an application. For example, let us take an example of a Customer class in an order/entry system. The Customer class will probably be instantiated when creating a new customer, updating the customer, adding orders to a customer and so on. If a developer needs to modify the Customer class according to a new requirement that developer will need to understand the where and the why that class is used in every part of the application. This can become overwhelming quickly. Providing extensibility points in a class through inheritance developers can override any methods or properties marked as virtual.


Liskov Substitution – This principle states that ‘if a child class C is a subtype of P then objects of type P may be replaced with objects of type C without altering the correctness of that program’. What it means is that the child classes cannot break the functionality of the parent. The classic example is one in which we have a rectangle parent class and a child square class. The square class is-a rectangle but when we constrain the square to have fixed length and width parameters we break the Liskov Substitution principal because the parent can no longer be replaced with the child class.


Interface Segregation – In alignment with the Single Responsibility principle, the Interface Segregation principle dictates that interfaces should be as simple and focused as possible. It’s better to have multiple interfaces that are focused than a single interface that tries to do everything. In a language like Java or C#, the designer and developer lose nothing by splitting a large interface into multiple interfaces. As we know we can implement several interfaces on a class. Classes that implement interfaces must implement all the properties the interface dictates.


Dependency Inversion – Dependencies should rely on abstractions rather than concretions. The Dependency Inversion principle specifies that we should move the instantiation of classes that a higher-level class depends on outside of that class thus inverting the dependency. This allows us to replace the dependency with another class and not break our open/closed principle.

For example: Let us analyze the following code:

using System;

public class Program
{
private static Computer _computer;
public static void Main()
{
_computer = new Computer();
_computer.Test();
}
}

public class Computer
{
private MotherBoard _mb;
private Cpu _cpu;
private Ram _ram;
private HardDrive _hd;
private PowerSupply _ps;
public Computer()
{
_mb = new MotherBoard();
_cpu = new Cpu();
_ram = new Ram();
_hd = new HardDrive();
_ps = new PowerSupply();
}

public void Test()
{
Console.WriteLine(_mb.Motherboarding());
Console.WriteLine(_cpu.Calculate());
Console.WriteLine(_ram.StoringData());
Console.WriteLine(_hd.WritingData());
Console.WriteLine(_ps.ProducePower());
}
}

internal class PowerSupply
{
public string ProducePower()
{
return "Producing Power";
}
}

internal class HardDrive
{
public string WritingData()
{
return "Writing Data";
}
}

internal class Ram
{
public string StoringData()
{
return "Storing Data";
}
}

internal class Cpu
{
public string Calculate()
{
return "Calculating";
}
}

internal class MotherBoard
{
public string Motherboarding()
{
return "Connecting everything";
}

}


This code is not SOLID. Now, what we must do is we have to be able to build mobile, computer, laptop, etc. as needed using SOLID principles. The final code may look like this:
using System;
using System.Collections.Generic;
namespace Test
{    
public class Program
    {
        private static void Build(Device Dev)
        {
            Dev.Test();
        }
        public static void Main()
        {
            Build(new Device(new List<IPart>{
                new MotherBoard(),new Cpu(),
                new Ram(), new HardDrive(),
                new PowerSupply() }));
            Build(new Device(new List<IPart>{
                new MotherBoard(),new Cpu(),
                new Ram(), new HardDrive(),
                new PowerSupply(), new Battery() }));
          }
    }
    //Open Closed Principle
    internal class Device
    {
        private List<IPart> _parts;
        //Dependency Inversion
        public Device(List<IPart> parts)
        {
            _parts = parts;
        }
        public void Test()
        {
             //Liskov Substitution Principle
            foreach(IPart p in _parts)
                Console.WriteLine(p.MyMessage());
        }
    }
    //single responsibility principle
    internal class Battery : IPart
    {
        public string MyMessage()
        {
                return "Charging";
        }
    }
    internal class PowerSupply : IPart
    {
        public string MyMessage()
        {
                return "Producing Power";
        }
    }
    internal class HardDrive : IPart
    {
        public string MyMessage()
        {
                return "Writing Data";
        }
    }
    internal class Ram : IPart
    {
        public string MyMessage()
        {
                return "Storing Data";
        }
    }
    internal class Cpu : IPart
    {
        public string MyMessage()
        {
            return "Calculating";
        }
    }
    internal class MotherBoard: IPart
    {
        public string MyMessage()
        {
                return "Connecting everything";
        }
    }    //Interface Segregation
    interface IPart
    {
        string MyMessage();
    }