SOLID Principles

This is what ChatGPT provided.

SOLID Principles Examples

Single Responsibility Principle (SRP)

This principle states that a class should have only one reason to change. In other words, it should have only one responsibility.

class Employee:
    def __init__(self, name: str, salary: float):
        self.name = name
        self.salary = salary
    
    def get_name(self):
        return self.name
    
    def get_salary(self):
        return self.salary

class SalaryCalculator:
    def calculate_salary(self, employee: Employee):
        # Calculate salary based on employee's position, experience, etc.
        pass

In the above example, Employee class has only one responsibility - to store information about an employee. SalaryCalculator class has only one responsibility - to calculate the salary of an employee based on their information.

Open/Closed Principle (OCP)

This principle states that a class should be open for extension but closed for modification.

class Shape:
    def draw(self):
        pass
    
class Circle(Shape):
    def draw(self):
        print("Drawing a circle")
    
class Rectangle(Shape):
    def draw(self):
        print("Drawing a rectangle")
    
class ShapeDrawer:
    def __init__(self, shapes: List[Shape]):
        self.shapes = shapes
    
    def draw_all_shapes(self):
        for shape in self.shapes:
            shape.draw()

In the above example, Shape class is open for extension as we can add new shapes by creating new subclasses of Shape. However, Shape class is closed for modification as we don't need to modify the draw method when we add a new shape.

Liskov Substitution Principle (LSP)

This principle states that a subclass should be able to substitute its superclass without any problems.

class Animal:
    def move(self):
        pass
    
class Dog(Animal):
    def move(self):
        print("Walking")
    
class Bird(Animal):
    def move(self):
        print("Flying")
    
def make_animal_move(animal: Animal):
    animal.move()

In the above example, we can substitute Dog and Bird for Animal without any problems as they both have implemented the move method.

Interface Segregation Principle (ISP)

This principle states that a client should not be forced to implement methods which it does not use.

class Document:
    def open(self):
        pass
    
    def save(self):
        pass
    
class Editor:
    def __init__(self, document: Document):
        self.document = document
    
    def edit(self):
        self.document.open()
        # Edit the document
        self.document.save()

class PrintableDocument:
    def print(self):
        pass
    
class PrintableEditor:
    def __init__(self, document: PrintableDocument):
        self.document = document
    
    def print(self):
        self.document.print()

In the above example, Editor class uses only the open and save methods of Document class. PrintableEditor class uses only the print method of PrintableDocument class. Therefore, we can split Document and PrintableDocument into two separate interfaces - Openable and Printable, respectively.

Dependency Inversion Principle (DIP)

This principle states that high-level modules should not depend on low-level modules. Instead, both should depend on abstractions.

class UserService:
    def __init__(self, user_repository: UserRepository):
        self.user_repository = user_repository
    
    def get_user(self, user_id: int

Dependency Injection Principle (DIP)

This principle is a natural consequence of the Dependency Inversion Principle (DIP). It says that dependencies should be injected into an object from the outside, rather than created inside the object.

class MyService:
    def __init__(self, my_dependency: MyDependency):
        self.my_dependency = my_dependency
    
    def do_something(self):
        self.my_dependency.do_something_else()

In the above example, MyService depends on MyDependency, but it is not responsible for creating it. Instead, MyDependency is injected from the outside, which makes MyService more flexible and easier to test.

Composition Over Inheritance Principle

This principle states that you should prefer composition over inheritance. In other words, you should use object composition to achieve code reuse, rather than inheriting from a base class.

class Engine:
    def start(self):
        pass

class Car:
    def __init__(self, engine: Engine):
        self.engine = engine
    
    def start(self):
        self.engine.start()

In the above example, Car uses composition to reuse the functionality of Engine, rather than inheriting from it.

Don't Repeat Yourself Principle (DRY)

This principle states that you should not repeat yourself. In other words, you should avoid duplicating code, and instead, use abstraction and modularization to keep your code clean and maintainable.

class MyService:
    def __init__(self, my_dependency: MyDependency):
        self.my_dependency = my_dependency
    
    def do_something(self):
        self.my_dependency.do_something_else()
    
    def do_something_else(self):
        self.my_dependency.do_something_else()

In the above example, do_something and do_something_else both call my_dependency.do_something_else. Instead of duplicating the code, we can call do_something_else from do_something.

Last updated