Skip to content

sfaqer/autumn

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Autumn/ОСень

Что такое осень - это желудь!

Осень…. прекрасная пора. Ею вдохновлялся Пушкин, говорил, что осенью его особенно прёт. Именно это и произошло с авторами фреймворка "ОСень". Потому что осень - это прекрасно.
Когда приложение становится большим, вам потребуется дерево (желательно дуб) и немного пластилина. Ах, да, обязательно творческое осеннее настроение, чай и стабильная психика.

Фреймворк компонентных приложений для 1Script под названием "ОСень" поможет вам невероятным магическим образом компоновать ваше приложение из компонентов, которым не нужно заниматься собственным созданием или настройкой. И все это будет щедро обмазано пластилином и приятно пахнуть дубовыми вениками.

Оглавление

Зачем мне это?

Вот есть у вас объект, у которого объявлен конструктор с параметрами. И есть много мест, где он создается. Параметры этого конструктора называются зависимостями. То есть, объект не может жить без передачи ему этих параметров, он от них зависит.
А где взять значения этих параметров, чтобы передать в объект? Очевидно что их тоже надо создать (через Новый или откуда-то получить). А у них тоже есть зависимости, и у зависимостей зависимостей есть зависимости.

Зависимость. Да, вот слово, которое приходит в голову, когда изучаешь API "ОСени". Зависимость у авторов явно есть. Но это неправда. Мы зависимы только от просмотра зоопорно красивого кода и вообще, не одобряем нехороших веществ, разве что пластилин (не тот) и желуди. Даа, желуди нам определенно нравятся, ведь они овальные и в смешных шапочках.

Но! шутки в сторону. В ваших руках не просто пластилин и желуди, в ваших руках - Dependency Injection Framework для любимого языка программирования. Теперь ваши объекты будут опрятными и шелковистыми создаваться сами, и не нужно будет думать как добыть параметры конструирования, сколько их, в каком порядке они идут. Достаточно сказать: "Хочу класс УгольныйКомбайн" - а марка колес, двигатель и прочие запчасти КАК-ТО создадутся и передадутся в конструктор.

Теперь приложение может состоять из сложных компонентов, которые намного проще менять и перенастраивать. Вот был у вас класс ОбновляторВерсии1С. В конструкторе получал логин/пароль пользователя. И все было хорошо, но понадобилось вам в этот класс добавить знание об уже установленных версиях 1С, чтобы не скачивать лишние с сайта. Можно прямо в этом классе написать проверку установленных версий, но это нарушение ПЕО (Принцип Единой Ответственности): проверятор версий не надо смешивать с обновлятором. Мало ли в каких еще местах пригодится проверятор версий, а мы его жестко внутрь другого класса зашьем… Повторное использование - наше все.

Чтобы всё было по красоте, нам надо передать в конструктор объект ПроверяторВерсий, который предоставит Обновлятору1С информацию о том, что за версии у нас уже установлены. И все бы ничего, но Обновлятор1С создается через Новый в тысяче мест. В эти места нужно залезть, и дополнительно там создать ПроверяторВерсий и передать его в конструктор... А если Проверятор тоже имеет зависимость (а кто ее не имеет в наше сложное время, а?), тогда придется протащить всё дерево зависимостей через все методы и компоненты чуть ли не от самого старта приложения. Так жить нельзя, такая жесткая связность будет мешать развитию приложения и усложнять его.

Осенью, когда вам хочется создать Обновлятор1С, вы просто говорите "Лунная призма, дай мне силы!" Осень.ПолучитьЖелудь("Обновлятор1С") - и все зависимости зависимостей создадутся сами. Сколько у них параметров в конструкторе, как они создаются, кто им передает параметры и где берет - это знает только "ОСень". К чему вдаваться в метафизику… "ОСень" возьмет ваши проблемы на себя, главное - показать ей, где лежит пластилин...

Как с этим работать?

Инициализация приложения

"ОСень" - это вам non penis canina est, а фреймворк. Любой порядочный фреймворк нужно немножечко сконфигурировать, чтобы дальше все заработало.

Для инициализации контекста "ОСени" служит класс КонтекстПриложения, который необходимо создать через Новый (один разочек можно и написать это вредное слово), а затем наполнить Желудями, Дубами и Напильниками. Нет, мы не упоролись, скоро расскажем, что тут к чему.

Инициализировать контекст можно двумя способами.

  1. Через сканирование каталога:
// file: main.os

#Использовать autumn

КонтекстПриложения = Новый КонтекстПриложения();

КонтекстПриложения.ПросканироватьКаталог(ТекущийКаталог());

Сей нехитрый код заставит ОСень просканировать все *.os файлы в текущем каталоге (включая подкаталоги), понять, кто из них желудь, а кто дуб, и последовательно их зарегистрировать.

  1. Через непосредственную регистрацию:
// file: main.os

#Использовать autumn

КонтекстПриложения = Новый КонтекстПриложения();

КонтекстПриложения.ЗарегистрироватьЖелудь(Тип("ВерхнеуровневыйЖелудь"));
КонтекстПриложения.ЗарегистрироватьЖелудь(Тип("ЖелудьНижнегоУровня"));

КонтекстПриложения.ЗарегистрироватьДуб(Тип("ГлавныйДуб"));

Не так красиво, как первый вариант, зато гибкости побольше, если вдруг она необходима.

Объявление компонента

Как вы, наверное, догадались по словосочетанию "компонентное приложение", основой вашего приложения становится Желудь. Жёлудь - это всё, и всё есть жёлудь. ОбновляторВерсии1С? Жёлудь. ПроверяторУстановленныхВерсий? Тоже Жёлудь. И даже логин с паролем - это тоже в некотором роде жёлуди.

Основной способ обозначения класса как желудя - это навешивание аннотации &Желудь над конструктором объекта. Вроде такого:

// file: Классы/ПроверяторВерсий.os

&Желудь
Процедура ПриСозданииОбъекта()

КонецПроцедуры

... прочая очень нужная, но абсолютно не интересная логика класса.

Главной характеристикой желудя является его имя. По умолчанию имя берется из имени типа (ПроверяторВерсий для случая выше), но может быть переопределено в параметре аннотации &Желудь.

Получение экземпляра компонента

Мы определили желудь, настало время его создать!

В инстанцировании компонентов нам поможет КонтекстПриложения. Зря что ли мы рассказывали ему, где взять желудей?

// file: main.os

ПроверяторВерсий = КонтекстПриложения.ПолучитьЖелудь("ПроверяторВерсий");

ПроверяторВерсий.ЧтоНибудьПроверить();

В результате выполнения куска кода выше в переменную ПроверяторВерсий прилетит свеженький блестящий желудь, зарегистрированный ранее под именем "ПроверяторВерсий". Легко и просто, не правда ли?

Связывание компонентов между собой

Что нужно двум многоуважаемым Желудям для связи друг с другом? Правильно, пластилин. Каждый ребенок знает, что хорошая поделка - это желуди, пластилин и г... Мы немного отвлеклись.

Для указания зависимостей желудя служит аннотация &Пластилин.

// file: Классы/ОбновляторВерсий.os

Перем _ПроверяторВерсий;
Перем _Логин;
Перем _Пароль;

&Желудь
Процедура ПриСозданииОбъекта(
	&Пластилин ПроверяторВерсий,
	&Пластилин Логин,
	&Пластилин Пароль
)
	_ПроверяторВерсий = ПроверяторВерсий;
	_Логин = Логин;
	_Пароль = Пароль;
КонецПроцедуры

Как же создать такой сложный желудь, сверху донизу обмазанный пластилином? Точно так же.

// file: main.os

ОбновляторВерсий = КонтекстПриложения.ПолучитьЖелудь("ОбновляторВерсий");

ОбновляторВерсий.ОбновисьЧтоБыТамНеСтояло();

Заметьте, никаких зависимостей передавать не нужно. "ОСень" все взяла на себя - по именам параметров нашла зарегистрированные желуди и передала их в конструктор объекта.

Если вы начитались Овидия в оригинале или в вас вселился СОТОНА, а может просто потеряли совесть и захотели греческих букв в именах переменных, вы можете подсказать "ОСени", что за зависимость нужна в данном конкретном случае.

&Желудь
Процедура ПриСозданииОбъекта(
	&Пластилин("ПроверяторВерсий") μ,
	&Пластилин(Значение = "Логин") ξ,
	&Пластилин("Пароль") ὦ
)
	_ПроверяторВерсий = μ;
	_Логин = ξ;
	_Пароль =;
КонецПроцедуры

Имя нужного желудя передается в параметре "Значение" аннотации "&Пластилин". Если аннотация имеет один параметр или вы передаете только значение параметра "Значение", то имя параметра можно опустить.

Фабрика компонентов

Не все желуди обязаны являться полноценными классами с точки зрения системы типов 1Script. Согласитесь, странно заводить целый класс для хранения логина от ИТС, просто потому что кто-то пережарил желудей.

Философский вопрос в зал: откуда берутся желуди? Кто-нибудь? Может быть вы, в свитере цвета осенней листвы? Правильно, желуди растут на дубах! Дуб является источником желудей. На ветвях образуются цветочки, из цветочков появляются завязи, а из завязи - желуди.

И что самое приятное, дуб когда-то тоже был желудем, а значит, к нему применимы те правила игры, что и к обычным компонентам-желудям.

Итак, мы хотим передать Обновлятору логин и пароль в виде желудей. Для этого в новом классе, помеченном аннотацией &Дуб, нужно объявить два метода, помеченные аннотацией &Завязь. На дубе завязи, из завязей получатся желуди. Логично? Логично. Поехали!

// file: Классы/ДанныеАвторизации.os

&Дуб
Процедура ПриСозданииОбъекта()
КонецПроцедуры

&Завязь
Функция Логин() Экспорт
	Возврат ПеременныеСреды().USERNAME;
КонецФункциии

&Завязь
Функция Пароль(&Пластилин Логин) Экспорт
	Если Логин = "user" Тогда
		Возврат "password";
	КонецЕсли;
	
	Возврат ПеременныеСреды().PASSWORD;
КонецФункции

В листинге выше объявляются две функции-конструктора, возвращающие желуди. Как вы видите, желудь может быть чем угодно, а что угодно (в данном случае - строка) может быть желудем.

Т. к. Дуб - это тоже желудь, а методы "Завязью" - это псевдо-конструкторы, то такой метод может быть скреплен пластилином с другими желудями. Плохие желуди могут даже хардкодить значения паролей, но мы закроем на это глаза.

Внедрение зависимостей в поля и функции-сеттеры

Не всегда бывает удобно пихать желуди прямо в конструктор. Например, желуди могут так сильно зависеть друг от друга, что образуют циклическую зависимость. Как Заяц-Волк, только Желудь-Желудь. В таком случае внедрить зависимости через конструктор не получится. На помощь нам придут внедрение желудей через экспортные поля и функции, принимающие значения.

// file: Классы/Установщик.os

&Пластилин
Перем Логин Экспорт; // Для успешного внедрения поле должно быть экспортным.

Перем Пароль;

&Пластилин
Процедура УстановитьПароль(Значение) Экспорт
	Пароль = Значение;
КонецПроцедуры

&Желудь
Процедура ПриСозданииОбъекта()
КонецПроцедуры

При получении желудя Установщик зависимость Логин будет внедрена в поле напрямую, а зависимость Пароль установлена через процедуру УстановитьПароль.

Как и в случае с подстановкой желудей в конструктор, имя конкретного желудя может быть переопределено в параметре аннотации &Пластилин. По умолчанию имя внедряемого желудя берется либо из имени поля ("Логин") либо из имени метода, из которого отбрасывается префикс "Установить": УстановитьПароль -> Пароль.

Пост-инициализация желудя

Если вы все еще внимательно следите за нитью документации, у вас мог возникнуть вопрос вида "Что за херня тут происходит" "В каком порядке внедряются зависимости желудя?". И это очень хороший вопрос.

Установить значения в поля несозданного объекта или вызвать в нем какой-либо метод довольно проблематично. Поэтому:

  • объект сначала создается (и вызывается его конструктор ПриСозданииОбъекта);
  • затем пластилином обмазываются поля класса;
  • оставшиеся куски пластилина идут на внедрение зависимостей через вызов методов.

В такой ситуации может возникнуть желание что-нибудь поделать с желудем, когда в него уже всё-всё внедрено. И такая возможность есть! Создаем новый метод (на этот раз без пластилина) и указываем над им аннотацию &ФинальныйШтрих.

// file: Классы/КлассСПостИнициализацией.os

&Пластилин
Перем Логин Экспорт;

&ФинальныйШтрих
Процедура Напоследочек() Экспорт
	Сообщить("Логин здесь уже доступен: " + Логин);
КонецПроцедуры

&Желудь
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Уникальность экземпляров компонентов

Желуди почти как люди. У каждого есть свой &Характер. Кто-то показывает его явно, а кто-то ведет себя "как все".

Большинство желудей характеризуют себя как Одиночка. Все желуди, которым понадобился ОбновляторВерсий как зависимость, получат один и тот же экземпляр Обновлятора с одним и тем же (очень одиноким) состоянием. Ну, вы знаете. Одинокий одиночка, одиноко идущий по одинокой дороге. Один.

Однако не всегда это удобно. Предположим, вы написали свой супер-пупер уникальный генератор случайных чисел, который дает чудесное распределение. И хотите предоставить приложению возможность получать результат работы генератора в виде желудя, как зависимость. Будет не очень здорово, если все компоненты вашего приложения получат абсолютно случайно попавшееся число 42, не правда ли? Нам не подходят желуди-одиночки, нужно что-то более дружелюбное. Компанейское!

В решении этой проблемы нам поможет аннотация &Характер.

// file: Классы/МойГенератор.os

Перем ГСЧ;

&Завязь
&Характер("Компанейский")
Функция СлучайноеЦелое() Экспорт
	Возврат ГСЧ.СлучайноеЦелое();
КонецФункции

&Дуб
Процедура ПриСозданииОбъекта()
	ГСЧ = Новый ГенераторСлучайныхЧисел();
КонецПроцедуры

Данный уникальный в своем роде генератор случайных чисел является "Дубом", то есть источником желудей. Его отличительной особенностью является его Компанейский характер. Теперь любой желудь, который попросит себе зависимость СлучайноеЧисло, действительно получит случайное число!

Дополнительная обработка компонента

Предположим, вы хотите сделать лошадку из желудей. Что для этого нужно? Для начала надо взять несколько желудей. Они будут немного отличаться друг от друга: тот, что покрупнее, пойдет на тело лошадки, тонкие желуди пойдут ноги, а вот этот смешной желудь в виде конуса будет мордой нашей лошадки. Конечно же, обмажем все пластилином, чтобы оно держалось вместе.

Вы смотрите на получившуюся лошадку и понимаете: что-то не то. Желуди-то все блестящие, полированные! А вы так мечтали о теплой и матовой лошадке. Что же делать? Есть решение: желуди нужно обработать напильником, чтобы придать им приятный матовый оттенок.

Конечно же вы можете добавить нужный код по приведению желудя к матовому цвету, например, в ПриСозданииОбъекта. Но желуди-то разные, копипастить код между разными компонентами... Как-то фу. Хорошо, что "ОСень" может нам помочь.

Для дополнительной обработки объекта помимо "желудей" и "дубов" можно использовать &Напильник. Это специальный объект с методом ОбработатьЖелудь, который будет вызываться при каждом создании нового желудя.

// file: Классы/ПриданиеМатовогоЦветаЖелудям.os

Функция ОбработатьЖелудь(Желудь, ИмяЖелудя) Экспорт
	ВжухнутьРазочек(Желудь);
	
	Возврат Желудь;
КонецФункции

&Напильник(Порядок = 10)
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Метод обработки напильником возвращает желудь, причем не обязательно возвращать тот же самый желудь. Вам может захотеться обернуть его в объект-контейнер и накинуть на него несколько новых методов, например, с помощью (decorator)[https://github.com/nixel2007/decorator].

Напильник в этой удивительной осенней вселенной тоже является желудем, поэтому может иметь зависимости от других желудей. Но тут надо аккуратно - можно окончательно упороться и улететь таки на дно циклических зависимостей.

"ОСень" в своем составе уже содержит два напильника: один отвечает за внедрение желудей в поля и методы установки значений, а второй - за вызовы методов &ФинальныйШтрих.

Любой порядок стремится к хаосу, а несколько напильников - к порядку. Чтобы не запутаться, кто в какой последовательности вжухает по желудю у каждого Напильника в соответствующей аннотации можно/нужно задать Порядок исполнения. Напильник внедрения зависимостей имеет порядок 0, а напильник "Финального штриха" - 999999. Вы вольны занять любое значение порядка между этими границами. Не забудьте оставить немного места другим желающим!

Использование контекста приложения

КонтекстПриложения является входной точкой для работы вашего приложения. Через него вы собираете информацию о ваших желудях, инициализируете их. Приятной особенностью "контекста приложения" является то, что он сам по себе тоже является желудем! Все есть желудь, помните же?

Поэтому вам никто не запретит с помощью Пластилина прилепить КонтекстПриложения в ваш Желудь, Дуб или даже Напильник. Главное - берегите свое ментальное здоровье.

Заключение

Если вы думаете, что мы упоролись, то вы в чем-то даже правы. Напоследок отмечу, что среди вариантов именования аннотаций "ОСени" еще была связка Гриб/Грибница/Спора/Рецепт для Желудь/Дуб/Завязь/Напильник соответственно. Так что еще не все потеряно. Надеюсь.

Спасибо, что дочитали. <3

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • 1C Enterprise 100.0%