DEV Community

Cover image for SOLID Principles: A Quick Guide (.NET examples)
Phil the Dev
Phil the Dev

Posted on

SOLID Principles: A Quick Guide (.NET examples)

Recently, I was thinking about some topics for a short and knowledge-refreshing article. One subject that I rarely hear being talked about, but is really important in software development, are the SOLID principles. For a lot of experienced developers, these "rules" may seem pretty clear, yet I often come across code changes that don't follow these principles. Sometimes, I even find some that I've made myself, so no disrespect intended.

To enhance the value of this article, I've included some basic explanations and examples in C#

Single Responsibility Principle (SRP)

Each class should have one, and only one, reason to change. This means a class should only have one job.

Example:

public class Order
{
    public void CalculateTotalSum() { /*...*/ }
    public void GetItems() { /*...*/ }
    public void PrintOrder() { /*...*/ }
    public void ShowOrder() { /*...*/ }
}
Enter fullscreen mode Exit fullscreen mode

This Order class violates SRP because it has more than one responsibility. A better approach is to split this into several classes, each handling a single concern.


Open-Closed Principle (OCP)

Software entities should be open for extension but closed for modification. This encourages stability while allowing system growth.

Example:

public class Rectangle
{
    public double Height { get; set; }
    public double Width { get; set; }
}
public class AreaCalculator
{
    public double TotalArea(Rectangle[] arrRectangles)
    {
        double area = 0;
        foreach (var objRectangle in arrRectangles)
        {
            area += objRectangle.Height * objRectangle.Width;
        }
        return area;
    }
}
Enter fullscreen mode Exit fullscreen mode

In this example, if we want to add a new shape, we'd have to modify the AreaCalculator class, which violates OCP.


Liskov Substitution Principle (LSP)

This principle insists that subclasses must be substitutable for their base classes without affecting functionality.

Example:

public class Vehicle
{
    public virtual void StartEngine() { /*...*/ }
}

public class ElectricCar : Vehicle
{
    public override void StartEngine() { /*...*/ }
}

public class Bicycle : Vehicle
{
    public override void StartEngine() { /*...*/ }
}
Enter fullscreen mode Exit fullscreen mode

Here, a Bicycle doesn't have an engine to start, so substituting a Vehicle with a Bicycle would cause issues, thus violating LSP. A better approach would be to have a separate class for vehicles with engines.


Interface Segregation Principle (ISP)

No client should be forced to depend on interfaces they do not use.

Example

public interface IWorker
{
    void Work();
    void Eat();
}
Enter fullscreen mode Exit fullscreen mode

Here, if a robot implements IWorker, the Eat method is useless. This violates ISP. It's better to break this into two interfaces: IWorker and IEater.


Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules, but both should depend on abstractions. This reduces tight coupling.

Example:

public class Email
{
    public void SendEmail() { /*...*/ }
}
public class Notification
{
    private Email _email;
    public Notification()
    {
        _email = new Email();
    }
}
Enter fullscreen mode Exit fullscreen mode

here, the Notification class is tightly coupled with the Email class. Using DIP, we can introduce an interface to decouple them.


Despite their importance in writing maintainable and scalable code, these principles are frequently overlooked. For me it was a short, simple and much needed refresher. Hopefully for any other reader too 😀
Feel free to comment your thoughts

Happy coding!

Top comments (0)