SOLID Principles — A Practical guide in C#
SOLID Principles — best practices using C#
The SOLID principles are a set of five design principles for writing maintainable and scalable software. They were introduced by Robert C. Martin and are widely used in object-oriented programming. The SOLID acronym stands for:
- Single Responsibility Principle (SRP):
- A class should have only one reason to change, meaning that a class should only have one responsibility.
- This principle encourages the division of responsibilities in a program so that if a change occurs, it only affects one part of the code.
2. Open/Closed Principle (OCP):
- Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.
- This principle promotes the use of abstraction and polymorphism to allow adding new functionality without altering existing code.
3. Liskov Substitution Principle (LSP):
- Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.
- This principle ensures that a derived class can be substituted for its base class without changing the correctness of the program.
4. Interface Segregation Principle (ISP):
- A client should not be forced to depend on interfaces it does not use.
- This principle encourages the creation of small, specific interfaces instead of large, general-purpose ones, to prevent classes from being forced to implement methods they don’t need.
5. Dependency Inversion Principle (DIP):
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
- This principle encourages the use of dependency injection and inversion of control to achieve a flexible and maintainable architecture.
Now, let’s provide a practical guide in C# for each of these SOLID principles:
Single Responsibility Principle (SRP):
// Bad Example
class Employee
{
public void CalculateSalary() { /* ... */ }
public void GenerateReport() { /* ... */ }
}
// Good Example
class Employee
{
public void CalculateSalary() { /* ... */ }
}
class ReportGenerator
{
public void GenerateReport() { /* ... */ }
}
Open/Closed Principle (OCP):
:// Bad Example
class Shape
{
public virtual double Area() { /* ... */ }
}
class Circle : Shape
{
public override double Area() { /* ... */ }
}
// Good Example
interface IShape
{
double Area();
}
class Circle : IShape
{
public double Area() { /* ... */ }
}
class Square : IShape
{
public double Area() { /* ... */ }
}
Liskov Substitution Principle (LSP):
// Bad Example
class Bird
{
public virtual void Fly() { /* ... */ }
}
class Penguin : Bird
{
public override void Fly() { /* Not applicable */ }
}
// Good Example
interface IFlyable
{
void Fly();
}
class Bird : IFlyable
{
public void Fly() { /* ... */ }
}
class Penguin : IFlyable
{
public void Fly() { /* Not applicable */ }
}
Interface Segregation Principle (ISP):
// Bad Example
interface IWorker
{
void Work();
void TakeBreak();
}
class Manager : IWorker
{
public void Work() { /* ... */ }
public void TakeBreak() { /* ... */ }
}
// Good Example
interface IWorker
{
void Work();
}
interface IBreakable
{
void TakeBreak();
}
class Manager : IWorker, IBreakable
{
public void Work() { /* ... */ }
public void TakeBreak() { /* ... */ }
}
Dependency Inversion Principle (DIP):
// Bad Example
class DataAccessLayer
{
public string GetData() { /* ... */ }
}
class BusinessLogicLayer
{
private readonly DataAccessLayer _dataAccessLayer;
public BusinessLogicLayer()
{
_dataAccessLayer = new DataAccessLayer();
}
public string ProcessData()
{
var data = _dataAccessLayer.GetData();
// Process data
return data;
}
}
// Good Example
interface IDataAccessLayer
{
string GetData();
}
class DataAccessLayer : IDataAccessLayer
{
public string GetData() { /* ... */ }
}
class BusinessLogicLayer
{
private readonly IDataAccessLayer _dataAccessLayer;
public BusinessLogicLayer(IDataAccessLayer dataAccessLayer)
{
_dataAccessLayer = dataAccessLayer;
}
public string ProcessData()
{
var data = _dataAccessLayer.GetData();
// Process data
return data;
}
}
These examples illustrate how to apply each SOLID principle in C# to create more maintainable, flexible, and scalable code. Keep in mind that these are simplified examples, and real-world scenarios may require more complex implementations.