SOLID Principles
Tags: solid, softwareengineering, architecture
Published: 2024-03-05 21:54:21

As a software developer for a decade, I've come to realize that myself and a significant percentage of programmers are not prioritizing good practices, or at least, these concerns are not among our top priorities.
However, I decide revert this and learn to become a better professional. 

I’m writing these articles as my personal notes and hope motivate you to be a better programmer as well.

So let’s start for the beginning: SOLID principles!

The SOLID principles are a set of five design principles intended to make software designs more understandable, flexible, and maintainable. They are:

1 - **Single Responsibility Principle (SRP)**: A class should have only one reason to change, meaning it should have only one responsibility or job.
We all did the mistake to have a huge class that do more than one thing and different things with different contexts. 

```

public class UserManagementSystem
{
    public void AddUser(User user)
    {
        // Add user to the database
    }

    public void DeleteUser(User user)
    {
        // Delete user from the database
    }

    public void SendEmailToUser(User user, string message)
    {
        // Send email to the user
    }

    public void GenerateReportForUser(User user)
    {
        // Generate report for the user
    }
}

```
In this example, the UserManagementSystem class is responsible for multiple tasks: adding/deleting users, sending emails, and generating reports. This violates the SRP because the class has multiple reasons to change: if the database structure changes, if the email service changes, or if the report generation logic changes. It would be better to separate these responsibilities into distinct classes.


2 -  **Open/Closed Principle (OCP)**: Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. This means that you should be able to add new functionality without altering existing code.

```

public class Shape
{
    public string Type { get; set; }

    public double Area()
    {
        if (Type == "Rectangle")
        {
            Rectangle rectangle = new Rectangle();
            return rectangle.Length * rectangle.Width;
        }
        else if (Type == "Circle")
        {
            Circle circle = new Circle();
            return Math.PI * Math.Pow(circle.Radius, 2);
        }
        // More shapes and calculations can be added here, but violates OCP
        else
        {
            throw new InvalidOperationException("Unsupported shape type");
        }
    }
}

public class Rectangle
{
    public double Length { get; set; }
    public double Width { get; set; }
}

public class Circle
{
    public double Radius { get; set; }
}

```

In this example, the Shape class contains a method to calculate the area based on different shapes such as rectangles and circles. However, if a new shape is introduced, such as a triangle, the Area() method in the Shape class would need to be modified to accommodate the new shape. This violates the OCP because the class is not closed for modification; instead, it requires modification whenever a new shape is added.

3 - **Liskov Substitution Principle (LSP)**: Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program. In other words, derived classes should be substitutable for their base classes without altering the desirable properties of the program.

4 - **Interface Segregation Principle (ISP)**: A client should not be forced to depend on interfaces it does not use. Instead of one large interface, it is better to have smaller, specific interfaces.

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 promotes loose coupling between software modules.