Принцип подстановки Барбары Лисков (LSP)
Поговорим о третьем из пяти основных принципов объектно-ориентированного программирования:
S — принцип единой ответственности (Single responsibility Principle)
O — принцип открытости / закрытости (Open-closed Principle)
L — принцип подстановки Барбары Лисков (Liskov Substitution Principle)
I — принцип разделения интерфейса (Interface Segregation Principle)
D — принцип инверсии зависимостей (Dependency Inversion Principle)
Принцип подстановки Лисков гласит: дочерний класс должен быть написан так, чтобы он мог заменить родительский. Таким образом дочерний класс мог бы занять место своего родительского класса, не вызывая никаких ошибок.
from abc import ABC, abstractmethod class Notification(ABC): @abstractmethod def notify(self, message, email): pass class Email(Notification): def notify(self, message, email): print(f'Send {message} to {email}') class SMS(Notification): def notify(self, message, phone): print(f'Send {message} to {phone}') if __name__ == '__main__': notification = SMS() notification.notify('Hello', '[email protected]')
У нас есть три класса: Notification
, Email
и SMS
. Классы Email
и SMS
наследуются от класса Notification
.
В абстрактном классе Notification
есть метод notify()
, который отправляет сообщение на электронную почту.
Метод notify()
в классе Email
отправляет сообщение на электронную почту, что вполне нормально.
Однако в классе SMS
для отправки сообщения используется номер телефона, а не адрес электронной почты. Поэтому нам нужно изменить сигнатуру метода notify()
класса SMS
, чтобы он принимал номер телефона вместо электронной почты.
Следующий класс NotificationManager
использует объект Notification
для отправки сообщения контакту (Contact
):
class Contact: def __init__(self, name, email, phone): self.name = name self.email = email self.phone = phone class NotificationManager: def __init__(self, notification, contact): self.contact = contact self.notification = notification def send(self, message): if isinstance(self.notification, Email): self.notification.notify(message, contact.email) elif isinstance(self.notification, SMS): self.notification.notify(message, contact.phone) else: raise Exception('The notification is not supported') if __name__ == '__main__': contact = Contact('Some Name', '[email protected]', '8-999-999-9999') notification_manager = NotificationManager(SMS(), contact) notification_manager.send('Hello!')
Метод send()
класса NoticationManager
принимает некое уведомление и проверяет, является ли оно экземпляром класса Email
или класса SMS
. После метод передает электронную почту и телефон контакта в метод notify()
соответственно.
Делаем всё по канонам ООП
Для начала переопределим метод notify()
класса Notification
так, чтобы он не включал параметр email
:
class Notification(ABC): @abstractmethod def notify(self, message): pass
Затем добавляем параметр email
в метод __init__
класса Email
:
class Email(Notification): def __init__(self, email): self.email = email def notify(self, message): print(f'Send "{message}" to {self.email}')
Добавляем параметр phone
в метод __init__
класса SMS
:
class SMS(Notification): def __init__(self, phone): self.phone = phone def notify(self, message): print(f'Send "{message}" to {self.phone}')
Изменяем класс NotificationManager
:
class NotificationManager: def __init__(self, notification): self.notification = notification def send(self, message): self.notification.notify(message)
Просто как 0, 1, 2, 3. Собираем всё в кучу:
from abc import ABC, abstractmethod class Notification(ABC): @abstractmethod def notify(self, message): pass class Email(Notification): def __init__(self, email): self.email = email def notify(self, message): print(f'Send "{message}" to {self.email}') class SMS(Notification): def __init__(self, phone): self.phone = phone def notify(self, message): print(f'Send "{message}" to {self.phone}') class Contact: def __init__(self, name, email, phone): self.name = name self.email = email self.phone = phone class NotificationManager: def __init__(self, notification): self.notification = notification def send(self, message): self.notification.notify(message) if __name__ == '__main__': contact = Contact('John Doe', '[email protected]', '(408)-888-9999') sms_notification = SMS(contact.phone) email_notification = Email(contact.email) notification_manager = NotificationManager(sms_notification) notification_manager.send('Hello John') notification_manager.notification = email_notification notification_manager.send('Hi John')
Заключение
Принцип подстановки Барбары Лисков гласит: дочерний класс должен быть написан так, чтобы он мог заменить свой родительский класс, не вызывая при этом ошибок.
Источник: Python Tutorial
Перевод и адаптация: Екатерина Прохорова
👉🏻Подписывайтесь на PythonTalk в Telegram 👈🏻