Introduction
SOLID is a set of design principles for writing clean, maintainable, and scalable code. It stands for:
S- Single Responsibility Principle (SRP)
A class or function should have only one responsibility. In the bad example below, the CreateUser function should not handle email-related logic.
Bad example:
public class UserService {
public void CreateUser(string email) {
var user = new User(email);
_repository.Save(user);
var smtpClient = new SmtpClient("smtp.company.com");
var message = new MailMessage(
"no-reply@myapp.com", email,"Welcome!",
"Your account was created successfully."
);
smtpClient.Send(message);
}
}
Good example:
public class UserService{
public void CreateUser(string email) {
var user = new User(email);
_repository.Save(user);
_emailService.SendWelcome(email);
}
}
O β Open/Closed Principle (OCP)
Code should be open for extension but closed for modification. This means you should be able to add new behavior without changing existing code. In the bad example below, whenever a new discount is added, you edit the function.
Bad example:
public class DiscountService {
public double Calculate(string type) {
if (type == "VIP") return 0.2;
if (type == "Regular") return 0.1;
if (type == "Employee") return 0.3;
return 0;
}
}
Good example:
public interface IDiscount {
double Calculate();
}
public class VipDiscount : IDiscount{
public double Calculate() => 0.2;
}
public class RegularDiscount : IDiscount {
public double Calculate() => 0.1;
}
public class DiscountService {
public double Calculate(IDiscount discount) {
return discount.Calculate();
}
}
L- Liskov Substitution Principle (LSP)
Subtypes must be replaceable for their base types without breaking the system. This means you should be able to use a subclass anywhere the base class is expected, without causing issues. In the bad example below, the subclass breaks the expected behavior by throwing an exception.
Bad example:
public class Bird {
public virtual void Fly() { }
}
public class Penguin : Bird {
public override void Fly() {
throw new Exception("Penguins can't fly");
}
}
Bird bird = new Penguin();
bird.Fly(); // throws exception
Good example:
public abstract class Bird { }
public interface IFlyingBird {
void Fly();
}
public class Eagle : Bird, IFlyingBird {
public void Fly() { }
}
public class Penguin : Bird { }
I- Interface Segregation Principle (ISP)
Clients should not depend on methods they do not use. The example below looks like the one from LSP, but the difference here it's about dependency minimization, and the other one is about behavioral contracts.
Bad example:
public interface IWorker
{
void Work();
void Eat();
}
public class Robot : IWorker
{
public void Work() { }
public void Eat()
{
//forced to implement this method even though it doesn't use it
}
}
Good example:
public interface IWorkable {
void Work();
}
public interface IEatable {
void Eat();
}
public class Human : IWorkable, IEatable {
public void Work() { }
public void Eat() { }
}
public class Robot : IWorkable {
public void Work() { }
}
D- Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions. In other words, donβt depend on concrete implementations β depend on abstractions.
Bad example:
public class UserService
{
private EmailService _emailService = new EmailService();
public void CreateUser(string email)
{
// logic
_emailService.Send(email);
}
}
Good example:
public interface IEmailService{
void Send(string email);
}
public class EmailService : IEmailService{
public void Send(string email) { }
}
public class UserService{
private readonly IEmailService _emailService;
public UserService(IEmailService emailService) {
_emailService = emailService;
}
public void CreateUser(string email) {
_emailService.Send(email);
}
}
Top comments (0)