Вопрос проверяет знание принципов объектно-ориентированного программирования, важных для написания поддерживаемого и масштабируемого кода.
SOLID — это набор из пяти принципов ООП, которые помогают писать устойчивый и легко поддерживаемый код. Принципы включают: единую ответственность, открытость/закрытость, подстановку Лисков, разделение интерфейсов и инверсии зависимостей.
SOLID — это аббревиатура для пяти принципов, которые помогают разработчику писать более чистый, удобный для расширения и тестирования код. Каждый из принципов направлен на решение определенной проблемы при проектировании программных систем.
S — Принцип единой ответственности (SRP): Каждый класс должен иметь только одну причину для изменений. Это помогает уменьшить связность и улучшить поддержку.
# Плохой пример: класс выполняет несколько задач
class Report:
def generate_report(self):
# Логика генерации отчета
pass
def send_report(self):
# Логика отправки отчета
pass
# Хороший пример: разделение ответственности
class ReportGenerator:
def generate_report(self):
# Логика генерации отчета
pass
class ReportSender:
def send_report(self):
# Логика отправки отчета
passO — Принцип открытости/закрытости (OCP): Классы должны быть открыты для расширений, но закрыты для модификаций. Это позволяет добавлять новые функциональности без изменения существующего кода.
# Плохой пример: изменения в существующем коде
class PaymentProcessor:
def process_payment(self, payment_type):
if payment_type == "credit_card":
print("Processing credit card payment")
elif payment_type == "paypal":
print("Processing PayPal payment")
# Хороший пример: расширение через наследование
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def process_payment(self):
pass
class CreditCardPayment(PaymentMethod):
def process_payment(self):
print("Processing credit card payment")
class PayPalPayment(PaymentMethod):
def process_payment(self):
print("Processing PayPal payment")L — Принцип подстановки Лисков (LSP): Объекты производных классов должны быть заменяемы объектами базовых классов без нарушения правильности работы программы.
# Плохой пример: подкласс нарушает контракт базового класса
class Bird:
def fly(self):
print("I can fly")
class Ostrich(Bird):
def fly(self):
raise NotImplementedError("Ostriches can't fly")
# Хороший пример: переработка дизайна
from abc import ABC, abstractmethod
class Bird(ABC):
@abstractmethod
def move(self):
pass
class FlyingBird(Bird):
def move(self):
print("I can fly")
class RunningBird(Bird):
def move(self):
print("I can run")I — Принцип разделения интерфейсов (ISP): Лучше создавать несколько специфичных интерфейсов, чем один универсальный. Это помогает избегать ненужных зависимостей.
# Плохой пример: один интерфейс с лишними методами
class Machine:
def print(self): pass
def scan(self): pass
def fax(self): pass
class OldPrinter(Machine):
def print(self):
print("Printing")
def scan(self):
raise NotImplementedError("This machine can't scan")
def fax(self):
raise NotImplementedError("This machine can't fax")
# Хороший пример: разделённые интерфейсы
class Printer:
def print(self): pass
class Scanner:
def scan(self): pass
class Fax:
def fax(self): pass
class OldPrinter(Printer):
def print(self):
print("Printing")D — Принцип инверсии зависимостей (DIP): Модули высокого уровня не должны зависеть от модулей низкого уровня, оба должны зависеть от абстракций. Это способствует улучшению гибкости и тестируемости кода.
# Плохой пример: зависимость от конкретного класса
class MySQLDatabase:
def connect(self):
print("Connecting to MySQL database")
class Application:
def __init__(self):
self.db = MySQLDatabase()
def start(self):
self.db.connect()
# Хороший пример: зависимость от абстракции
class Database(ABC):
@abstractmethod
def connect(self):
pass
class MySQLDatabase(Database):
def connect(self):
print("Connecting to MySQL database")
class MongoDB(Database):
def connect(self):
print("Connecting to MongoDB database")
class Application:
def __init__(self, database: Database):
self.db = database
def start(self):
self.db.connect()