SOLID Principal Using C# Introduction with Complete Example
Tags: webdev, programming, beginners, codenewbie
Published: 2023-05-19 13:17:24

SOLID is an acronym for five design principles that help create maintainable and flexible software systems. Here's an example for each principle:
### 1. Single Responsibility Principle (SRP):
   The SRP states that a class should have only one reason to change. In other words, a class should have a single responsibility.
```csharp
public class Customer
{
    public string Name { get; set; }
    public string Email { get; set; }
    
    public void SendEmail(string message)
    {
        // Code to send an email
    }
}
```
In this example, the `Customer` class has a single responsibility, which is to represent a customer. The email-sending functionality has been separated into a separate class.
### 2. Open/Closed Principle (OCP):
   The OCP states that software entities should be open for extension but closed for modification. In other words, you should be able to add new functionality without modifying existing code.
```csharp
public interface IShape
{
    double CalculateArea();
}

public class Circle : IShape
{
    public double Radius { get; set; }
    
    public double CalculateArea()
    {
        return Math.PI * Math.Pow(Radius, 2);
    }
}

public class Rectangle : IShape
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    public double CalculateArea()
    {
        return Width * Height;
    }
}
```
In this example, the `IShape` interface defines a contract for calculating the area of a shape. The `Circle` and `Rectangle` classes implement this interface and provide their own implementation of the `CalculateArea` method. If we want to add a new shape, we can do so without modifying the existing code.
### 3. Liskov Substitution Principle (LSP):
   The LSP states that objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program.

```csharp
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Animal is making a sound.");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Dog is barking.");
    }
}

public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Cat is meowing.");
    }
}
```
In this example, the `Animal` class is the superclass, and `Dog` and `Cat` are its subclasses. Both subclasses can be used interchangeably wherever an `Animal` object is expected.
### 4. Interface Segregation Principle (ISP):
   The ISP states that clients should not be forced to depend on interfaces they do not use. In other words, interfaces should be specific to the needs of the clients.

```csharp
public interface IOrder
{
    void ProcessOrder();
}

public interface IInvoice
{
    void GenerateInvoice();
}

public class OnlineOrder : IOrder
{
    public void ProcessOrder()
    {
        // Code to process an online order
    }
}

public class PointOfSaleOrder : IOrder, IInvoice
{
    public void ProcessOrder()
    {
        // Code to process a point-of-sale order
    }
    
    public void GenerateInvoice()
    {
        // Code to generate an invoice
    }
}
```

In this example, we have two interfaces, `IOrder` and `IInvoice`. The `OnlineOrder` class only needs to implement the `IOrder` interface, while the `PointOfSaleOrder` class implements both `IOrder` and `IInvoice` interfaces
. Clients can depend on specific interfaces based on their needs.
### 5. Dependency Inversion Principle (DIP):
   The DIP states that high-level modules should not depend on low-level modules. Both should depend on abstractions. In other words, dependencies should be defined through interfaces or abstract classes.

```csharp
public interface ILogger
{
    void Log(string message);
}

public class FileLogger : ILogger
{
    public void Log(string message)
    {
        // Code to log message to a file
    }
}

public class DatabaseLogger : ILogger
{
    public void Log(string message)
    {
        // Code to log message to a database
    }
}

public class CustomerService
{
    private readonly ILogger _logger;
    
    public CustomerService(ILogger logger)
    {
        _logger = logger;
    }
    
    public void AddCustomer(string name)
    {
        // Code to add a customer
        _logger.Log("Customer added: " + name);
    }
}
```

In this example, the `CustomerService` class depends on the `ILogger` interface rather than concrete logger implementations. The actual logger implementation is injected into the `CustomerService` class through constructor injection.
These examples demonstrate the SOLID principles in action using C#. Applying these principles can lead to more maintainable, flexible, and testable code.