- Published on
SOLID Principles in Object-Oriented Design
- Authors

- Name
- Skim
OOP is a useful paradigm for abstraction, but as projects grow in complexity, maintaining and extending the codebase can become challenging due to the abstraction itself. The SOLID principles, introduced by Robert C. Martin, are five design guidelines for writing maintainable, flexible object-oriented code.
- SRP (Single Responsibility): A class should have only one reason to change. This keeps classes focused and prevents them from becoming too complex.
- OCP (Open-Closed): Software entities should be open for extension but closed for modification. Add new functionality through inheritance or interfaces rather than changing existing code.
- LSP (Liskov Substitution): Objects of a subclass should be substitutable for their superclass without breaking the program.
- ISP (Interface Segregation): Clients should not be forced to depend on interfaces they don't use. Smaller, specific interfaces are better than large monolithic ones.
- DIP (Dependency Inversion): 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 of ISP
// Violation of ISP
interface Worker {
void work();
void eat();
}
class Engineer implements Worker {
public void work() {
// engineer-specific work
}
public void eat() {
// engineer-specific eating
}
}
// Clients forced to implement unnecessary eat() method
class Robot implements Worker {
public void work() {
// robot-specific work
}
public void eat() {
// robot doesn't eat, unnecessary implementation
}
// Adhering to ISP
interface Workable {
void work();
}
interface Eatable {
void eat();
}
class Engineer implements Workable, Eatable {
public void work() {
// engineer-specific work
}
public void eat() {
// engineer-specific eating
}
}
class Robot implements Workable {
public void work() {
// robot-specific work
}
}
Example of DIP
# Violation of DIP
class LightBulb:
def turn_on(self):
pass
class Switch:
def __init__(self, bulb):
self.bulb = bulb
def operate(self):
self.bulb.turn_on()
# High-level Switch class depends on low-level LightBulb class
# Changes in LightBulb may affect Switch
bulb = LightBulb()
switch = Switch(bulb)
# Adhering to DIP
class Switchable:
def turn_on(self):
pass
class LightBulb(Switchable):
def turn_on(self):
# light bulb-specific logic
class Switch:
def __init__(self, device):
self.device = device
def operate(self):
self.device.turn_on()
# High-level Switch class depends on abstraction (Switchable)
# Changes in LightBulb do not affect Switch
bulb = LightBulb()
switch = Switch(bulb)