DEV Community

a1excpunk
a1excpunk

Posted on

Understanding SOLID Principles with C# (short intro)

The SOLID principles are a set of design guidelines in object-oriented programming aimed at creating robust, maintainable, and scalable software. Coined by Robert C. Martin (Uncle Bob), these principles help developers avoid common pitfalls and write cleaner code.

S: Single Responsibility Principle (SRP)

Definition: A class should have only one reason to change.

Explanation: Each class should focus on a single responsibility or functionality. This makes code easier to understand, test, and maintain.

Example:

Instead of creating a User class that handles data storage and email notifications, split it into User for managing user data and EmailService for handling email notifications.

public class UserService 
{ 
    public void AddUser(string name) 
    { 
       // Implement logic 
    } 
}

public class EmailService 
{ 
    public void SendEmail(string email) 
    { 
        // Implement logic 
    } 
}

Enter fullscreen mode Exit fullscreen mode

O: Open/Closed Principle (OCP)

Definition: Classes should be open for extension but closed for modification.

Explanation: Code should allow new functionality to be added without altering existing code. This promotes stability and reduces the risk of bugs.

Example:

Instead of modifying a class to add a new type of payment method, use interfaces or inheritance to extend functionality.

public interface IPayment 
{ 
    void Pay(); 
}

public class CardPayment : IPayment 
{ 
    public void Pay() 
    { 
        // Card payment 
    } 
}

public class PaymentProcessor 
{ 
    public void Process(IPayment payment) 
    { 
        payment.Pay(); 
    } 
}

Enter fullscreen mode Exit fullscreen mode

L: Liskov Substitution Principle (LSP)

Definition: Subtypes must be substitutable for their base types without altering the correctness of the program.

Explanation: If a class B is a subclass of class A, then objects of type A should be replaceable with objects of type B without breaking the application.

Example:

If Rectangle is a superclass and Square inherits from it, ensure the subclass doesn’t alter behavior, such as how dimensions are set.

public interface IShape 
{ 
    int Area(); 
}

public class Rectangle : IShape 
{ 
    public int Area() => Width * Height; 
}

public class Square : IShape 
{ 
    public int Area() => Side * Side; 
}

Enter fullscreen mode Exit fullscreen mode

I: Interface Segregation Principle (ISP)

Definition: A class should not be forced to implement interfaces it does not use.

Explanation: Split large interfaces into smaller, more specific ones. This ensures classes implement only what they need.

Example:

Instead of one large Animal interface with methods like Fly(), Swim(), and Run(), create specific interfaces like Flyable, Swimmable, and Runnable.

public interface IFlyable 
{ 
    void Fly(); 
}

public interface IWalkable 
{ 
    void Walk(); 
}

public class Bird : IFlyable, IWalkable 
{ 
    public void Fly() 
    { 
        // Flying logic 
    }

    public void Walk() 
    { 
        // Walking logic 
    } 
}

Enter fullscreen mode Exit fullscreen mode

D: Dependency Inversion Principle (DIP)

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

Explanation: Rely on abstractions rather than concrete implementations to reduce coupling between components.

Example:

Instead of hardcoding a SQLDatabase in a class, use an interface like IDatabase, allowing easy switching to other databases (e.g., NoSQL).

public interface IDatabase 
{ 
    void Save(string data); 
}

public class SQLDatabase : IDatabase 
{ 
    public void Save(string data) 
    { 
        // Save to SQL 
    } 
}

public class DataManager 
{ 
    public DataManager(IDatabase db) 
    { 
        db.Save("data"); 
    } 
}

Enter fullscreen mode Exit fullscreen mode

Why Use SOLID?

  1. Better Maintainability: Easier to understand and modify code.
  2. Scalability: Adapts well to new features or requirements.
  3. Reduced Bugs: Minimizes side effects when changes are made.
  4. Reusability: Code can be reused across projects.

Top comments (0)