Паттерны GoF, которые реально нужны бэкендеру
Из 23 паттернов GoF бэкенд-разработчику в повседневной работе нужны 6–8. Strategy, Observer, Factory, Repository, Decorator — паттерны, которые вы уже используете, даже если не знаете их названий.
Почему это важно: Паттерны — это не про заучивание UML-диаграмм, а про общий словарь и проверенные решения типичных проблем. Когда вы говорите «давай сделаем через Strategy», вся команда сразу понимает структуру.
Главная идея
Паттерн — шаблон решения повторяющейся проблемы. Strategy выносит алгоритм в отдельный объект. Observer уведомляет подписчиков об изменении. Factory скрывает создание сложных объектов. Repository абстрагирует доступ к данным.
Как это выглядит на практике
- Задача: уведомить пользователя о заказе по email, SMS и push одновременно
- Наивный подход: три вызова внутри OrderService — жёсткая связь
- Observer: OrderService публикует событие order.created
- EmailNotifier, SmsNotifier, PushNotifier — независимые подписчики
- Добавить Telegram-уведомление = создать TelegramNotifier и подписать на событие
- OrderService не изменился, тесты OrderService не сломались
Примеры кода
Strategy — вынесение алгоритма
# Проблема: разные способы расчёта доставки
# Без паттерна: if/elsif внутри Order
# С паттерном: каждый алгоритм — отдельный объект
class FlatRateShipping
def calculate(order)
500 # 500 рублей за любой заказ
end
end
class WeightBasedShipping
RATE_PER_KG = 150
def calculate(order)
total_weight = order.items.sum(&:weight_kg)
(total_weight * RATE_PER_KG).ceil
end
end
class FreeShippingOver3000
def initialize(fallback:)
@fallback = fallback
end
def calculate(order)
order.total >= 3000 ? 0 : @fallback.calculate(order)
end
end
# Использование
shipping = FreeShippingOver3000.new(
fallback: WeightBasedShipping.new
)
cost = shipping.calculate(order)
# Новый тариф? Новый класс. Ноль правок в существующих.
Observer — реальный пример на ActiveSupport
# Rails-приложение: после создания заказа нужно
# отправить email, обновить аналитику, зарезервировать товар
# app/models/order.rb
class Order < ApplicationRecord
after_create_commit :publish_created_event
private
def publish_created_event
ActiveSupport::Notifications.instrument(
'order.created', order: self
)
end
end
# config/initializers/order_subscribers.rb
ActiveSupport::Notifications.subscribe('order.created') do |*, payload|
order = payload[:order]
OrderMailer.confirmation(order).deliver_later
end
ActiveSupport::Notifications.subscribe('order.created') do |*, payload|
order = payload[:order]
InventoryService.reserve(order.items)
end
ActiveSupport::Notifications.subscribe('order.created') do |*, payload|
order = payload[:order]
AnalyticsTracker.track('purchase', amount: order.total)
end
# Добавить Telegram-уведомление:
# 1 файл, 0 изменений в Order
Service Object + Repository
# Service Object инкапсулирует бизнес-операцию
class TransferMoney
def initialize(account_repo: AccountRepository.new)
@accounts = account_repo
end
def call(from_id:, to_id:, amount:)
from = @accounts.find(from_id)
to = @accounts.find(to_id)
raise InsufficientFunds if from.balance < amount
@accounts.transaction do
from.withdraw(amount)
to.deposit(amount)
@accounts.save(from)
@accounts.save(to)
end
{ from: from.balance, to: to.balance }
end
end
# Repository абстрагирует доступ к данным
class AccountRepository
def find(id)
Account.find(id)
end
def save(account)
account.save!
end
def transaction(&block)
ActiveRecord::Base.transaction(&block)
end
end
# В тестах подменяем репозиторий на in-memory:
# TransferMoney.new(account_repo: FakeAccountRepo.new)
Что происходит под капотом
- Strategy заменяет условную логику (if/elsif/case) полиморфизмом — каждая ветка становится классом
- Observer (pub/sub) разрывает связь между источником события и его обработчиками
- Factory скрывает сложную логику создания объекта — клиент не знает, какой конкретный класс создаётся
- Repository отделяет бизнес-логику от способа хранения — можно менять БД без изменения сервисов
- Decorator оборачивает объект, добавляя поведение (логирование, кэш, валидация) без наследования
Типичные ошибки и заблуждения
- «Нужно знать все 23 паттерна GoF» — на практике 6–8 покрывают 90% задач бэкендера
- «Паттерн = больше кода» — да, но этот код легче менять, тестировать и понимать в команде
- «Паттерны устарели» — названия из 1994, но проблемы те же: условная логика, жёсткая связь, сложное создание объектов
Ключевые выводы
- Strategy: разные алгоритмы (доставка, оплата, скидки) — отдельные объекты с одинаковым интерфейсом
- Observer: событийная модель разрывает связь между «что произошло» и «что делать»
- Repository + Service Object: бизнес-логика отделена от фреймворка и хранения
Термины урока
Связь с работой backend-разработчика
Паттерны — общий язык команды и проверенные решения. Если вы видите растущий if/elsif — это Strategy. Если хардкодите вызовы после события — это Observer. Если бизнес-логика пронизана SQL-запросами — это Repository.
Мини-разбор реальной ситуации
Финтех-стартап: расчёт комиссии содержал case-оператор на 200 строк (15 типов транзакций). Каждая новая комиссия — 2 дня работы и ручное регрессионное тестирование всех 15 веток. После рефакторинга на Strategy (15 маленьких классов по 20–30 строк) добавление новой комиссии занимает 1–2 часа, тестируется изолированно.
Что запомнить
- Strategy заменяет if/elsif полиморфизмом — новое поведение = новый класс
- Observer (pub/sub) = источник не знает о подписчиках, подписчики не знают друг о друге
- Service Object + Repository = тестируемая бизнес-логика, независимая от фреймворка
Итог
Паттерны — не академическое упражнение, а практические решения проблем, с которыми вы сталкиваетесь каждый день. Strategy, Observer, Repository, Service Object — четыре паттерна, которые кардинально улучшают структуру backend-кода.
Комментарии к уроку
Войдите, чтобы оставить комментарий.
Пока нет комментариев — будьте первым.