Принцип единственной обязанности требует того, чтобы один класс выполнял только одну работу.
Ниже приведен класс, который имеет две обязанности:
class User:
def __init__(self, name: str):
self.name = name
def get_name(self) -> str:
pass
def save(self, user: User):
pass
Мы же просто разделим класс. Мы создадим ещё один класс, который возьмет на себя одну ответственность — управление базой данных пользователя:
class User:
def __init__(self, name: str):
self.name = name
def get_name(self):
pass
class UserDB:
def get_user(self, id) -> User:
pass
def save(self, user: User):
pass
Распространённым решением этой проблемы является применение шаблона проектирования Фасад.
Принцип открытости/закрытости - программные сущности (классы, модули, функции) должно быть открыты для расширения, но не модификации.
OCP запрещает это:
class Discount:
def __init__(self, customer, price):
self.customer = customer
self.price = price
def give_discount(self):
if self.customer == 'fav': return self.price * 0.2
if self.customer == 'vip': return self.price * 0.4
Чтобы следовать OCP, мы добавим новый класс, который будет расширять Discount
, и в этом новом классе реализуем требуемую логику:
class Discount:
def __init__(self, customer, price):
self.customer = customer
self.price = price
def get_discount(self):
return self.price * 0.2
class VIPDiscount(Discount):
def get_discount(self):
return super().get_discount() * 2
Если вы решите дать скидку супер VIP пользователям, то это будет выглядеть так:
class SuperVIPDiscount(VIPDiscount):
def get_discount(self):
return super().get_discount() * 2
Расширяйте, но не модифицируйте.
Принцип подстановки Лисков - для любого класса клиент должен иметь возможность использовать любой подкласс базового класса, не замечая разницы между ними, и следовательно, без каких-либо изменений поведения программы при выполнении. Это означает, что клиент полностью изолирован и не подозревает об изменениях в иерархии классов.
class User():
def __init__(self, color, board):
create_pieces()
self.color = color
self.board = board
def move(self, piece: Piece, position: int):
piece.move(position)
chessmate_check()
board = ChessBoard()
user_white = User("white", board)
user_black = User("black", board)
pieces = user_white.pieces
horse = helper.getHorse(user_white, 1)
user.move(horse)
LSP это основа хорошего объектно-ориентированного проектирования программного обеспечения, потому что он следует одному из базовых принципов ООП — полиморфизму.
**Принцип разделения интерфейсов ** - клиенты не должны зависеть от интерфейсов, которые они не используют. Этот принцип устраняет недостатки реализации больших интерфейсов.
Классический пример:
class IShape:
def draw(self):
raise NotImplementedError
class Circle(IShape):
def draw(self):
pass
class Square(IShape):
def draw(self):
pass
class Rectangle(IShape):
def draw(self):
pass
from abc import ABC, abstractmethod
class InputDevice(ABC):
@abstractmethod
def read_input(self):
pass
class OutputDevice(ABC):
@abstractmethod
def write_output(self, data):
pass
class Keyboard(InputDevice):
def read_input(self):
# Логика чтения ввода с клавиатуры
pass
class Mouse(InputDevice):
def read_input(self):
# Логика чтения ввода с мыши
pass
class Monitor(OutputDevice):
def write_output(self, data):
# Логика вывода данных на монитор
pass
class Printer(OutputDevice):
def write_output(self, data):
# Логика вывода данных на принтер
pass
**Принцип инверсии зависимостей ** - классы не должны напрямую полагаться на другие классы, а вместо этого должны зависеть от абстракций.
class AuthenticationForUser():
def __init__(self, connector: Connector):
self.connection = connector.connect()
def authenticate(self, credentials):
pass
def is_authenticated(self):
pass
def last_login(self):
pass
class AnonymousAuth(AuthenticationForUser):
pass
class GithubAuth(AuthenticationForUser):
def last_login(self):
pass
class FacebookAuth(AuthenticationForUser):
pass
class Permissions()
def __init__(self, auth: AuthenticationForUser)
self.auth = auth
def has_permissions():
pass
class IsLoggedInPermissions(Permissions):
def last_login():
return auth.last_log
Другой пример:
from abc import ABC, abstractmethod
class Notification(ABC):
@abstractmethod
def send_notification(self, message):
pass
class EmailSender(Notification):
def send_notification(self, message):
# Логика отправки уведомления по электронной почте
pass
class SMSNotification(Notification):
def send_notification(self, message):
# Логика отправки уведомления по SMS
pass
class User:
def __init__(self, username, email):
self.username = username
self.email = email
self.notification_service = EmailSender()
def send_notification(self, message):
self.notification_service.send_notification(message)
Чтобы применить принцип DIP, мы изменяем User
, чтобы он зависел от абстракции Notification
, а не от конкретной реализации:
class User:
def __init__(self, username, email, notification_service):
self.username = username
self.email = email
self.notification_service = notification_service
def send_notification(self, message):
self.notification_service.send_notification(message)
email_sender = EmailSender()
user = User("John", "john@example.com", email_sender)
user.send_notification("Hello!")
sms_notification = SMSNotification()
user = User("Jane", "jane@example.com", sms_notification)
user.send_notification("Hi there!")
Теперь User
зависит от абстракции Notification
и может быть легко настроен для работы с различными реализациями уведомлений.