Паттерны проектирования
Паттерны проектирования - именованные подходы к повторяющимся задачам проектирования. Они дают общий словарь, но не являются готовым кодом или причиной добавлять абстракции до появления проблемы.
23 паттерна Gang of Four (GoF) обычно делят на группы:
- Порождающие:
Abstract Factory,Builder,Factory Method,Prototype,Singleton. - Структурные:
Adapter,Bridge,Composite,Decorator,Facade,Flyweight,Proxy. - Поведенческие:
Chain of Responsibility,Command,Interpreter,Iterator,Mediator,Memento,Observer,State,Strategy,Template Method,Visitor.
Ниже рассмотрены паттерны, особенно часто встречающиеся в Android-коде и API.
Factory Method и Abstract Factory
Factory Method определяет операцию создания, которую наследники или реализации переопределяют для выбора конкретного продукта. Отдельную функцию вроде createParser(format), выбирающую класс, обычно называют простой фабрикой, а не Factory Method из GoF.
Abstract Factory создаёт семейство связанных продуктов через единый контракт. Она может предоставлять согласованный набор платформенных сервисов или UI-компонентов, не раскрывая конкретные классы.
Используйте конструктор напрямую, если создание простое. Фабрика полезна, когда нужны выбор реализации, проверка, кеширование или несколько согласованных зависимостей.
Singleton и ограниченные области жизни
Singleton объединяет единственный экземпляр с глобальным доступом. Глобальное изменяемое состояние, скрытые зависимости и влияние тестов друг на друга делают ручные singleton-объекты рискованными в Android.
Предпочитайте явные зависимости через конструктор, которыми управляет контейнер приложения или DI-фреймворк. Экземпляр в области Hilt уникален внутри своего компонента, но не существует вечно: смерть процесса уничтожает граф, а более узкие компоненты живут меньше. Потокобезопасное создание экземпляра также не делает его изменяемое состояние потокобезопасным.
Observer и реактивные потоки
Observer уведомляет зарегистрированных подписчиков об изменениях наблюдаемого объекта. Android listeners и callbacks часто следуют этой модели. StateFlow, SharedFlow и LiveData предлагают связанные наблюдаемые API, но отличаются повторной доставкой, буферизацией, жизненным циклом и обработкой ошибок.
Холодный Flow обычно отдельно запускает upstream-работу для каждого коллектора, поэтому это не просто список наблюдателей одного работающего источника. Собирайте UI-потоки через lifecycle-aware API и отменяйте callback-подписки при остановке их владельца.
Adapter
Adapter преобразует существующий интерфейс в другой, ожидаемый клиентом. Например, он может обернуть старый callback API в suspend-контракт или представить стороннее хранилище через интерфейс приложения.
Преобразование DTO в доменную модель обычно является mapper: оно меняет данные, а не интерфейс объекта. RecyclerView.Adapter согласует данные и создание View с протоколом RecyclerView, хотя его роль во фреймворке шире минимального паттерна GoF.
Strategy и State
Strategy скрывает взаимозаменяемые алгоритмы за одним контрактом. Валидаторы, правила расчёта цены или повторных запросов можно выбирать конфигурацией и использовать без большого условия.
State тоже делегирует поведение, но выбранный объект представляет текущее состояние владельца и меняется при переходах. Используйте sealed-иерархию и when, если набор состояний закрыт и прост; полиморфные объекты состояний полезны, когда каждое состояние содержит существенное поведение и переходы.
Decorator
Decorator оборачивает объект с тем же контрактом и добавляет поведение до или после делегирования. Логирование, метрики или авторизацию можно наслаивать без наследования:
class MeasuredRepository(
private val delegate: UserRepository,
private val metrics: Metrics,
) : UserRepository by delegate {
override suspend fun user(id: UserId): User =
metrics.measure("load_user") { delegate.user(id) }
}
Kotlin by устраняет шаблонную переадресацию. Если обёртка управляет доступом, намерение точнее описывает Proxy; если она преобразует интерфейс - Adapter.
Выбор паттерна
Сначала назовите конкретную проблему: создание объекта, несовместимый интерфейс, изменяемый алгоритм, зависящее от состояния поведение или дополнительная ответственность. Предпочитайте возможности языка и простую композицию, если они решают задачу ясно. Паттерн полезен, когда делает границы изменений понятнее, а не просто добавляет знакомые названия классов.