diff --git a/README.md b/README.md index 43cd781..31d3cbd 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,992 @@ Фреймворк компонентных приложений для 1Script под названием "ОСень" поможет вам невероятным магическим образом компоновать ваше приложение из компонентов, которым не нужно заниматься собственным созданием или настройкой. И все это будет щедро обмазано пластилином и приятно пахнуть дубовыми вениками. -# Документация +## Оглавление -Документация и материалы по работе с фрэймворком можно найти [здесь](https://autumndocs.neocities.org) +- [Autumn/ОСень](#autumnосень) + - [Оглавление](#оглавление) + - [Зачем мне это?](#зачем-мне-это) + - [Как с этим работать?](#как-с-этим-работать) + - [Инициализация приложения](#инициализация-приложения) + - [Запуск приложения](#запуск-приложения) + - [Объявление компонента](#объявление-компонента) + - [Получение экземпляра компонента](#получение-экземпляра-компонента) + - [Связывание компонентов между собой](#связывание-компонентов-между-собой) + - [Фабрика компонентов](#фабрика-компонентов) + - [Внедрение зависимостей в поля и функции-сеттеры](#внедрение-зависимостей-в-поля-и-функции-сеттеры) + - [Внедрение настроек приложения](#внедрение-настроек-приложения) + - [Произвольные аргументы конструктора](#произвольные-аргументы-конструктора) + - [Генератор экземпляров компонентов](#генератор-экземпляров-компонентов) + - [Алиасы компонентов](#алиасы-компонентов) + - [Группировка компонентов по алиасу](#группировка-компонентов-по-алиасу) + - [Упорядочивание компонентов при получении в виде сортированных коллекций](#упорядочивание-компонентов-при-получении-в-виде-сортированных-коллекций) + - [Приоритизация компонентов с общим алиасом](#приоритизация-компонентов-с-общим-алиасом) + - [Переопределение компонента по имени](#переопределение-компонента-по-имени) + - [Пост-инициализация компонента](#пост-инициализация-компонента) + - [Уникальность экземпляров компонентов](#уникальность-экземпляров-компонентов) + - [Дополнительная обработка компонента](#дополнительная-обработка-компонента) + - [Использование контекста приложения](#использование-контекста-приложения) + - [Условная регистрация компонентов в контексте приложения](#условная-регистрация-компонентов-в-контексте-приложения) + - [Инверсия управления запуском приложения](#инверсия-управления-запуском-приложения) + - [Собственные аннотации](#собственные-аннотации) + - [Наследование аннотаций](#наследование-аннотаций) + - [Обработка аннотаций](#обработка-аннотаций) + - [В предыдущих сериях](#в-предыдущих-сериях) + - [Заключение](#заключение) + +## Зачем мне это? + +Вот есть у вас объект, у которого объявлен конструктор с параметрами. И есть много мест, где он создается. Параметры этого конструктора называются *зависимостями*. То есть, объект не может жить без передачи ему этих параметров, он от них *зависит.* +А где взять значения этих параметров, чтобы передать в объект? Очевидно что их тоже надо создать (через `Новый` или откуда-то получить). А у них тоже есть зависимости, и у зависимостей зависимостей есть зависимости. + +*Зависимость*. Да, вот слово, которое приходит в голову, когда изучаешь API "ОСени". Зависимость у авторов явно есть. Но это неправда. Мы зависимы только от просмотра ~~зоопорно~~ красивого кода и вообще, не одобряем нехороших веществ, разве что пластилин (не тот) и желуди. Даа, желуди нам определенно нравятся, ведь они овальные и в смешных шапочках. + +Но! шутки в сторону. В ваших руках не просто пластилин и желуди, в ваших руках - Dependency Injection Framework для любимого языка программирования. Теперь ваши объекты будут ~~опрятными и шелковистыми~~ создаваться сами, и не нужно будет думать как добыть параметры конструирования, сколько их, в каком порядке они идут. Достаточно сказать: "Хочу класс УгольныйКомбайн" - а марка колес, двигатель и прочие запчасти КАК-ТО создадутся и передадутся в конструктор. + +Теперь приложение может состоять из сложных компонентов, которые намного проще менять и перенастраивать. Вот был у вас класс `ОбновляторВерсии1С`. В конструкторе получал логин/пароль пользователя. И все было хорошо, но понадобилось вам в этот класс добавить знание об уже установленных версиях 1С, чтобы не скачивать лишние с сайта. Можно прямо в этом классе написать проверку установленных версий, но это нарушение ПЕО (Принцип Единой Ответственности): проверятор версий не надо смешивать с обновлятором. Мало ли в каких еще местах пригодится проверятор версий, а мы его жестко внутрь другого класса зашьем… Повторное использование - наше все. + +Чтобы всё было по красоте, нам надо передать в конструктор объект `ПроверяторВерсий`, который предоставит `Обновлятору1С` информацию о том, что за версии у нас уже установлены. И все бы ничего, но `Обновлятор1С` создается через `Новый` в тысяче мест. В эти места нужно залезть, и дополнительно там создать `ПроверяторВерсий` и передать его в конструктор... А если `Проверятор` тоже имеет зависимость (а кто ее не имеет в наше сложное время, а?), тогда придется протащить всё дерево зависимостей через все методы и компоненты чуть ли не от самого старта приложения. Так жить нельзя, такая жесткая связность будет мешать развитию приложения и усложнять его. + +Осенью, когда вам хочется создать `Обновлятор1С`, вы просто говорите ~~"Лунная призма, дай мне силы!"~~ `Осень.НайтиЖелудь("Обновлятор1С")` - и все зависимости зависимостей создадутся сами. Сколько у них параметров в конструкторе, как они создаются, кто им передает параметры и где берет - это знает только "ОСень". К чему вдаваться в метафизику… "ОСень" возьмет ваши проблемы на себя, главное - показать ей, где лежит пластилин... + +## Как с этим работать? + +### Инициализация приложения + +"ОСень" - это вам non penis canina est, а фреймворк. Любой порядочный фреймворк нужно немножечко сконфигурировать, чтобы дальше все заработало. + +Для инициализации контекста "ОСени" служит класс `Поделка`, который необходимо создать через `Новый` (один разочек можно и написать это вредное слово), а затем наполнить Желудями, Дубами и Напильниками. Нет, мы не упоролись, скоро расскажем, что тут к чему. + +Инициализировать контекст можно так: + +```bsl +// file: main.os + +#Использовать autumn + +Поделка = Новый Поделка(); +``` + +И... всё. + +При создании Поделки ОСень автоматически просканирует все доступные в системе типов классы, определит, кто их них желудь, а то дуб, и последовательно добавит их в контекст. + +Вероятно, помимо загрузки собственно ОСени, вы захотите так же зарегистрировать и собственные классы. Единственное, что для этого нужно, это выполнить обычное для OneScript подключение библиотек по имени или пути: + +```bsl +// file: main.os + +#Использовать autumn +#Использовать "." + +Поделка = Новый Поделка(); +``` + +### Запуск приложения + +После добавления всех желудей, дубов и прочих частей в нашу поделку мы готовы к запуску приложения. + +```bsl +Поделка.ЗапуститьПриложение(); +``` + +В чем суть? Жизненный цикл нашей поделки разделен на две фазы: + +- фаза инициализации, когда мы можем только добавлять в поделку новые компоненты, но еще не разрешаем им взаимодействовать; +- фаза выполнения, когда в нашу поделку вдыхается жизнь ~~и выдыхаются желуди~~, наше приложение запускается и мы можем наконец начать делать что-то полезное. + +### Объявление компонента + +Как вы, наверное, догадались по словосочетанию "компонентное приложение", основой вашего приложения становится `Желудь`. Жёлудь - это всё, и всё есть жёлудь. `ОбновляторВерсии1С`? Жёлудь. `ПроверяторУстановленныхВерсий`? Тоже Жёлудь. И даже логин с паролем - это тоже в некотором роде жёлуди. + +Основной способ обозначения класса как желудя - это навешивание аннотации `&Желудь` над конструктором объекта. Вроде такого: + +```bsl +// file: Классы/ПроверяторВерсий.os + +&Желудь +Процедура ПриСозданииОбъекта() + +КонецПроцедуры + +... прочая очень нужная, но абсолютно не интересная логика класса. +``` + +Главной характеристикой желудя является его имя. По умолчанию имя берется из имени типа (`ПроверяторВерсий` для случая выше), но может быть переопределено в параметре аннотации `&Желудь`. + +### Получение экземпляра компонента + +Мы определили желудь, настало время его создать! + +В создании компонентов нам поможет `Поделка`. Зря что ли мы накидывали в нее желудей? + +```bsl +// file: main.os + +ПроверяторВерсий = Поделка.НайтиЖелудь("ПроверяторВерсий"); + +ПроверяторВерсий.ЧтоНибудьПроверить(); +``` + +В результате выполнения куска кода выше в переменную `ПроверяторВерсий` прилетит свеженький блестящий желудь, добавленный ранее под именем "ПроверяторВерсий". Легко и просто, не правда ли? + +### Связывание компонентов между собой + +Что нужно двум многоуважаемым Желудям для связи друг с другом? Правильно, пластилин. Каждый ребенок знает, что хорошая поделка - это желуди, пластилин и г... Мы немного отвлеклись. + +Для указания зависимостей желудя служит аннотация `&Пластилин`. + +```bsl +// file: Классы/ОбновляторВерсий.os + +Перем _ПроверяторВерсий; +Перем _Логин; +Перем _Пароль; + +&Желудь +Процедура ПриСозданииОбъекта( + &Пластилин ПроверяторВерсий, + &Пластилин Логин, + &Пластилин Пароль +) + _ПроверяторВерсий = ПроверяторВерсий; + _Логин = Логин; + _Пароль = Пароль; +КонецПроцедуры +``` + + Как же создать такой сложный желудь, сверху донизу обмазанный пластилином? Точно так же. + +```bsl +// file: main.os + +ОбновляторВерсий = Поделка.НайтиЖелудь("ОбновляторВерсий"); + +ОбновляторВерсий.ОбновисьЧтоБыТамНеСтояло(); +``` + +Заметьте, никаких зависимостей передавать не нужно. "ОСень" все взяла на себя - по именам параметров нашла зарегистрированные желуди и передала их в конструктор объекта. + +Если вы начитались Овидия в оригинале или в вас вселился СОТОНА, а может просто потеряли совесть и захотели греческих букв в именах переменных, вы можете подсказать "ОСени", что за зависимость нужна в данном конкретном случае. + +```bsl +&Желудь +Процедура ПриСозданииОбъекта( + &Пластилин("ПроверяторВерсий") μ, + &Пластилин(Значение = "Логин") ξ, + &Пластилин("Пароль") ὦ +) + _ПроверяторВерсий = μ; + _Логин = ξ; + _Пароль = ὦ; +КонецПроцедуры +``` + +> Имя нужного желудя передается в параметре "Значение" аннотации "&Пластилин". Если аннотация имеет один параметр или вы передаете только значение параметра "Значение", то имя параметра можно опустить. + +### Фабрика компонентов + +Не все желуди обязаны являться полноценными классами с точки зрения системы типов 1Script. Согласитесь, странно заводить целый класс для хранения логина от ИТС, просто потому что кто-то пережарил желудей. + +Философский вопрос в зал: откуда берутся желуди? Кто-нибудь? Может быть вы, в свитере цвета осенней листвы? Правильно, желуди растут на дубах! Дуб является источником желудей. На ветвях образуются цветочки, из цветочков появляются завязи, а из завязи - желуди. + +И что самое приятное, дуб когда-то тоже был желудем, а значит, к нему применимы те правила игры, что и к обычным компонентам-желудям. + +Итак, мы хотим передать Обновлятору логин и пароль в виде желудей. Для этого в новом классе, помеченном аннотацией `&Дуб`, нужно объявить два метода, помеченные аннотацией `&Завязь`. На дубе завязи, из завязей получатся желуди. Логично? Логично. Поехали! + +```bsl +// file: Классы/ДанныеАвторизации.os + +&Дуб +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +&Завязь(Тип = "Строка") +Функция Логин() Экспорт + Возврат ПеременныеСреды().USERNAME; +КонецФункции + +&Завязь(Тип = "Строка") +Функция Пароль(&Пластилин Логин) Экспорт + Если Логин = "user" Тогда + Возврат "password"; + КонецЕсли; + + Возврат ПеременныеСреды().PASSWORD; +КонецФункции +``` + +В листинге выше объявляются две функции-конструктора, возвращающие желуди. Как вы видите, желудь может быть чем угодно, а что угодно (в данном случае - строка) может быть желудем. + +Т. к. `Дуб` - это тоже желудь, а методы с "Завязью" - это псевдо-конструкторы, то такой метод может быть скреплен пластилином с другими желудями. Плохие желуди могут даже хардкодить значения паролей, но мы закроем на это глаза. + +В листинге выше `&Завязь` содержит параметр `Тип`. Он требуется, если из имени функции непонятно, что за тип она вернет. Ну, нам-то с вами понятно, что `Пароль` - это строка, но вот глупой железке надо немного помочь. + +С другой стороны, если вы объявляете желудь с "типовым" именем, то и параметр добавлять не нужно: + +```bsl +&Завязь +Функция Строка(&Пластилин Логин, &Пластилин Пароль) Экспорт + Возврат Логин + ":" + Пароль; +КонецФункции +``` + +### Внедрение зависимостей в поля и функции-сеттеры + +Не всегда бывает удобно пихать желуди прямо в конструктор. Например, желуди могут так сильно зависеть друг от друга, что образуют циклическую зависимость. Как Заяц-Волк, только Желудь-Желудь. В таком случае внедрить зависимости через конструктор не получится. На помощь нам придут внедрение желудей через поля и функции, принимающие значения. + +```bsl +// file: Классы/Обновлятор1С.os + +&Пластилин +Перем Логин; + +Перем Пароль; + +&Пластилин +Процедура УстановитьПароль(Значение) Экспорт + Пароль = Значение; +КонецПроцедуры + +&Желудь +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +При получении желудя `Обновлятор1С` зависимость `Логин` будет внедрена в поле напрямую, а зависимость `Пароль` установлена через процедуру `УстановитьПароль`. + +Как и в случае с подстановкой желудей в конструктор, имя конкретного желудя может быть переопределено в параметре аннотации `&Пластилин`. По умолчанию имя внедряемого желудя берется либо из имени поля ("Логин") либо из имени метода, из которого отбрасывается префикс "Установить": `УстановитьПароль` `->` `Пароль`. + +### Внедрение настроек приложения + +Хорошая поделка состоит не только из желудей и пластилина, но и обвешана маленькими приятными глазу детальками. Причем разные поделки, собранные по одному и тому же чертежу, могут только этими детальками и отличаться. + +В примере выше мы передавали Обновлятору1С данные авторизации через переменные среды, читающиеся с помощью `&Дуба` и его `&Завязей`. Но это не единственный способ работы с настройками приложения. + +Осень содержит в себе механизм передачи желудю настроек приложения напрямую из конфигурационного файла. Для этого служит аннотация `&Деталька`. `&Пластилин` помогает связать между собой несколько полноценных `&Желудей`, тогда как `&Деталька` налепляется сразу на нужный `&Желудь`. + +> Поддержка загрузки настроек из переменных окружения и аргументов командной строки в ближайших планах разработки. Еще год-два и точно запилим, честно-честно! + +Конфигурационный файл может быть в виде `json`, `yaml` или `ini` файла, называется `autumn-properties.json`/`autumn-properties.yml`/`autumn-properties.ini` соответственно и ищется ОСенью в каталоге запуска приложения или в подкаталоге `src`. + +```json +{ + "Логин": "user", + "Пароль": "pass", + "ПрочиеНастройки": { + "Настроение": "Хорошее" + } +} +``` + +А вот так их можно использовать в жёлуде: + +```bsl +// file: Классы/Обновлятор1С.os + +&Деталька +Перем Логин; + +&Деталька +Перем Пароль; + +&Деталька("ПрочиеНастройки.Настроение") +Перем Настроение; + +&Деталька(Значение = "ПрочиеНастройки.ОтветНаГлавныйВопросЖизниВселеннойИВсегоОстального", ЗначениеПоУмолчанию = 42) +Перем Ответ; + +&Желудь +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +Вы можете добавлять к желудю любые детальки вне зависимости от того, есть они в конфигурационном файле или нет. + +По умолчанию "ОСень" будет пытаться найти значение настройки по имени параметра конструктора: в json есть параметры "Логин" и "Пароль", в конструкторе есть параметры "Логин" и "Пароль", искра, буря, безумие! + +JSON обычно содержит вложенные объекты и массивы. Путь к настройкам в таких вложенных структурах можно указать в параметре аннотации `&Деталька` в формате библиотеки [configor](https://github.com/khorevaa/configor), используя точки для объектов и квадратные скобки для массивов. + +Не обязательно заставлять пользователя указывать все настройки в конфигурационном файле, если ему могут подойти значения настроек по умолчанию. Для указания значения по умолчанию в `&Детальке` есть параметр `ЗначениеПоУмолчанию`. Поразительно, не правда ли? + +### Произвольные аргументы конструктора + +Иногда понимание того, что же еще добавить в поделку, приходит в самый последний момент. Буквально после запуска приложения, кодом, на основании какой-то хитрой логики. Да, можно сделать хитрый `&Дуб`, передать в него в виде желудя расчетчик этой самой бизнес-логики, который выдаст нужную циферку, но порой хочется просто передать `42` в конструктор желудя. + +Для таких случаев в ОСени есть аннотация `&Блестяшка`. + +```bsl +// file: Классы/ЛенивыйЖелудь.os + +&Желудь +Процедура ПриСозданииОбъекта( + &Пластилин ПроверяторВерсий + &Блестяшка ФормулировкаВопроса +) +КонецПроцедуры +``` + +Как ее использовать? Двумя способами. Первый - это передать все параметры конструктора в виде массива в метод `Поделка.НайтиЖелудь()`: + +```bsl +ПараметрыКонструктора = Новый Массив; +ПараметрыКонструктора.Добавить(Поделка.НайтиЖелудь("ПроверяторВерсий")); +ПараметрыКонструктора.Добавить("Главный вопрос жизни, вселенной и всего такого"); + +ЛенивыйЖелудь = Поделка.НайтиЖелудь( + "ЛенивыйЖелудь", + ПараметрыКонструктора +); +``` + +Второй - передать только блестяшки. А весь `&Пластилин` и `&Детальки` ОСень подставит сама: + +```bsl +ПараметрыКонструктора = Новый Массив; +ПараметрыКонструктора.Добавить("Главный вопрос жизни, вселенной и всего такого"); + +ЛенивыйЖелудь = Поделка.НайтиЖелудь( + "ЛенивыйЖелудь", + ПараметрыКонструктора +); +``` + +`&Блестяшки` можно внедрять только в конструктор желудя. Внедрение в поля и методы жёлудя не поддерживается. Однако вы можете объявить `&Дуб` и `&Завязь`, куда `&Блестяшку` передать таки можно: + +```bsl +// file: Классы/ДубСЛенивымЖелудем.os + +&Дуб +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +&Завязь +Процедура ЛенивыйЖелудь( + &Пластилин ПроверяторВерсий + &Блестяшка ФормулировкаВопроса +) + ЛенивыйЖелудь = Новый ЛенивыйЖелудь(ПроверяторВерсий); + ЛенивыйЖелудь.ЗадатьВопрос(ФормулировкаВопроса); + + Возврат ЛенивыйЖелудь; +КонецПроцедуры +``` + +И в заключение темы, `ЛенивыйЖелудь` (не важно, созданный конструктором или `&Завязью`) можно внедрить в другой желудь, воспользовавшись повторяемым параметром `Блестяшка` в аннотации `&Пластилин`: + +```bsl +// file: Классы/ЛюбительЛенивыхЖелудей.os + +&Пластилин(Блестяшка = "Шестью девять?") +Перем ЛенивыйЖелудь; + +&Желудь +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +При поиске любителя ленивых желудей в него будет прилеплен `ЛенивыйЖелудь` с вопросом `Шестью девять?`. + +Повторяемые параметры аннотаций позволяют указывать один и тот же параметр аннотации несколько раз с разными значениями. Например, если у желудя в конструктор принимается несколько блестяшек, то и внедрить такой желудь можно указав несколько блестяшек в аннотации `&Пластилин`: + +```bsl +&Пластилин(Блестяшка = "Первое значение", Блестяшка = "Второе значение") +Перем ЖелудьСДвумяБлестяшками; +``` + +### Генератор экземпляров компонентов + +Иногда возникает необходимость получить желудь-зависимость отложено. Например, по какому-то условию или с передачей произвольных параметров. Эту задачу можно решить через внедрение в желудь `Поделки` целиком, но умные книжки учат нас, что зависеть от глобального контекста приложения - это вообще-то фу-фу-фу и не надо так. Для уменьшения связывания полезного кода с инфраструктурным в ОСени есть дополнительная аннотация `&Табакерка`. Если ее навесить над полем или параметром метода, над которым уже висит `&Пластилин`, `&Деталька` или `&Блестяшка`, то вместо внедрения в поле или параметр непосредственно нужного значения будет внедрен специальный объект типа `Табакерка`, который позволяет достать желудь/детальку/блестяшку по требованию. + +```bsl +// file: Классы/КлассСТабакеркой.os + +&Табакерка +&Пластилин +Перем ПроверяторВерсий; + +Функция ЧтоНибудьОтложеноПроверить(Версия) Экспорт + РеальныйПроверяторВерсий = ПроверяторВерсий.Достать(); + + Возврат РеальныйПроверяторВерсий.Проверить(Версия); +КонецФункции + +&Желудь +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +В примере выше в поле `ПроверяторВерсий` будет внедрен объект типа `Табакерка`, который позволит получить желудь `ПроверяторВерсий` по требованию. + +В метод `Табакерка.Достать()` можно передать параметр-массив с прилепляемыми частицами, аналогично тому, как это делается для `Поделка.НайтиЖелудь(ИмяЖелудя, МассивПрилепляемыхЧастиц)`. Таким образом, можно получить желудь с прилепленными к нему блестяшками или другими рассчитываемыми желудями. + +### Алиасы компонентов + +У желудей все как у людей. Есть свои увлечения, субкультуры и даже может быть `&Прозвище`. Желудь `Василий` в силу своего темного прошлого "у своих" зовется не иначе как `Васян`. Как это отразить в ОСени? + +```bsl +// file: Классы/Василий.os + +&Желудь +&Прозвище("Васян") +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +``` + +Как же разные группы желудей могут обращаться к нашему Василию? Сделать это можно и по имени и по прозвищу. И отзовётся при этом один и тот же желудь: + +```bsl +// file: main.os + +Василий = Поделка.НайтиЖелудь("Василий"); +Васян = Поделка.НайтиЖелудь("Васян"); + +Ожидаем.Что(Василий).Равен(Васян); +``` + +Василий может иметь много прозвищ. Поэтому аннотация &Прозвище повторяемая, т. е. на желудь ее можно навесить несколько раз с разными значениями и величать кого угодно и как угодно. + +### Группировка компонентов по алиасу + +Представьте себе компанию панков. Каждый из них уникален, каждый - личность. Но при этом у них есть и что-то общее - все они панки. К каждому можно обратиться как: "Эй, ты, панк!", и он не только отзовется громогласным "Хой!", но возможно даже предложит Вам пива. + +Попробуем описать нашу небольшую и дружную компанию: + +```bsl +// file: Классы/ДжонниРоттен.os + +&Желудь +&Прозвище("Панк") +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +```bsl +// file: Классы/СтивДжонс.os + +&Желудь +&Прозвище("Панк") +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +```bsl +// file: Классы/ПолКук.os + +&Желудь +&Прозвище("Панк") +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +```bsl +// file: Классы/СидВишес.os + +&Желудь +&Прозвище("Панк") +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +Чудесная компания собралась, неправда ли? Что же мы можем сделать с этими красавцами? Получить их как пачку желудей! В этом нам поможет метод `Поделка.НайтиЖелуди()`: + +```bsl +Панки = Поделка.НайтиЖелуди("Панк"); + +Ожидаем.Что(Панки).ИмеетТип("Массив"); +Ожидаем.Что(Панки.Количество()).Равно(4); + +Для Каждого Панк Из Панки Цикл + Панк.Хой(); +КонецЦикла; +``` + +Этот бэнд можно получить не только императивно, через `НайтиЖелуди`, но и внедрить в качестве зависимости. Для указания того, что мы ждем массив из панков, а не какого-то конкретного, у аннотации `&Пластилин` есть параметр `Тип`, который может принимать значения всех `&ПрилепляемаяКоллекция`, в частности `Массив`: + +```bsl +// file: Классы/РокБэнд.os + +Процедура ПриСозданииОбъекта(&Пластилин(Значение = "Панк", Тип = "Массив") Панки) + Для Каждого Панк Из Панки Цикл + Панк.Хой(); + КонецЦикла; +КонецПроцедуры +``` + +Аналогичный параметр (третий) есть и у метода `НайтиЖелуди`: + +```bsl +Панки = Поделка.НайтиЖелуди("Панк", , "Массив"); +``` + +По умолчанию, через `&Пластилин` внедряется единичный `Желудь`, а метод `НайтиЖелуди` возвращает `Массив`. + +Помимо единичного желудя и массива, РокБэнд можно получить в виде соответствия (`"Соответствие"`), где ключом будет имя ~~панка~~ жёлудя, а значением - сам жёлудь, и в виде таблицы значений (`"ТаблицаЗначений"`), в каждой строке которой будет содержаться `Имя`, `ОпределениеЖелудя` и сам `Желудь`. + +> И вообще, загляните в документацию библиотеки [autumn-collections](https://github.com/autumn-library/autumn-collections), там много вкусных типов! + +И даже больше, РокБэнд можно получить своим собственным способом, для этого нужно создать у себя свой уникальный `СобирательПанков.os`, в котором нужно определить два метода, один который получает на входе панка и его описание, а второй который возвращает собранную банду + +```bsl +// СобирательПанков.os + +Перем Банда; + +// Тут нам на вход приходит очередной панк +Процедура Добавить(Панк, ОпределениеПанка) Экспорт + + Для Каждого РольИИсполнитель Из Банда Цикл + + Если Не ЗначениеЗаполнено(РольИИсполнитель.Значение) Тогда + + Банда.Вставить(РольИИсполнитель.Ключ, Желудь); + Прервать; + + КонецЕсли; + + КонецЦикла; + + Коллекция.Добавить(ОпределениеЖелудя.Имя()); + +КонецПроцедуры + +// А тут нам надо вернуть собранную банду +Функция Получить() Экспорт + Возврат Новый ФиксированнаяСтруктура(Банда); +КонецФункции + +&ПрилепляемаяКоллекция("СобирательПанков") +Процедура ПриСозданииОбъекта() + + Банда = Новый Структура( + "Гитарист, Барабанщик, БасГитарист, Вокалист" + ); + +КонецПроцедуры +``` + +Теперь нам вернётся целая собранная группа: + +```bsl + +Панки = Поделка.НайтиЖелуди("Панк", , "СобирательПанков"); + +// > Панки.Гитарист +// > Панки.Барабанщик +// > Панки.БасГитарист +// > Панки.Вокалист + +``` + +### Упорядочивание компонентов при получении в виде сортированных коллекций + +Если вы прилепляете себе несколько желудей с общим прозвищем в виде массива или таблицы значений, вам может понадобиться расставить их по некоторому порядку. Не будем вдаваться в подробности, кто в SexPistols третий панк, а кто второй, но точно можно предположить, что сессионные музыканты должны быть в самом конце массива панков. В этом нам поможет аннотация `&Порядок`, принимающая числовое значение порядка. + +```bsl +// file: Классы/СессионныйМузыкант.os + +&Желудь +&Прозвище("Панк") +&Порядок(5) +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +По умолчанию, каждый желудь имеет порядок, равный единице. + +> &Рогатка и &Напильник тоже могут быть упорядочены! + +### Приоритизация компонентов с общим алиасом + +Но давайте будем честны. Когда мы говорим про Sex Pistols, то в первую очередь вспоминаем Сида Вишеса, и только потом уже Джонни Роттена и остальных. А какой у него получился алко-my-way, ух! Аннотация `&Верховный` подскажет "ОСени", кого вы считаете панком "по умолчанию": + +```bsl +// file: Классы/ДжонниРоттен.os + +&Желудь +&Прозвище("Панк") +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +--- +// file: Классы/СидВишес.os + +&Верховный +&Желудь +&Прозвище("Панк") +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +Без аннотации `&Верховный` следующий код выдал бы ошибку, но с ней он прекрасно работает: + +```bsl +Панк = Поделка.НайтиЖелудь("Панк"); + +Ожидаем.Что(Панк).ИмеетТип("СидВишес"); +``` + +### Переопределение компонента по имени + +Сила Верховного желудя настолько велика, что он может вытеснить данные о более слабом желуде из Поделки. + +Вспомним любимые "типовые". Представьте, что есть некая типовая форма документа, которая нам почему-то не нравится. Например, хотим ее упростить для пользователя. Или как-нибудь ее покорежить. Можно влезть в код формы, но не всегда хочется так делать, а иногда и просто нельзя (привет, расширения и базовые конфигурации). На помощь нам может прийти копирование формы (с последующей модификацией) и/или подмена базовой формы собственной реализацией. Причем тут желуди? + +От "типовой" нам пришла реализация некоего "важного компонента". + +```bsl +// file: Классы/ТиповойКомпонент.os + +// важная, но все еще ненужная логика класса. + +&Желудь("ВажныйКомпонент") +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +Но она нам не нравится, и мы хотим подменить этот желудь собственной реализацией. + +```bsl +// file: Классы/НашСобственныйКомпонент.os + +// важная, но все еще ненужная логика класса. + +&Верховный +&Желудь("ВажныйКомпонент") +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +Обратите внимание, что оба желудя описывают себя как "ВажныйКомпонент", то есть имеют одинаковое *имя*. В обычной ситуации ОСень выкинула бы исключение, т. к. не может быть двух желудей с одним именем. + +Но использование аннотации `&Верховный` подсказывает ОСени, что наша собственная реализация должна превалировать над типовой реализацией и позволит заместить желудь "ВажныйКомпонент" нашей версией. + +> Два &Верховных желудя с одинаковым именем ожидаемо выкинут исключение. +> +> There can be only one! + +### Пост-инициализация компонента + +Если вы все еще внимательно следите за нитью документации, у вас мог возникнуть вопрос вида ~~"Что за херня тут происходит"~~ "В каком порядке внедряются зависимости желудя?". И это очень хороший вопрос. + +Установить значения в поля несозданного объекта или вызвать в нем какой-либо метод довольно проблематично. Поэтому: + +- объект сначала создается (и вызывается его конструктор `ПриСозданииОбъекта`); +- затем пластилином обмазываются поля класса; +- оставшиеся куски пластилина идут на внедрение зависимостей через вызов методов. + +В такой ситуации может возникнуть желание что-нибудь поделать с желудем, когда в него уже всё-всё внедрено. И такая возможность есть! Создаем новый метод (на этот раз без пластилина) и указываем над им аннотацию `&ФинальныйШтрих`. + +```bsl +// file: Классы/КлассСПостИнициализацией.os + +&Пластилин +Перем Логин; + +&ФинальныйШтрих +Процедура Напоследочек() Экспорт + Сообщить("Логин здесь уже доступен: " + Логин); +КонецПроцедуры + +&Желудь +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +### Уникальность экземпляров компонентов + +Желуди почти как люди. У каждого есть свой `&Характер`. Кто-то показывает его явно, а кто-то ведет себя "как все". + +Большинство желудей характеризуют себя как `Одиночка`. Все желуди, которым понадобился `ОбновляторВерсий` как зависимость, получат один и тот же экземпляр Обновлятора с одним и тем же (очень одиноким) состоянием. Ну, вы знаете. Одинокий одиночка, одиноко идущий по одинокой дороге. Один. + +Однако не всегда это удобно. Предположим, вы написали свой супер-пупер уникальный генератор случайных чисел, который дает чудесное распределение. И хотите предоставить приложению возможность получать результат работы генератора в виде желудя, как зависимость. Будет не очень здорово, если все компоненты вашего приложения получат абсолютно случайно попавшееся число 42, не правда ли? Нам не подходят желуди-одиночки, нужно что-то более дружелюбное. Компанейское! + +В решении этой проблемы нам поможет аннотация `&Характер`. + +```bsl +// file: Классы/МойГенератор.os + +Перем ГСЧ; + +&Завязь +&Характер("Компанейский") +Функция СлучайноеЦелое() Экспорт + Возврат ГСЧ.СлучайноеЦелое(); +КонецФункции + +&Дуб +Процедура ПриСозданииОбъекта() + ГСЧ = Новый ГенераторСлучайныхЧисел(); +КонецПроцедуры +``` + +Данный уникальный в своем роде генератор случайных чисел является "Дубом", то есть источником желудей. Его отличительной особенностью является его `Компанейский` характер. Теперь любой желудь, который попросит себе зависимость `СлучайноеЧисло`, действительно получит случайное число! + +### Дополнительная обработка компонента + +Предположим, вы хотите сделать лошадку из желудей. Что для этого нужно? Для начала надо взять несколько желудей. Они будут немного отличаться друг от друга: тот, что покрупнее, пойдет на тело лошадки, тонкие желуди пойдут ноги, а вот этот смешной желудь в виде конуса будет мордой нашей лошадки. Конечно же, обмажем все пластилином, чтобы оно держалось вместе. + +Вы смотрите на получившуюся лошадку и понимаете: что-то не то. Желуди-то все блестящие, полированные! А вы так мечтали о теплой и матовой лошадке. Что же делать? Есть решение: желуди нужно обработать напильником, чтобы придать им приятный матовый оттенок. + +Конечно же вы можете добавить нужный код по приведению желудя к матовому цвету, например, в `ПриСозданииОбъекта`. Но желуди-то разные, копипастить код между разными компонентами... Как-то фу. Хорошо, что "ОСень" может нам помочь. + +Для дополнительной обработки объекта помимо "желудей" и "дубов" можно использовать `&Напильник`. Это специальный объект с методом `ОбработатьЖелудь`, который будет вызываться при каждом создании нового желудя. + +```bsl +// file: Классы/ПриданиеМатовогоЦветаЖелудям.os + +Функция ОбработатьЖелудь(Желудь, ОпределениеЖелудя) Экспорт + ВжухнутьРазочек(Желудь); + + Возврат Желудь; +КонецФункции + +&Напильник +&Порядок(10) +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +Метод обработки напильником возвращает желудь, причем не обязательно возвращать *тот же самый* желудь. Вам может захотеться обернуть его в объект-контейнер и накинуть на него несколько новых методов, например, с помощью [decorator](https://github.com/nixel2007/decorator). + +Не каждый напильник стоит применять ко всем желудям. Вспоминая замечательный пример с панками, мы можем захотеть добавить напильник для полировки заклепок на напульсниках наших музыкантов. При этом очевидно, что далеко не все носят напульсники, да еще и с заклепками. В задаче ограничения применения напильника поможет повторяемый параметр аннотации `&Напильник` под названием `ПрименяетсяТолькоНа`. В нем можно указать имена или прозвища желудей, к которым применяется данный напильник. + +```bsl +// file: Классы/ПолировщикЗаклепок.os + +&Напильник(ПрименяетсяТолькоНа = "Панк") +Процедура ПриСозданииОбъекта() + +КонецПроцедуры +``` + +С другой стороны сам желудь может захотеть ограничить список напильников, которые могут по нему вжухнуть. Или даже вовсе отключить вжухание всех напильников. Для этого на желудь можно навесить аннотацию `&ОсобоеОбращение`, которая может принимать булев параметр `ОтключитьВсеНапильники`, повторяемый строковый параметр `ПрименятьТолькоНапильник` и опять же повторяемый строковый параметр `НеПрименятьНапильник`. Назначение первого параметра, надеемся, достаточно очевидно, поэтому вот пример работы с одним из других: + +```bsl +// file: Классы/ПозерВНапульсниках.os + +&Желудь +&ОсобоеОбращение( + ПрименятьТолькоНапильник = "ПолировщикЗаклепок" +) +Процедура ПриСозданииОбъекта() +КонецПроцедуры +``` + +Зачем все это может быть нужно? Перфоманса ради, например. В случае большого количества компанейских желудей, которые не нуждаются в обработке напильником, можно отключить работу всех напильников для них. Ведь зачем тратить время на обработку, если она не нужна? При этом можно сохранить все плюшки от внедрения зависимостей и деталек через конструктор. Удобненько. + +Напильник в этой удивительной осенней вселенной тоже является желудем, поэтому может иметь зависимости от других желудей. Но тут надо аккуратно - можно окончательно упороться и улететь таки на дно циклических зависимостей. + +> Чтобы уберечь себя ото дна, все напильники инициализируются перед запуском приложения. Как в жизни - сначала разложил рядом инструменты, а потом начинаешь творить. + +"ОСень" в своем составе уже содержит два напильника: один отвечает за внедрение желудей в поля и методы установки значений, а второй - за вызовы методов `&ФинальныйШтрих`. + +Любой порядок стремится к хаосу, а несколько напильников - к порядку. Чтобы не запутаться, кто в какой последовательности вжухает по желудю каждому `Напильнику` в аннотации `&Порядок` можно/нужно задать порядок исполнения. Напильник внедрения зависимостей имеет порядок `0`, а напильник "Финального штриха" - `999999`. Вы вольны занять любое значение порядка между этими границами. + +### Использование контекста приложения + +`Поделка` является входной точкой для работы вашего приложения. Через него вы собираете информацию о ваших желудях, инициализируете их. Приятной особенностью "поделки" является то, что он сам по себе тоже является желудем! Все есть желудь, помните же? + +Поэтому вам никто не запретит с помощью `Пластилина` прилепить `Поделка` в ваш `Желудь`, `Дуб` или даже `Напильник`. Главное - берегите свое ментальное здоровье. + +### Условная регистрация компонентов в контексте приложения + +Иногда вам может потребоваться выбирать, какие именно классы вы хотите добавлять в свою поделку. Например, вы хотите загружать определенный класс только при выполнении какого-нибудь условия (из-за конфликта имен, к примеру). На помощь придёт объект `&Заготовка`, который можно расценивать как готовый набор желудей, добавляемый в поделку. + +`&Заготовка` - это очень хитрый `&Желудь`! В отличие от обычных желудей он автоматически создается на фазе инициализации приложения, а затем у него вызывается метод `ПриИнициализацииПоделки(Поделка)`. Желудёвость заготовки позволяет пользоваться частью возможностей обычных желудей - на нем могут срабатывать `&Напильники`, добавленные в `Поделку` до `&Заготовки`, однако из всего многообразия ~~веществ~~ зависимостей ему доступны только `&Детальки` с настройками приложения. Ни тебе `&Пластилина`, ни `&Блестяшек` навесить на `&Заготовку` нельзя. + +```bsl +// file: Классы/НаборЖелудей.os + +Перем _ПереопределитьПроверятор; + +&Заготовка +Процедура ПриСозданииОбъекта(&Деталька(ЗначениеПоУмолчанию = Ложь) ПереопределитьПроверятор) + _ПереопределитьПроверятор = ПереопределитьПроверятор; +КонецПроцедуры + +Процедура ПриИнициализацииПоделки(Поделка) Экспорт + + ПутьКСценарию = "./ОбычныйПроверятор.os"; + + Если _ПереопределитьПроверятор Тогда + ПутьКСценарию = "./НеобычныйПроверятор.os"; + КонецЕсли; + + ПодключитьСценарий(ПутьКСценарию, "Проверятор"); + +КонецПроцедуры +``` + +### Инверсия управления запуском приложения + +Одной из конечных точкой использования фреймворка для внедрения зависимостей является отказ от какой-либо логики при запуске приложения. В конце концов мы же жёлуди в поделку добавляем не просто так, а чтобы они могли `Сообщить("Привет, мир!")`. + +Вместо императивного стиля в виде поиска желудя и вызова у него нужного метода, мы можем добавить в `Поделку`... ещё один жёлудь с аннотацией `&Рогатка`. + +`&Рогатка` - это специальный желудь, содержащий метод `ПриЗапускеПриложения()`, который вызовется при, кхм, запуске приложения. Если еще точнее, при вызове `Поделка.ЗапуститьПриложение()`. + +```bsl +// file: Классы/ПришедшийСМиром.os + +&Деталька(ЗначениеПоУмолчанию = "Привет, Мир!") +Перем Приветствие; + +&Рогатка +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +Процедура ПриЗапускеПриложения() Экспорт + Сообщить(Приветствие); +КонецПроцедуры +``` + +`&Рогатка` является полноценным желудем, поэтому может принимать с свой конструктор и поля `&Детальки` и `&Пластилин`, а так же вжухается напильниками после создания. + +По умолчанию, все `&Рогатки` запускаются в фоновом задании (да, в OneScript есть фоновые задания!), затем ОСень начинает ожидать завершения работы каждой `&Рогатки` и выводить сообщения об ошибке, если вдруг выстрел не удался. Это поведение можно переопределить с помощью параметров аннотации с говорящими названиями `ЗапускатьВФоне` и `ОжидатьЗавершения`. + +```bsl +// file: Классы/СинхронноПришедшийСМиром.os + +&Рогатка(ЗапускатьВФоне = Ложь) +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +Процедура ПриЗапускеПриложения() Экспорт + Сообщить("Привет, Мир!"); +КонецПроцедуры +``` + +> Следует аккуратно писать код в `&Рогатках`, запускаемых не в фоновом режиме, т.к. в этом случае `Поделка` будет ожидать завершения такой не-фоновой `&Рогатки` и не запускать остальные `&Рогатки`. Если же `ПриЗапускеПриложения()` в такой `&Рогатке` выбросит исключение, то запуск приложения остановится, а работа приложения завершится. + +Как и любой `&Желудь`, `&Рогатка` может иметь аннотацию `&Порядок`. Она будет влиять на порядок запуска рогаток, что особенно важно для рогаток, запускающихся не в фоне. + +### Собственные аннотации + +OneScript могуч. В отличие от материнской платформы, над любым методом может быть любое количество произвольных аннотаций, у них могут быть именованные и неименованные параметры. Аннотации даже могут быть над `Перем`енными модуля и параметрами методов! Как тебе такое, УанЭс Энтерпрайз? + +Но с большой силой приходит большая ответственность. Произвольные параметры произвольных аннотаций довольно сложно контролировать и единообразно обрабатывать. Поэтому ОСень с одной стороны немного загоняет Вас в рамки, а с другой - дает возможность проверять корректность использования аннотаций пользователем Вашего приложения или библиотеки. Даже если это Вы сами. Будем честными, больше всего со своим кодом косячим мы сами. + +Помимо `&Пластилина`, `&Желудей` и прочих осенних продуктов, Вы можете описать собственную аннотацию. Для этого нужно создать новый класс, и навесить на него аннотацию `&Аннотация`. Здорово, правда? `(c)` + +```bsl +// file: Классы/МояАннотация.os + +&Аннотация("МояХитраяАннотация") +Процедура ПриСозданииОбъекта(Значение = "Трррунь", ОбязательныйПараметр) + +КонецПроцедуры +``` + +По аналогии с `&Желудем`, у `&Аннотации` можно переопределить имя. Конструктор аннотации может принимать в себя любое количество именованных `&Параметров`, в том числе "волшебный" параметр с именем `Значение`. Так же для параметра можно указать значение по умолчанию. + +> Слышали про дог-фудинг?.. +> +> Каждая аннотация в ОСени представлена отдельным классом АннотацияИмя с навешенной `&Аннотацией` над конструктором. +> +> Да, в ОСени есть аннотация `&Аннотация`, описание которой расположено в классе с именем `АннотацияАннотация.os`, над конструктором которого висит аннотация `&Аннотация` со `Значение` = `Аннотация`. Вот такая вот метафизика. + +Если вы хотите разрешить пользователю ваших аннотаций указывать несколько значений для одного и того же параметра, его нужно пометить аннотацией `&Повторяемый`. Например, вот так выглядит описание аннотации `&Пластилин`: + +```bsl +&Аннотация("Пластилин") +Процедура ПриСозданииОбъекта(Значение = "", Тип = "", &Повторяемый Блестяшка = Неопределено) + +КонецПроцедуры +``` + +### Наследование аннотаций + +Представьте себе ситуацию, когда над несколькими компонентами системы нужно развесить несколько аннотаций по одному и тому же принципу. Например, объявить много компанейских `&Желудей` с одинаковыми `&Прозвищами`. Чтобы упростить себе жизнь, можно создать новую аннотацию, в которой скомбинировать несколько других. Например: + +```bsl +// file: Классы/МояАннотация.os + +&Аннотация("КомпанейскийОбозванныйЖелудь") +&Характер("Компанейский") +&Желудь +&Прозвище("Обозванный") +Процедура ПриСозданииОбъекта() + +КонецПроцедуры +``` + +Имея такую аннотацию, можно применить ее к любому компоненту: + +```bsl +// file: Классы/Компонент.os + +&КомпанейскийОбозванныйЖелудь +Процедура ПриСозданииОбъекта() + +КонецПроцедуры +``` + +При инициализации контекста этот компонент будет автоматически помечен как `компанейский` `&Желудь` и с `&Прозвищем` "Обозванный". + +Можно добавить в нашу новую аннотацию универсальности, объявив в конструкторе новый `&Параметр` и доопределив метод `ПриРазворачиванииАннотации`: + +```bsl +// file: Классы/МояАннотация.os + +Перем СохраненноеЗначение; + +&Аннотация("КомпанейскийОбозванныйЖелудь") +&Характер("Компанейский") +&Желудь +&Прозвище("Заглушка") +Процедура ПриСозданииОбъекта(Значение) + СохраненноеЗначение = Значение; +КонецПроцедуры + +Процедура ПриРазворачиванииАннотации(ОпределениеАннотации, ПодчиненныеАннотации, ТипВладельцаСвойств, Свойство) Экспорт + Аннотация = РаботаСАннотациями.НайтиАннотацию( + ПодчиненныеАннотации, + "Прозвище" + ); + + РаботаСАннотациями.УстановитьЗначениеПараметраАннотации( + Аннотация, + "Значение", + СохраненноеЗначение + ); +КонецПроцедуры +``` + +```bsl +// file: Классы/Компонент.os + +&КомпанейскийОбозванныйЖелудь("Каштан") +Процедура ПриСозданииОбъекта() + +КонецПроцедуры +``` + +Пример кода выше хоть и может вызвать дикое желание записаться на курсы французского языка в Балашихе, сам по себе довольно прост. Значение нового `&Параметр`, принятого в конструктор аннотации, сохраняется в переменную `СохраненноеЗначение`. + +При инициализации поделки происходит "разворачивание" всех имеющихся аннотаций как над свойствами так и над методами. Если в Вашей аннотации объявлен метод `ПриРазворачиванииАннотации`, то он будет вызван с параметром `ОпределениеАннотации`, содержащей информацию о текущей аннотации и всех подчиненных аннотациях первого уровня. + +В примере выше среди подчиненных аннотаций ищется аннотация `Прозвище` и ей устанавливается значение из переменной `СохраненноеЗначение`. Таким образом для `Компонент` будет установлено `&Прозвище` `"Каштан"`. + +### Обработка аннотаций + +Если Вы уже попробовали делать свои `&Напильники`, то скорее всего ознакомились с классом `ОпределениеЖелудя`. Помимо хранения метаинформации о собственно желуде, методы `Свойства()` и `Методы()` возвращают информацию об аннотациях над свойствами и методами уже в "развернутом" и плоском виде. + +Если же у Вас на руках только тип объекта, голый рефлектор и необходимость работы с аннотациями в обход "ОСени" (не надо так), то с помощью `&Пластилина` можно прилепить к себе желудь `РазворачивательАннотаций`, в котором можно найти методы `РазвернутьАннотацииСвойств` и `РазвернутьАннотацииСвойства`. И да, данные метода от рефлектора туда тоже можно передать. + +Для всего остального есть модуль `РаботаСАннотациями`. + +## В предыдущих сериях + +В прошлых версиях библиотеки и/или документации вы могли встречать описание таких вещи как: + +* методы `Поделка.ДобавитьЖелудь()`/`Поделка.ДобавитьДуб()` и другие методы добавления желудей в поделку; +* метод `Поделка.ПросканироватьКаталог()`; +* модуль `Осень` и автоматическую регистрацию заготовок для запуска при создании поделки; +* модуль `ТипыПрилепляемыхЖелудей`; + +Все это устарело и теперь либо не используется, либо просто не нужно. Да, следить за чейнджлогами полезно! ## Заключение