DEV Community

Cover image for How can applying the SOLID principles make the code better?
Antonov Mike
Antonov Mike

Posted on • Updated on

How can applying the SOLID principles make the code better?

Applying the SOLID principles to your code can significantly enhance its quality, making it more understandable, flexible, maintainable, and scalable. Here's how each principle contributes to improving code quality:

1. Single Responsibility Principle (SRP)

The Single Responsibility Principle states that a class should have only one reason to change. This principle aims to separate behaviours so that if bugs arise as a result of your change, it won’t affect other unrelated behaviours.
If each class has only one reason to change, you make the code easier to maintain. Changes are more localized, reducing the risk of introducing bugs. And since the responsibilities of each class are more clearly defined, such code is easier to read.
No SRP

class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

    def save(self):
        pass

    def send_email(self):
        pass
Enter fullscreen mode Exit fullscreen mode

SRP

class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

class UserRepository:
    def save(self, user):
        pass

class EmailService:
    def send_email(self, user):
        pass
Enter fullscreen mode Exit fullscreen mode

To address business responsibilities, we might consider what business logic or rules apply to the User and how they can be encapsulated within a class.

2. Open/Closed Principle (OCP)

Classes should be open for extension, but closed for modification. This means that you should be able to add new functionality without changing the existing code.
OCP allows new functionality to be added without modifying existing code, making the system more adaptable to change. And you minimize the risk of introducing bugs when extending the functionality of your system.

from abc import ABC, abstractmethod

class Payment(ABC):
    @abstractmethod
    def process_payment(self, amount):
        pass

class CreditCardPayment(Payment):
    def process_payment(self, amount):
        print(f"Processing credit card payment of {amount}")

class OnlinePayment(Payment):
    def process_payment(self, amount):
        print(f"Processing online payment of {amount}")

class CryptoCurrencyPayment(Payment):
    def process_payment(self, amount):
        print(f"Processing crypto payment of {amount}")

# New class that uses a specific payment method passed as an argument
class PaymentProcessing:
    def concrete_payments(self, payment_method: Payment, amount):
        payment_method.process_payment(amount)

credit_card_payment = CreditCardPayment()
some_object = PaymentProcessing()
some_object.concrete_payments(credit_card_payment, 100)

online_payment = OnlinePayment()
some_object.concrete_payments(online_payment, 200)

crypto_payment = CryptoCurrencyPayment()
some_object.concrete_payments(crypto_payment, 300)
Enter fullscreen mode Exit fullscreen mode

3. Liskov Substitution Principle (LSP)

The Liskov Substitution Principle states that if a program is using a base class, it should be able to use any of its subclasses without the program knowing it. In other words, the subclasses should be substitutable for their base class.
LSP ensures that subclasses can be used in place of their base classes without altering the correctness of the program, making the code more robust. In addition, you can reuse code because code that works with the base class will also work with any of its subclasses.
No LSP

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def set_width(self, width):
        self.width = width

    def set_height(self, height):
        self.height = height

class Square(Rectangle):
    def __init__(self, side):
        super().__init__(side, side)

    def set_side(self, side):
        self.width = side
        self.height = side
Enter fullscreen mode Exit fullscreen mode

LSP

class Shape:
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * (self.radius ** 2)
Enter fullscreen mode Exit fullscreen mode

4. Interface Segregation Principle (ISP)

The Interface Segregation Principle states that no client should be forced to depend on interfaces they do not use. This means that a class should not have to implement methods it doesn't use. Clients should not be forced to depend on methods that they do not use.
By ensuring that interfaces are small and focused, so classes are not forced to depend on interfaces they do not use. This makes the system more flexible and adaptable to change.
No ISP

class Worker:
    def work(self):
        pass

    def eat(self):
        pass

class Robot(Worker):
    def work(self):
        pass

    def eat(self):
        raise NotImplementedError("Robots don't eat.")
Enter fullscreen mode Exit fullscreen mode

ISP

class Worker:
    def work(self):
        pass

class Eater:
    def eat(self):
        pass

class Human(Worker, Eater):
    def work(self):
        pass

    def eat(self):
        pass

class Robot(Worker):
    def work(self):
        pass
Enter fullscreen mode Exit fullscreen mode

5. Dependency Inversion Principle (DIP)

High-level modules depend on abstractions rather than on low-level modules. This makes the code more flexible and easier to understand. It becomes easier to write unit tests for individual components. Each component can be tested in isolation, without needing to consider the behavior of other components.
No DIP

class LightBulb:
    def __init__(self):
        self.is_on_state = False

    def turn_on(self):
        self.is_on_state = True
        print("Light is on")

    def turn_off(self):
        self.is_on_state = False
        print("Light is off")

    def is_on(self):
        return self.is_on_state


class ElectricPowerSwitch:
    def __init__(self):
        self.light_bulb = LightBulb()

    def press(self):
        if self.light_bulb.is_on():
            self.light_bulb.turn_off()
        else:
            self.light_bulb.turn_on()


switch = ElectricPowerSwitch()

switch.press()
switch.press()
Enter fullscreen mode Exit fullscreen mode

DIP

from abc import ABC, abstractmethod


class Switchable(ABC):
    @abstractmethod
    def turn_on(self):
        pass

    @abstractmethod
    def turn_off(self):
        pass

    @abstractmethod
    def is_on(self):
        pass


class LightBulb(Switchable):
    def __init__(self):
        self.is_on_state = False

    def turn_on(self):
        self.is_on_state = True
        print("Light is on")

    def turn_off(self):
        self.is_on_state = False
        print("Light is off")

    def is_on(self):
        return self.is_on_state


class ElectricPowerSwitch:
    def __init__(self, switchable: Switchable):
        self.switchable = switchable

    def press(self):
        if self.switchable.is_on():
            self.switchable.turn_off()
        else:
            self.switchable.turn_on()


light_bulb = LightBulb()
switch = ElectricPowerSwitch(light_bulb)

switch.press()
switch.press()
Enter fullscreen mode Exit fullscreen mode

Conclusion

By adhering to the SOLID principles, developers aim to create a system that is easy to understand, easy to change, and easy to maintain. These principles guide the design of software in a way that is robust, flexible, and adaptable to future changes. They help in creating a system that is not only functional but also scalable and maintainable, making it a better choice for long-term projects.

Image created by Bing

Next part: How to put SOLID principles into practice

Top comments (4)

Collapse
 
antonov_mike profile image
Antonov Mike

An example of a simple architecture based on SOLID principles. This is still not a working application because it has no business logic.
permanent link
repository (it may change later)

Collapse
 
riverajefer profile image
Jefferson Rivera

Nice.
Excellent examples.

Collapse
 
antonov_mike profile image
Antonov Mike

Thank you very much!

Collapse
 
antonov_mike profile image
Antonov Mike

Grab code examples here