Skip to content

SOLID

Принципи SOLID

Що таке SOLID

SOLID - це абревіатура складена з перших літер п'яти базових принципів ООП і дизайну, запропонована Робертом Мартіном.

Принципи SOLID використовуються для дизайну і розробки таких програмних систем, які зможуть тривалий час розширятись, розвиватись і підтримуватись.

S: Single Responsibility Principle (Принцип єдиної відповідальності). Кожен клас повинен мати лише одну відповідальність, вирішувати тільки одне завдання.

O: Open-Closed Principle (Принцип відкритості-закритості). Програмні сутності (класи, модулі, функції) повинні бути відкритими для розширення, але закритими для змін.

L: Liskov Substitution Principle (Принцип підстановки Барбари Лісков). Обєкти в програмі можуть бути заміненими їх нащадками без зміни коду програми. Клас-нащадок повинен доповнювати, а не змінювати базовий.

I: Interface Segregation Principle (Принцип розділення інтерфейсу). Багато спеціалізованих інтерфейсів краще за один універсальний. Клієнти не повинні залежати від інтерфейсів, які вони не використовують.

D: Dependency Inversion Principle (Принцип інверсії залежностей). Об'єктом залежності повинна бути абстракція, а не щось конкретне. - Модулі верхніх рівнів не повинні залежати від модулів нижніх рівнів. Обидва типи модулів повинні залежати від абстракцій. - Абстракції не повинні залежати від деталей. Деталі повинні залежати від абстракцій.

S - Single Responsibility Principle

S - Single Responsibility Principle - SRP - Принцип єдиної відповідальность Кожен клас повинен мати лише одну відповідальність, вирішувати тільки одне завдання. Це означає, що клас має бути створений для виконання лише однієї задачі, яку він повинен повністю інкапсулювати. Отже, всі сервіси цього класу мють бути підпорядковані її виконанню. Результатом слідування цій концепції є наявність лише однієї причини для зміни класу, що робить його значно здоровішим.

Переваги і недоліки - Протидіє дублюванню коду, адже якщо функціональність розташована в неправильному місці, то доведеться копіювати її в потрібне - Зменшує потребу зміни вже випробуваного коду - Забезпечує відповідність назв класів та їх функціональності, що полегшує життя тим, хто обслуговуватиме код в майбутньому - Мінус - зростання кількості класів, що призводить до зростання складності системи.

Реалізація - розділити більші класи на менші. Наприклад - є клас, який створює звіт і друкує звіт. Такий клас може змінитись через 2 причини - може змінитись сам звіт або формат друку. Тому цей клас треба розділити на 2 нових.

Антипатерном до цього принципу є Божественний клас - коли один виконує дуже багато всього. І тоді зявляється ефект сніжного кому - зміна в одному місці викликає зміну в багатьох.

Links

O - Open-Closed Principle

O - Open/Closed Principle - OCP - Принцип відкритості-закритості Програмні сутності (класи, модулі, функції) повинні бути відкритими для розширення, але закритими для змін. Розширення певного класу може здійснюватись через його успадкування.

Реалізацію можна міняти, але не можна міняти вхідні параметри і те, що повертає метод/обєкт.

L - Liskov Substitution Principle

L - Liskov Substitution Principle - LSP - Принцип підстановки Лісков Об'єкти в програмі можуть бути заміненими їх нащадками без зміни коду програми. Клас-нащадок повинен доповнювати, а не змінювати базовий. Це вимагає суворого дотримання контрактів та інтерфейсів, визначених у базовому класі, у всіх його похідних класах.

  • Функції, які використовують посилання на базові класи, мають мати можливість використовувати об'єкти похідних класів, не знаючи про це.
  • Принцип про правильне наслідування.
  • Проектування конктракту - контракт інформує авторів клієнтського коду про бажану поведінку класу. Контракт визначає вхідні і вихідні параметри.

I - Interface Segregation Principle

I - Interface Segregation Principle - ISP - Принцип розділення інтерфейсу. Багато спеціалізованих інтерфейсів краще за один універсальний. Інтерфейс може бути поділений на спеціалізовані ще на стадіях проектування, заради майбутньої гнучкості програмних компонентів.

  • Клієнти не повинні залежати від методів, які вони не використовують.
  • Занадто товсті інтерфейси необхідно розділяти на менші та специфічні, щоб їх клієнти знали лише про ті методи, що необхідні для них у роботі. Як результат, при зміні певног функціоналу, незмінними мають лишатися ті класи, як не використовують його. Тобто виконання цього принципу допомагає системі залишатись гнучкою при внесенні до неї змін.
# wrong
# In this design the `Car` class must implement the `fly()` method from the `Vehicle` class that the `Car` class doesn’t use. Therefore, this design violates the interface segregation principle.

class Vehicle(ABC): 
    @abstractmethod 
    def go(self): pass 
    @abstractmethod 
    def fly(self): pass

class Aircraft(Vehicle): 
    def go(self): print("Taxiing") 
    def fly(self): print("Flying")

class Car(Vehicle): 
    def go(self): print("Going") 
    def fly(self): raise Exception('The car cannot fly')

# To fix this, you need to split the `Vehicle` class into small ones and inherits from these classes from the `Aircraft` and `Car` classes:

class Movable(ABC): 
    @abstractmethod 
    def go(self): pass 

class Flyable(Movable): 
    @abstractmethod 
    def fly(self): pass

class Aircraft(Flyable): 
    def go(self): print("Taxiing") 
    def fly(self): print("Flying")

class Car(Movable): 
    def go(self): print("Going")

Прикладом розділення інтерфейсу можуть слугувати mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin в DRF.

Links

D - Dependency Inversion Principle

D - Dependency Inversion Principle - DIP - Принцип інверсії залежностей Модулі вищих рівнів не мають залежати від модулів нижчих рівнів. Обидва типи модулів повинні залежати від абстракцій. Це досягається через використання інтерфейсів та ін'єкції залежностей (dependency injection).