mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-03-24 20:12:04 +00:00
138 lines
8.8 KiB
Markdown
138 lines
8.8 KiB
Markdown
|
|
## ADDED Requirements
|
|||
|
|
|
|||
|
|
### Requirement: Сущности ядра DI
|
|||
|
|
Ядро DI MUST определять сущности `Scope`, `Module`, `Binding`, `BindingResolver`, `Disposable`, `CherryPickObserver` и их роли.
|
|||
|
|
|
|||
|
|
#### Scenario: Декларация базовых сущностей
|
|||
|
|
- **WHEN** разработчик использует публичный API ядра
|
|||
|
|
- **THEN** сущности `Scope`, `Module`, `Binding`, `Disposable`, `CherryPickObserver` доступны и описывают контракт DI
|
|||
|
|
|
|||
|
|
### Requirement: Жизненный цикл Scope
|
|||
|
|
`Scope` MUST поддерживать жизненный цикл открытия, использования и корректного закрытия с освобождением ресурсов.
|
|||
|
|
|
|||
|
|
#### Scenario: Открытие root scope
|
|||
|
|
- **WHEN** вызывается открытие root scope
|
|||
|
|
- **THEN** создается корневой контейнер для регистраций и резолва
|
|||
|
|
|
|||
|
|
#### Scenario: Открытие named subscope
|
|||
|
|
- **WHEN** открывается именованный subscope
|
|||
|
|
- **THEN** subscope наследует bindings родителя и может переопределять зависимости
|
|||
|
|
|
|||
|
|
#### Scenario: Закрытие subscope
|
|||
|
|
- **WHEN** вызывается закрытие subscope
|
|||
|
|
- **THEN** subscope удаляется из дерева, а связанные ресурсы освобождаются
|
|||
|
|
|
|||
|
|
#### Scenario: Путь scope и разделитель
|
|||
|
|
- **WHEN** scope открывается по иерархическому пути с разделителем
|
|||
|
|
- **THEN** создается цепочка subscopes по каждому сегменту пути
|
|||
|
|
|
|||
|
|
#### Scenario: Пустой scopeName
|
|||
|
|
- **WHEN** scope открывается с пустым именем
|
|||
|
|
- **THEN** возвращается root scope
|
|||
|
|
|
|||
|
|
### Requirement: Установка и удаление модулей
|
|||
|
|
`Scope` MUST поддерживать установку и удаление `Module`, а их bindings MUST становиться доступными сразу после установки.
|
|||
|
|
|
|||
|
|
#### Scenario: Установка модуля
|
|||
|
|
- **WHEN** модуль установлен в scope
|
|||
|
|
- **THEN** его bindings доступны для resolve/tryResolve
|
|||
|
|
|
|||
|
|
#### Scenario: Сброс модулей
|
|||
|
|
- **WHEN** модули сброшены
|
|||
|
|
- **THEN** bindings модулей не доступны для резолва в этом scope
|
|||
|
|
|
|||
|
|
### Requirement: Типы bindings
|
|||
|
|
`Binding` MUST поддерживать прямые инстансы, синхронные провайдеры, асинхронные провайдеры и провайдеры с параметрами, а также именованные bindings.
|
|||
|
|
|
|||
|
|
#### Scenario: Именованные bindings
|
|||
|
|
- **WHEN** зарегистрированы два bindings одного типа с разными именами
|
|||
|
|
- **THEN** resolve с указанным именем возвращает соответствующую реализацию
|
|||
|
|
|
|||
|
|
#### Scenario: Параметризованный provider
|
|||
|
|
- **WHEN** binding зарегистрирован с провайдером, принимающим параметры
|
|||
|
|
- **THEN** resolve с параметрами использует переданные аргументы для создания экземпляра
|
|||
|
|
|
|||
|
|
### Requirement: Семантика резолва и fallback
|
|||
|
|
Резолв MUST искать зависимость в текущем scope и, при отсутствии, подниматься к родительским scope.
|
|||
|
|
|
|||
|
|
#### Scenario: Fallback к родителю
|
|||
|
|
- **WHEN** зависимость отсутствует в текущем scope, но есть в родительском
|
|||
|
|
- **THEN** резолв выполняется через родительский binding
|
|||
|
|
|
|||
|
|
### Requirement: Синхронный и асинхронный резолв
|
|||
|
|
`Scope` MUST предоставлять синхронный и асинхронный резолв и их nullable‑варианты.
|
|||
|
|
|
|||
|
|
#### Scenario: Resolve (sync)
|
|||
|
|
- **WHEN** вызывается `resolve<T>()` для существующей зависимости
|
|||
|
|
- **THEN** возвращается экземпляр или выбрасывается ошибка при отсутствии
|
|||
|
|
|
|||
|
|
#### Scenario: TryResolve (sync)
|
|||
|
|
- **WHEN** вызывается `tryResolve<T>()` для отсутствующей зависимости
|
|||
|
|
- **THEN** возвращается `null` без исключения
|
|||
|
|
|
|||
|
|
#### Scenario: Resolve (async)
|
|||
|
|
- **WHEN** вызывается `resolveAsync<T>()` для зависимости с async‑провайдером
|
|||
|
|
- **THEN** возвращается `Future<T>` с экземпляром
|
|||
|
|
|
|||
|
|
#### Scenario: TryResolve (async)
|
|||
|
|
- **WHEN** вызывается `tryResolveAsync<T>()` для отсутствующей зависимости
|
|||
|
|
- **THEN** возвращается `null` без исключения
|
|||
|
|
|
|||
|
|
### Requirement: Ошибки несоответствия sync/async
|
|||
|
|
Резолв MUST выбрасывать ошибки при несоответствии sync/async режима.
|
|||
|
|
|
|||
|
|
#### Scenario: ResolveSync для async‑инстанса
|
|||
|
|
- **WHEN** binding зарегистрирован как async‑инстанс или async‑provider, а вызывается `resolveSync`
|
|||
|
|
- **THEN** выбрасывается ошибка с указанием использовать async‑резолв
|
|||
|
|
|
|||
|
|
### Requirement: Управление Disposable
|
|||
|
|
`Scope` MUST отслеживать экземпляры, реализующие `Disposable`, и вызывать `dispose()` при закрытии scope.
|
|||
|
|
|
|||
|
|
#### Scenario: Автоматическое освобождение
|
|||
|
|
- **WHEN** scope закрывается
|
|||
|
|
- **THEN** все `Disposable` в scope и дочерних scope освобождаются
|
|||
|
|
|
|||
|
|
### Requirement: Детекция циклов зависимостей
|
|||
|
|
Ядро DI MUST поддерживать локальную и глобальную детекцию циклов.
|
|||
|
|
|
|||
|
|
#### Scenario: Локальный цикл
|
|||
|
|
- **WHEN** локальная детекция включена и резолв обнаруживает цикл
|
|||
|
|
- **THEN** резолв завершается ошибкой цикла
|
|||
|
|
|
|||
|
|
#### Scenario: Глобальный цикл
|
|||
|
|
- **WHEN** глобальная детекция включена и цикл пересекает scope
|
|||
|
|
- **THEN** резолв завершается ошибкой цикла
|
|||
|
|
|
|||
|
|
### Requirement: Наблюдатель событий (Observer)
|
|||
|
|
Ядро DI MUST предоставлять наблюдателя, получающего события регистрации, резолва, ошибок, lifecycle и диагностики.
|
|||
|
|
|
|||
|
|
#### Scenario: События lifecycle
|
|||
|
|
- **WHEN** scope открывается, модули устанавливаются и зависимости резолвятся
|
|||
|
|
- **THEN** observer получает соответствующие уведомления
|
|||
|
|
|
|||
|
|
### Requirement: Ошибки и сообщения об ошибках
|
|||
|
|
При критических сбоях резолва ядро MUST выбрасывать ошибку с понятным сообщением, а для tryResolve MUST не бросать исключения.
|
|||
|
|
|
|||
|
|
#### Scenario: Ошибка отсутствующей зависимости
|
|||
|
|
- **WHEN** вызывается `resolve<T>()` для незарегистрированной зависимости
|
|||
|
|
- **THEN** выбрасывается ошибка с сообщением о невозможности резолва
|
|||
|
|
|
|||
|
|
#### Scenario: Ошибка цикла
|
|||
|
|
- **WHEN** обнаружен цикл зависимостей
|
|||
|
|
- **THEN** выбрасывается ошибка с указанием цепочки
|
|||
|
|
|
|||
|
|
#### Scenario: Отсутствующие параметры для параметризованного binding
|
|||
|
|
- **WHEN** binding зарегистрирован с `toProvideWithParams`, но резолв вызывается без `params`
|
|||
|
|
- **THEN** выбрасывается ошибка о необходимости передать параметры
|
|||
|
|
|
|||
|
|
### Requirement: Точки расширения
|
|||
|
|
Ядро DI MUST предоставлять точки расширения через пользовательский `Module` и `CherryPickObserver`.
|
|||
|
|
|
|||
|
|
#### Scenario: Пользовательский Module
|
|||
|
|
- **WHEN** разработчик создает свой `Module`
|
|||
|
|
- **THEN** он может зарегистрировать bindings и контролировать регистрацию
|
|||
|
|
|
|||
|
|
#### Scenario: Пользовательский Observer
|
|||
|
|
- **WHEN** разработчик передает кастомный observer
|
|||
|
|
- **THEN** он получает все DI‑события и может интегрировать внешние логеры/метрики
|