DEV Community

Sardar Mudassar Ali Khan
Sardar Mudassar Ali Khan

Posted on

1

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.

Image of Datadog

How to Diagram Your Cloud Architecture

Cloud architecture diagrams provide critical visibility into the resources in your environment and how they’re connected. In our latest eBook, AWS Solution Architects Jason Mimick and James Wenzel walk through best practices on how to build effective and professional diagrams.

Download the Free eBook

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more