SOLID
The SOLID principles are fundamental to object-oriented design, fostering modular, testable, and adaptable code.
1. Single Responsibility Principle (SRP)
A class should have one and only one reason to change, meaning that a class should have only one job.
This principle ensures that a class has a single responsibility, making it easier to maintain and modify without impacting other parts of the system. For example, a ReportGenerator class should handle report creation, while a ReportSaver class saves reports to storage.
2. Open/Closed Principle (OCP)
Objects or entities should be open for extension but closed for modification.
This principle encourages adding new functionality through extension rather than modifying existing code. For instance, if you have a Shape interface, new shapes like Circle or Square can be added without altering the existing implementation.
3. Liskov Substitution Principle (LSP)
Derived classes must be substitutable for their base classes
A subclass should not break the behaviour expected from its superclass. For example, if a Bird class has a method fly(), a Penguin subclass shouldn't override it with a "not implemented" exception—it breaks the principle.
4. Interface Segregation Principle (ISP)
A client should never be forced to implement an interface that it doesn’t use, or clients shouldn’t be forced to depend on methods they do not use.
Instead of having one large interface, split it into smaller, more specific interfaces. For example, instead of a Vehicle interface with unrelated methods like fly() and drive(), create separate interfaces like FlyingVehicle and DrivingVehicle.
5. Dependency Inversion Principle (DIP)
Depend on abstractions, not on concretions
This principle suggests that high-level modules should not depend on low-level modules. Both should depend on abstractions. Dependency injection frameworks often embody this principle.
DRY (Don’t Repeat Yourself)
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system
Duplication in code leads to inconsistencies and additional maintenance efforts. If you find yourself repeating code, abstract it into reusable functions or modules.
YAGNI (You Ain’t Gonna Need It)
Don’t implement something until you actually need it
Avoid speculative development. Over-engineering adds unnecessary complexity and often wastes time. Build only what you need for the current requirements.
KISS (Keep It Simple, Stupid)
Simplicity is the ultimate sophistication
Write code that is simple to read, understand, and maintain. While overly complex solutions may seem clever, they often lead to bugs and technical debt.
Law of Demeter (LoD)
Talk only to your immediate friends
A module should only communicate with its direct dependencies and not with the dependencies of those dependencies. For example, avoid chaining method calls like a.getB().getC().doSomething();
. Instead, encapsulate such interactions.
Composition Over Inheritance
Favor composition over inheritance
Inheritance often leads to tightly coupled code and an inflexible hierarchy. Composition, on the other hand, promotes reusability by combining behaviours through interfaces or mixins. For example, instead of a monolithic Animal
class with dozens of subclasses, create composable behaviours like Swimmer
, Flyer
, or Walker
.
Encapsulate What Varies
Identify the aspects of your application that vary and separate them from what stays the same
This principle is fundamental to design patterns like Strategy and Factory. For example, if you have different ways to calculate discounts, encapsulate the logic into separate classes instead of hardcoding it.
Hollywood Principle
Don’t call us, we’ll call you
This principle encourages frameworks or inversion of control, where the framework dictates the flow. For instance, in an event-driven system, the framework invokes your code in response to specific events.
Program Against Abstractions
Depend on interfaces or abstract classes rather than concrete implementations
This principle promotes flexibility and testability. For example, instead of directly using a concrete FileLogger class, depend on a Logger interface, which allows you to swap implementations easily.
Books
Some recommended books to explore the concepts further:
Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin) (Robert C. Martin Series)
Pragmatic Programmer, The: Your journey to mastery, 20th Anniversary Edition