SOLID Principles - hmislk/hmis GitHub Wiki
SOLID is a mnemonic acronym for five design principles intended to make object-oriented designs more understandable, flexible, and maintainable. These principles were introduced by Robert C. Martin (also known as "Uncle Bob").
When applied together, these principles help developers create systems that are easier to maintain and extend over time. They are fundamental concepts in object-oriented programming that help manage and reduce coupling, making the code more robust and scalable.
The five SOLID principles are:
- S - Single Responsibility Principle
- O - Open-Closed Principle
- L - Liskov Substitution Principle
- I - Interface Segregation Principle
- D - Dependency Inversion Principle
Single Responsibility Principle (SRP)
A class should have only one reason to change.
This principle states that a class should have only one job or responsibility. If a class has more than one responsibility, those responsibilities become coupled. Changes to one responsibility might force changes in the other, even if they are unrelated.
Goal: To separate concerns, which leads to classes that are easier to understand, test, and maintain.
Open-Closed Principle (OCP)
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
This principle means that you should be able to add new functionality to a class without changing its existing code. This is often achieved by using interfaces, abstract classes, or inheritance. New functionality can be added by creating new subclasses that implement or extend these abstractions.
Goal: To make the system more stable by preventing changes to existing, tested code while still allowing for new features to be added.
Liskov Substitution Principle (LSP)
Subtypes must be substitutable for their base types.
Named after Barbara Liskov, this principle states that objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program. If you have a function that works with a base class, it should also work with any of its derived classes without any special checks or modifications.
Goal: To ensure that inheritance is used correctly, maintaining a consistent and predictable hierarchy where subclasses genuinely extend the behavior of their parent classes without breaking it.
Interface Segregation Principle (ISP)
Clients should not be forced to depend upon interfaces they do not use.
This principle suggests that it's better to have many small, specific interfaces (known as "role interfaces") rather than one large, general-purpose interface. If a class implements an interface with methods it doesn't need, it's forced to provide "dummy" implementations, which is a sign of a poor design.
Goal: To reduce the "fat" in interfaces and prevent clients from being coupled to methods they don't require. This leads to a more decoupled system.
Dependency Inversion Principle (DIP)
- High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g., interfaces).
- Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
This principle reverses the traditional flow of dependencies. Instead of a high-level policy class (e.g., a business logic class) depending directly on a low-level detail class (e.g., a database logger), both should depend on an abstraction, like an ILogger interface. The high-level class uses the interface, and the low-level class implements it.
Goal: To decouple high-level and low-level modules, making the system more flexible. This allows you to easily swap out low-level implementations (e.g., change from a database logger to a file logger) without changing the high-level business logic.