Инженерия ПО

Design Patterns: GoF

1994 год. Гамма, Хельм, Джонсон, Влиссидес - 'банда четырёх' (Gang of Four). Книга Design Patterns. 23 паттерна. Стала одной из самых продаваемых книг по программированию. Через 30 лет половина из них вошла в стандартные библиотеки языков. PyTorch nn.Module - это Composite паттерн. Каждый слой нейросети - leaf node. Sequential - composite node. model.parameters() обходит всё дерево через единый интерфейс. Паттерн из 1994 года в коде 2024 года.

  • **PyTorch:** nn.Module (Composite) + autograd hooks (Observer) + optim (Strategy) - три GoF паттерна в одном фреймворке
  • **Hugging Face transformers:** AutoModel (Abstract Factory) + Pipeline (Facade) + Trainer callbacks (Observer)
  • **FastAPI:** Middleware (Decorator chain) + Dependency Injection (Strategy/DIP variant) + Router (Composite)
  • **LangChain:** Chain (Composite) + Tool (Command) + Memory (Strategy) + BaseLLM (Adapter)

Creational Patterns

1994 год. Гамма, Хельм, Джонсон, Влиссидес - 'банда четырёх' (Gang of Four). Книга Design Patterns. 23 паттерна. Стала одной из самых продаваемых книг по программированию. Через 30 лет половина из них вошла в стандартные библиотеки языков. Creational паттерны решают одну проблему: как создавать объекты, не привязываясь к конкретным классам. **Factory Method** - метод создаёт объект, но дочерний класс решает какой именно. **Abstract Factory** - семейство связанных объектов без указания конкретных классов. **Builder** - пошаговое создание сложного объекта, отделяя конструкцию от представления. **Singleton** - единственный экземпляр класса, глобальная точка доступа - и самый критикуемый паттерн GoF.

**Singleton - самый опасный GoF паттерн.** Это глобальное состояние с ООП-маской. Unit-тесты сломаны: нельзя изолировать компонент от глобального синглтона. Параллельность сломана: race conditions без явного mutexа. GoF книга сама называет Singleton 'не лучшим дизайном'. Современная альтернатива: Dependency Injection - передавать зависимость явно через конструктор вместо `getInstance()`.

Hugging Face `AutoModel.from_pretrained('bert-base-uncased')` возвращает `BertModel`, а `AutoModel.from_pretrained('gpt2')` возвращает `GPT2Model`. Какой GoF паттерн это реализует?

Structural Patterns

Structural паттерны решают задачу компоновки классов и объектов в более крупные структуры. **Adapter** - несовместимые интерфейсы начинают работать вместе через класс-переходник. **Decorator** - динамическое добавление поведения без изменения исходного класса. **Composite** - дерево объектов с единым интерфейсом для листьев и узлов. **Facade** - упрощённый интерфейс к сложной подсистеме. PyTorch `nn.Module` - это Composite: каждый слой сети - leaf node, `nn.Sequential` - composite node, `model.parameters()` обходит всё дерево через единый интерфейс. Паттерн из 1994 года в коде 2024 года.

**Composite в нейросетях:** идея Composite - единый интерфейс для атомарных и составных элементов - идеально ложится на иерархию слоёв. Один слой `Linear` и модель из 1000 слоёв имеют одинаковый интерфейс `forward()` и `parameters()`. Это позволяет строить произвольно глубокие сети через композицию без специальных случаев в коде обучения.

В PyTorch `nn.Sequential(nn.Linear(128, 64), nn.ReLU(), nn.Dropout(0.3))` можно вложить внутрь другого `nn.Sequential`. `model.parameters()` рекурсивно вернёт параметры всех уровней. Какой GoF паттерн это?

Behavioral Patterns

Behavioral паттерны описывают взаимодействие объектов и распределение ответственностей. **Observer** - объекты подписываются на события и получают уведомления при изменении состояния. **Strategy** - семейство взаимозаменяемых алгоритмов за общим интерфейсом. **Command** - инкапсуляция запроса как объекта, позволяющая откладывать и отменять операции. **Iterator** - последовательный доступ к элементам без раскрытия внутренней структуры. Парадокс: Strategy pattern в Python - это просто передача функции как аргумент. В Java 8 появились лямбды. Половина GoF паттернов стала ненужной потому что Python/JS/Kotlin поддержали first-class functions с самого начала.

**Python генераторы делают Iterator паттерн тривиальным.** В Java Iterator - это отдельный класс с `hasNext()` и `next()`. В Python - `yield` в функции, встроенная поддержка `for x in obj:`. Это не означает что паттерн исчез - он встроен в язык. DataLoader, файловые итераторы, pandas iterrows - всё это Iterator под другим именем.

Hugging Face Trainer принимает список `callbacks`: EarlyStoppingCallback, WandBCallback, LRSchedulerCallback. Каждый получает уведомления о событиях обучения. Какой GoF паттерн?

Когда применять и когда нет

Кристофер Александр, автор оригинальной идеи pattern language: 'Паттерн - это решение проблемы в контексте.' Не универсальное правило - решение конкретной проблемы в конкретном контексте. Главный антипаттерн - применение GoF паттернов потому что 'так правильно', не потому что есть реальная проблема. Результат: FactoryManagerFactoryBuilder для создания одного объекта. YAGNI (You Aren't Gonna Need It): не добавлять абстракцию пока нет второй реализации. Первый раз - пишем прямо. Второй раз - рефакторинг. В ML-системах паттерны появляются естественно: Strategy для выбора модели, Observer для training callbacks, Command для job queue. Не нужно их форсировать.

**Паттерны как словарь, не как цель.** GoF паттерны - это общий язык команды: сказать 'используем Observer для событий обучения' быстрее чем объяснять архитектуру на словах. Цель не 'добавить больше паттернов' - цель решить конкретную задачу с наименьшей сложностью. Если задача решается прямым кодом - прямой код лучше.

Чем больше GoF паттернов в коде - тем лучше архитектура

Паттерны - это словарь для описания решений, не цель сама по себе. Хорошая архитектура решает задачу с минимальной необходимой сложностью.

Over-engineering с паттернами - реальная проблема. FactoryManagerFactoryBuilder встречается в production коде. Паттерн решает конкретную проблему в конкретном контексте. Нет проблемы - нет паттерна. Functional style в Python/JS/Kotlin часто заменяет целые классы GoF паттернов одной функцией первого класса.

ML-инженер добавляет второй LLM-провайдер (Anthropic) к существующему коду, который работал только с OpenAI. Когда правильный момент ввести Strategy паттерн?

Ключевые идеи

  • **Creational** (Factory, Builder, Singleton): управление созданием объектов. Singleton - самый известный и самый опасный: глобальное состояние с ООП-маской
  • **Structural** (Adapter, Decorator, Composite, Facade): компоновка объектов. nn.Module в PyTorch - Composite паттерн в действии
  • **Behavioral** (Observer, Strategy, Command, Iterator): распределение ответственностей. В Python многие из них - просто callable или generator
  • **Когда применять:** при появлении второй реализации (Strategy), при необходимости уведомлений (Observer), при дереве объектов (Composite). Не раньше - YAGNI

Связанные темы

GoF паттерны проходят через весь стек разработки:

  • SOLID принципы — GoF паттерны - конкретные реализации SOLID. Strategy реализует OCP, Dependency Injection реализует DIP.
  • Clean Architecture — GoF паттерны как строительные блоки чистой архитектуры: Repository (Adapter + Strategy), Use Cases (Command)
  • AI Engineering: API Integration — LLM провайдеры через Adapter, параметры запросов через Builder, выбор модели через Strategy
  • Теория языков программирования — First-class functions и lambda в современных языках заменяют половину GoF паттернов
  • Строки и коллекции — Python генераторы - встроенный Iterator паттерн. for x in obj: это GoF Iterator под синтаксическим сахаром

Вопросы для размышления

  • PyTorch Trainer принимает список callbacks (EarlyStopping, ModelCheckpoint, LRScheduler). Какой GoF паттерн это реализует? Чем он лучше чем if/else внутри training loop?
  • В Python Strategy паттерн часто заменяется передачей функции как аргумента. Когда оправдано создавать полноценный класс-стратегию вместо callable?
  • Singleton считается антипаттерном в тестируемом коде. Какие реальные use cases оправдывают его применение и как минимизировать риски?

Связанные уроки

  • se-04
  • se-01
  • se-03
  • se-02
  • plt-05-gradual
  • aie-05-api-integration
  • prog-05-strings
  • ds-09-trees-intro
Design Patterns: GoF

0

1

Войти