Mastering SOLID Principles in C# Building Robust and Maintainable Software

sardarmudassaralikhan

Sardar Mudassar Ali Khan

Posted on December 25, 2023

Mastering SOLID Principles in C# Building Robust and Maintainable Software

In C#, the SOLID principles are a set of five design principles that help make software more understandable, flexible, and maintainable. Here they are with examples:

1. Single Responsibility Principle (SRP)

A class should have only one reason to change.

Example:

// Without SRP
public class Employee {
    public void CalculateSalary() {
        // Calculates salary
    }

    public void GenerateReport() {
        // Generates employee report
    }
}
Enter fullscreen mode Exit fullscreen mode

Refactored using SRP:

public class Employee {
    public void CalculateSalary() {
        // Calculates salary
    }
}

public class ReportGenerator {
    public void GenerateReport(Employee employee) {
        // Generates employee report
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Open/Closed Principle (OCP)

Software entities should be open for extension but closed for modification.

Example:

// Without OCP
public class Circle {
    public double Radius { get; set; }

    public double Area() {
        return Math.PI * Radius * Radius;
    }
}

// Adding a new shape requires modifying existing code.
public class Square {
    public double Side { get; set; }

    public double Area() {
        return Side * Side;
    }
}
Enter fullscreen mode Exit fullscreen mode

Refactored using OCP:

public abstract class Shape {
    public abstract double Area();
}

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

    public override double Area() {
        return Math.PI * Radius * Radius;
    }
}

public class Square : Shape {
    public double Side { get; set; }

    public override double Area() {
        return Side * Side;
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Liskov Substitution Principle (LSP)

Objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program.

Example:

// Without LSP
public class Rectangle {
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }

    public int CalculateArea() {
        return Width * Height;
    }
}

public class Square : Rectangle {
    public override int Width {
        get => base.Width;
        set {
            base.Width = value;
            base.Height = value;
        }
    }

    public override int Height {
        get => base.Height;
        set {
            base.Width = value;
            base.Height = value;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Refactored using LSP:

public abstract class Shape {
    public abstract int Area();
}

public class Rectangle : Shape {
    public int Width { get; set; }
    public int Height { get; set; }

    public override int Area() {
        return Width * Height;
    }
}

public class Square : Shape {
    public int Side { get; set; }

    public override int Area() {
        return Side * Side;
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Interface Segregation Principle (ISP)

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

Example:

// Without ISP
public interface IWorker {
    void Work();
    void TakeBreak();
    void ClockOut();
}

// For a Robot, some methods are irrelevant.
public class Robot : IWorker {
    public void Work() {
        // Perform work
    }

    public void TakeBreak() {
        // Not applicable
    }

    public void ClockOut() {
        // Not applicable
    }
}
Enter fullscreen mode Exit fullscreen mode

Refactored using ISP:

public interface IWorker {
    void Work();
}

public interface IBreakable {
    void TakeBreak();
}

public interface IClockable {
    void ClockOut();
}

public class Robot : IWorker {
    public void Work() {
        // Perform work
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Dependency Inversion Principle (DIP)

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

Example:

// Without DIP
public class LightSwitch {
    private readonly Bulb _bulb;

    public LightSwitch() {
        _bulb = new Bulb();
    }

    public void Toggle() {
        _bulb.Toggle();
    }
}

public class Bulb {
    public void Toggle() {
        // Toggle the bulb
    }
}
Enter fullscreen mode Exit fullscreen mode

Refactored using DIP:

public interface ISwitchable {
    void Toggle();
}

public class LightSwitch {
    private readonly ISwitchable _device;

    public LightSwitch(ISwitchable device) {
        _device = device;
    }

    public void Toggle() {
        _device.Toggle();
    }
}

public class Bulb : ISwitchable {
    public void Toggle() {
        // Toggle the bulb
    }
}
Enter fullscreen mode Exit fullscreen mode

These principles aim to create more maintainable, modular, and scalable code, promoting good design practices in object-oriented programming.

💖 💪 🙅 🚩
sardarmudassaralikhan
Sardar Mudassar Ali Khan

Posted on December 25, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related