Интерфейсы и когда паттерны вредят
Главный принцип, на котором держатся паттерны, — «программируй на уровне интерфейсов». И почему слепое применение паттернов вредит.
Программируй на уровне интерфейсов, а не реализаций — опирайся в коде на то, что объект умеет (его контракт), а не на то, каким классом он является.
Почему интерфейсы
Когда функция принимает «что угодно, у чего есть метод area()», вы можете подставить любую реализацию, даже ту, которой ещё не существует. Это и даёт гибкость, ради которой нужны паттерны. В Python контракт часто задают через abc.ABC или просто duck typing.
from abc import ABC, abstractmethod
class Notifier(ABC):
@abstractmethod
def send(self, msg):
...
class EmailNotifier(Notifier):
def send(self, msg):
print(f"[email] {msg}")
class SmsNotifier(Notifier):
def send(self, msg):
print(f"[sms] {msg}")
# Клиент работает с интерфейсом Notifier, а не с конкретным классом
def alert(notifier: Notifier, msg):
notifier.send(msg)
for n in (EmailNotifier(), SmsNotifier()):
alert(n, "Сервер перезагружен")
Вывод:
[email] Сервер перезагружен [sms] Сервер перезагружен
Функция alert не знает и не хочет знать, как именно отправляется сообщение. Добавится PushNotifier — код alert не изменится. Большинство паттернов — это разные способы аккуратно применить этот принцип.
Обратная сторона: over-engineering
Паттерны — лекарство, а лекарство в неправильной дозе вредит. Самая частая ошибка новичка — впихнуть паттерн туда, где он не нужен, «чтобы было по-взрослому».
| Симптом over-engineering | В чём вред |
| Фабрика ради одного класса | Лишний слой косвенности без выгоды |
| Интерфейс с единственной реализацией «на будущее» | Усложняет навигацию, будущее может не наступить |
| Strategy там, где хватило бы функции | Три класса вместо двух строк |
Сравните: иногда «паттерн» — это просто передать функцию.
# Не нужен класс-стратегия: достаточно передать функцию
def apply_discount(price, rule):
return rule(price)
print(apply_discount(100, lambda p: p * 0.9)) # 10% скидка
print(apply_discount(100, lambda p: p - 5)) # минус 5
Вывод:
90.0 95
Правило большого пальца
Помните три аббревиатуры: YAGNI (You Aren't Gonna Need It — не делай впрок), KISS (Keep It Simple) и «правило трёх» — обобщай не раньше, чем увидел третий похожий случай. Паттерн вводят, когда боль изменчивости уже реальна, а не воображаема.
Итог
- Опирайтесь на контракт (интерфейс), а не на конкретный класс.
- Паттерн без реальной изменчивости — это over-engineering.
- YAGNI, KISS и «правило трёх» защищают от лишней сложности.