Статьи
June 8, 2022

Принцип открытости/закрытости в Python

Из этого материала вы узнаете о принципе открытости/закрытости и о том, как расширять систему без прямого изменения существующего кода.

Введение в принцип открытости/закрытости

Это второй из пяти основных принципов объектно-ориентированного программирования:

S — принцип единой ответственности (Single responsibility Principle)

O — принцип открытости / закрытости (Open-closed Principle)

L — принцип подстановки Барбары Лисков (Liskov Substitution Principle)

I — принцип разделения интерфейса (Interface Segregation Principle)

D — принцип инверсии зависимостей (Dependency Inversion Principle)

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

Звучит противоречиво. Цель, вообще-то, благая – упростить добавление новых функций (или вариантов использования) в систему без непосредственного изменения существующего кода.

Рассмотрим пример:

class Person:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return f'Person(name={self.name})'


class PersonStorage:
    def save_to_database(self, person):
        print(f'Save the {person} to database')

    def save_to_json(self, person):
        print(f'Save the {person} to a JSON file')


if __name__ == '__main__':
    person = Person('John Doe')
    storage = PersonStorage()
    storage.save_to_database(person)

В нём у класса PersonStorage два метода:

  • save_to_database() сохраняет информацию о сущности «человек» в базе данных;
  • save_to_json() сохраняет данные о сущности в файл JSON.

Класс PersonStorage будет соответствовать второму принципу ООП, если его не нужно будет изменять всякий раз, когда возникнет необходимость сохранить объект Person в другой формат файла, например, в XML.

См. следующую схему классов:

А как сделать так, чтобы класс соответствовал нашему принципу?

Во-первых, определите абстрактный класс PersonStorage, который содержит абстрактный метод save():

from abc import ABC, abstractmethod

class PersonStorage(ABC):
    @abstractmethod
    def save(self, person):
        pass

Чтобы сохранить данные о сущности «человек» в XML-файл, вы можете определить новый класс PersonXML, который наследуется от класса PersonStorage следующим образом:

class PersonDB(PersonStorage):
    def save(self, person):
        print(f'Save the {person} to database')


class PersonJSON(PersonStorage):
    def save(self, person):
        print(f'Save the {person} to a JSON file')

Теперь вы можете сохранить данные о сущности «человек» в XML-файл с помощью класса PersonXML:

if __name__ == '__main__':
    person = Person('John Doe')
    storage = PersonXML()
    storage.save(person)

Собираем всё в кучу:

from abc import ABC, abstractmethod


class Person:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return f'Person(name={self.name})'


class PersonStorage(ABC):
    @abstractmethod
    def save(self, person):
        pass


class PersonDB(PersonStorage):
    def save(self, person):
        print(f'Save the {person} to database')


class PersonJSON(PersonStorage):
    def save(self, person):
        print(f'Save the {person} to a JSON file')


class PersonXML(PersonStorage):
    def save(self, person):
        print(f'Save the {person} to a XML file')


if __name__ == '__main__':
    person = Person('John Doe')
    storage = PersonXML()
    storage.save(person)

Заключение

Принцип открытости / закрытости позволяет спроектировать систему таким образом, чтобы её можно было расширять, но не изменять.

👉🏻Подписывайтесь на PythonTalk в Telegram 👈🏻

👨🏻‍💻Чат PythonTalk в Telegram💬

🍩 Поддержать канал 🫶

Источник: Python Tutorial
Перевод и адаптация: Екатерина Прохорова