SOLID Principles in .Net
Table of Contents
The SOLID principles are a set of five simple rules that help developers write better code. The first rule, Single Responsibility, means that every class or function should do just one thing. This makes the code easier to understand and fix. The second, Open/Closed, means your code should allow new features to be added without changing the existing code too much. This helps avoid bugs when adding new things to a project.
The third rule, Liskov Substitution, means that if one class is based on another, it should work in the same way without causing problems. The fourth, Interface Segregation, suggests using smaller, focused interfaces so classes only deal with what they actually need. The last rule, Dependency Inversion, encourages depending on general ideas or interfaces, not specific details, which makes the system more flexible. Following these rules keeps your code neat, easy to maintain, and ready for future changes.
1. Single Responsibility Principle (SRP):
A class should have only one reason to change, meaning it should have only one responsibility.
// Before applying SRP
public class User
{
public void SaveUser(User user)
{
// logic for saving user to the database
// logic for sending a confirmation email
}
}
// After applying SRP
public class UserRepository
{
public void SaveUser(User user)
{
// logic for saving user to the database
}
}
public class EmailService
{
public void SendConfirmationEmail(User user)
{
// logic for sending a confirmation email
}
}
2. Open/Closed Principle (OCP)
Software entities (classes, modules, functions) should be open for extension but closed for modification.
// Before applying OCP
public class Circle
{
public double Radius { get; set; }
public double Area()
{
return Math.PI * Math.Pow(Radius, 2);
}
}
// After applying OCP
public interface IShape
{
double Area();
}
public class Circle : IShape
{
public double Radius { get; set; }
public double Area()
{
return Math.PI * Math.Pow(Radius, 2);
}
}
public class Square : IShape
{
public double Side { get; set; }
public double Area()
{
return Math.Pow(Side, 2);
}
}
3. Liskov Substitution Principle (LSP):
Subtypes must be substitutable for their base types without altering the correctness of the program.
// Before applying LSP public class Bird { public virtual void Fly() { // logic for flying } } public class Penguin : Bird { public override void Fly() { // penguins can't fly } } // After applying LSP public interface IFlyable { void Fly(); } public class Bird : IFlyable { public void Fly() { // logic for flying } } public class Penguin : IFlyable { public void Fly() { // penguins can't fly } }
4. Interface Segregation Principle (ISP):
The Dependency Inversion Principle is the D in SOLID principles, and it focuses on decoupling high-level modules from low-level modules. “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.
// Before applying ISP public interface IWorker { void Work(); void TakeBreak(); } public class Manager : IWorker { public void Work() { // logic for managing } public void TakeBreak() { // logic for taking a break } } // After applying ISP public interface IWorker { void Work(); } public interface IBreakable { void TakeBreak(); } public class Manager : IWorker, IBreakable { public void Work() { // logic for managing } public void TakeBreak() { // logic for taking a break } }
5. Dependency Inversion (DIP):
A class should not be forced to implement interfaces it does not use.
// Before applying ISP
// Low-level module
public class EmailService
{
public void SendEmail(string message)
{
Console.WriteLine("Email sent: " + message);
}
}
// High-level module
public class Notification
{
private EmailService _emailService;
public Notification()
{
_emailService = new EmailService(); // tightly coupled to EmailService
}
public void Send(string message)
{
_emailService.SendEmail(message);
}
}
// After applying ISP
// Abstraction
public interface IMessageService
{
void Send(string message);
}
// Low-level module
public class EmailService : IMessageService
{
public void Send(string message)
{
Console.WriteLine("Email sent: " + message);
}
}
public class SmsService : IMessageService
{
public void Send(string message)
{
Console.WriteLine("SMS sent: " + message);
}
}
// High-level module depends on abstraction
public class Notification
{
private readonly IMessageService _messageService;
public Notification(IMessageService messageService)
{
_messageService = messageService;
}
public void Notify(string message)
{
_messageService.Send(message);
}
}
Applying SOLID principles can lead to code that is more modular, maintainable, and flexible. Each principle addresses a specific aspect of software design, contributing to the overall goal of creating robust and adaptable systems.