Принцип подстановки Барбары Лисков (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@test.com')У нас есть три класса: 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@test.com', '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', 'john@test.com', '(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 👈🏻