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
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";
}
}
using System.Collections.Generic;
namespace Test
{
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() }));
}
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.
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;
public class Program
{
private static void Build(Device Dev)
{
{
private static void Build(Device Dev)
Dev.Test();
}
public static void Main()
{
Build(new
Device(new
List<IPart>{
}
//Open
Closed Principle
internal class Device
{
private List<IPart> _parts;
//Dependency Inversion
public Device(List<IPart> parts)
{
//Dependency Inversion
_parts = parts;
}
}
public void Test()
{
//Liskov Substitution Principle
//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
{
}
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();
}