SOLID Principles in C#

SOLID Principles in C#

The SOLID principles are a set of five design principles intended to make software designs more understandable, flexible, and maintainable. These principles are particularly useful in object-oriented programming (OOP) and are widely adopted in various programming languages, including C#. The SOLID acronym stands for:

  1. Single Responsibility Principle (SRP)
  2. Open/Closed Principle (OCP)
  3. Liskov Substitution Principle (LSP)
  4. Interface Segregation Principle (ISP)
  5. Dependency Inversion Principle (DIP)

1. Single Responsibility Principle (SRP)

Definition: A class should have only one reason to change, meaning it should have only one responsibility or job.

Example in C#:

?

?

public class User {

??? public string Name { get; set; }

??? public string Email { get; set; }

???

??? public void Save() {

??????? // Code to save user data to a database

??? }

}

Violation: The User class is handling both the user data and its persistence. Following SRP, we should separate these concerns:

?

?

public class User {

??? public string Name { get; set; }

??? public string Email { get; set; }

}

?

public class UserRepository {

??? public void Save(User user) {

??????? // Code to save user data to a database

??? }

}

2. Open/Closed Principle (OCP)

Definition: Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.

Example in C#:

?

?

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 Rectangle : Shape {

??? public double Width { get; set; }

??? public double Height { get; set; }

???

??? public override double Area() {

??????? return Width * Height;

??? }

}

Explanation: By using abstraction, new shapes can be added without modifying the existing code.

3. Liskov Substitution Principle (LSP)

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

Example in C#:

?

?

public class Bird {

??? public virtual void Fly() {

??????? // Implementation for flying

??? }

}

?

public class Ostrich : Bird {

??? public override void Fly() {

??????? throw new NotImplementedException("Ostriches can't fly!");

??? }

}

Violation: Ostrich should not override Fly method if it can't fly. A better design might involve segregating birds into those that can fly and those that can't:

?

?

public abstract class Bird {

??? // Common bird properties and methods

}

?

public interface IFlyingBird {

??? void Fly();

}

?

public class Sparrow : Bird, IFlyingBird {

??? public void Fly() {

??????? // Implementation for flying

??? }

}

?

public class Ostrich : Bird {

??? // Ostrich-specific implementation

}

4. Interface Segregation Principle (ISP)

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

Example in C#:

?

?

public interface IWorker {

??? void Work();

??? void Eat();

}

?

public class Worker : IWorker {

??? public void Work() {

??????? // Implementation for working

??? }

?

??? public void Eat() {

??????? // Implementation for eating

??? }

}

?

public class Robot : IWorker {

??? public void Work() {

??????? // Implementation for working

??? }

?

??? public void Eat() {

??????? throw new NotImplementedException();

??? }

}

Violation: Robot class should not implement Eat method if it doesn't eat. Splitting the interface can solve this:

?

?

public interface IWorker {

??? void Work();

}

?

public interface IHumanWorker : IWorker {

??? void Eat();

}

?

public class Worker : IHumanWorker {

??? public void Work() {

??????? // Implementation for working

??? }

?

??? public void Eat() {

??????? // Implementation for eating

??? }

}

?

public class Robot : IWorker {

??? public void Work() {

??????? // Implementation for working

??? }

}

5. Dependency Inversion Principle (DIP)

Definition: 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.

Example in C#:

?

?

public class LightBulb {

??? public void TurnOn() {

??????? // Implementation to turn on the light

??? }

}

?

public class Switch {

??? private LightBulb _bulb = new LightBulb();

?

??? public void Operate() {

??????? _bulb.TurnOn();

??? }

}

Violation: Switch is tightly coupled to LightBulb. To follow DIP:

?

?

public interface ILight {

??? void TurnOn();

}

?

public class LightBulb : ILight {

??? public void TurnOn() {

??????? // Implementation to turn on the light

??? }

}

?

public class Switch {

??? private ILight _light;

?

??? public Switch(ILight light) {

??????? _light = light;

??? }

?

??? public void Operate() {

??????? _light.TurnOn();

??? }

}

Conclusion

By adhering to the SOLID principles, developers can create more robust, maintainable, and scalable software. In C#, these principles help in designing classes and interfaces that are easier to understand, test, and extend, leading to more efficient and effective development processes.

?

Deep Lakharia

Lead Consultant @ ITC Infotech | 9+ Years of Experience | .Net Developer | .Net Framework | .Net Core | ASP.Net MVC | C# | Web API | Angular | SQL | Azure | Web Application Development | Full Stack Developer

4 个月

simple and easy ??

要查看或添加评论,请登录

社区洞察