Understanding SOLID Principles in C# with Examples

Pavan EduPavan Edu
4 min read

The SOLID principles are five fundamental design principles intended to make software designs more understandable, flexible, and maintainable. These principles are particularly relevant in object-oriented programming and can significantly improve the quality of your C# code.

Below, we will explore each principle using relatable examples involving Employee, Product, and Sales contexts.

1. Single Responsibility Principle (SRP)

Definition: A class should have only one reason to change, meaning it should have only one job or responsibility.

Example: Consider anEmployeeclass that handles both employee data management and payroll processing. This violates SRP because it has multiple responsibilities.

Before Refactoring:

public class Employee
{
    public string Name { get; set; }
    public decimal Salary { get; set; }

    public void Save()
    {
        // Save employee data
    }

    public void ProcessPayroll()
    {
        // Process payroll
    }
}

After Refactoring:

public class Employee
{
    public string Name { get; set; }
    public decimal Salary { get; set; }
}

public class PayrollProcessor
{
    public void ProcessPayroll(Employee employee)
    {
        // Process payroll for the employee
    }
}

In this refactoring, theEmployeeclass now has a single responsibility—managing employee data—while payroll processing is handled by a separate PayrollProcessorclass.

2. Open/Closed Principle (OCP)

Definition: Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.

Example: Imagine aProductclass that calculates discounts. If we want to add new discount types, we shouldn't modify the existingProductclass.

Before Refactoring:

public class Product
{
    public decimal Price { get; set; }

    public decimal CalculateDiscount(string discountType)
    {
        if (discountType == "Seasonal")
            return Price * 0.10m;
        else if (discountType == "Clearance")
            return Price * 0.20m;
        return 0;
    }
}

After Refactoring:

public interface IDiscountStrategy
{
    decimal ApplyDiscount(decimal price);
}

public class SeasonalDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal price) => price * 0.10m;
}

public class ClearanceDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal price) => price * 0.20m;
}

public class Product
{
    public decimal Price { get; set; }

    public decimal CalculateDiscount(IDiscountStrategy discountStrategy)
    {
        return discountStrategy.ApplyDiscount(Price);
    }
}

Now, to add new discount types, you can create new classes implementing IDiscountStrategywithout modifying theProductclass.

3. Liskov Substitution Principle (LSP)

Definition: Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.

Example: Suppose we have a base class SalesPersonand derived classes CommissionedSalesPerson and SalariedSalesPerson. Both subclasses should behave correctly when used interchangeably.

public abstract class SalesPerson
{
    public abstract decimal CalculateEarnings();
}

public class CommissionedSalesPerson : SalesPerson
{
    public decimal Commission { get; set; }

    public override decimal CalculateEarnings() => Commission;
}

public class SalariedSalesPerson : SalesPerson
{
    public decimal Salary { get; set; }

    public override decimal CalculateEarnings() => Salary;
}

In this structure, both subclasses can replace the base class without altering the program's behavior.

4. Interface Segregation Principle (ISP)

Definition: No client should be forced to depend on methods it does not use.

Example: Instead of having a large interface for all worker types, we can create smaller interfaces specific to their functionalities.

Before Refactoring:

public interface IWorker
{
    void Work();
    void Eat();
}

public class HumanWorker : IWorker
{
    public void Work() { /* Working */ }
    public void Eat() { /* Eating */ }
}

public class RobotWorker : IWorker
{
    public void Work() { /* Working */ }
    public void Eat() => throw new NotImplementedException();
}

After Refactoring:

public interface IWorkable
{
    void Work();
}

public interface IEatable
{
    void Eat();
}

public class HumanWorker : IWorkable, IEatable
{
    public void Work() { /* Working */ }
    public void Eat() { /* Eating */ }
}

public class RobotWorker : IWorkable
{
    public void Work() { /* Working */ }
}

Now, classes only implement interfaces relevant to them, adhering to ISP.

5. Dependency Inversion Principle (DIP)

Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions.

Example: In a sales application, instead of directly using a concrete implementation for sending notifications, we can use an abstraction.

public interface INotificationService
{
    void Send(string message);
}

public class EmailNotificationService : INotificationService
{
    public void Send(string message) { /* Send email */ }
}

public class SalesManager
{
    private readonly INotificationService _notificationService;

    public SalesManager(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }

    public void NotifySale(string message)
    {
        _notificationService.Send(message);
    }
}

In this example, SalesManager depends on the abstraction INotificationService, allowing for flexibility in choosing different notification methods without changing its code.

Conclusion

By applying the SOLID principles, developers can create systems that are easier to maintain, extend, and understand. Each principle serves to enhance code quality and adaptability in software development projects. Following these principles leads to more robust applications that can evolve with changing requirements over time.

0
Subscribe to my newsletter

Read articles from Pavan Edu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Pavan Edu
Pavan Edu