# SOLID Principles

#### 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`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lukasz-nowakiewicz.gitbook.io/resources/python/solid-principles.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
