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)
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.
?
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 ??