Работа с массивом в функциональном стиле

Разработка предоставляет программный интерфейс для манипулирования массивом, используя который вы получите возможность альтернативной записи кода для традиционных и новых возможностей. Например так:
ФасадМассива.НовыйМассив().Добавить(Элемент1).ПолучитьМассив();

Идея фасада в том, чтобы можно было совершать действия с массивом "пачкой", не размазывая простую по сути операцию на кучу строк.

Не вот так вот:

Результат = Новый Массив();

Результат.Добавить(ЧтоТоОдно);
Результат.Добавить(ЧтоТоДругое);

Возврат Результат;

А так:

Возврат ФасадМассива.МассивИзЭлементов(ЧтоТоОдно, ЧтоТоДругое).ПолучитьМассив();

Такое иногда называют функциональным стилем.

Бонусы при этом следующие:

  • уменьшается количество кода, убираются паразитные повторы имени массива
  • повышается читаемость, лаконичность
  • добавляются возможности не предусмотренные платформой

Ключевые моменты

  1. Работает все это исключительно в рамках встроенного языка и не лезет во внешние по отношению к платформе среды.
  2. Общий модуль помечен как Клиент и Сервер, но на клиенте модуль будет генерировать серверный вызов на каждом шагу.
  3. Код фактически исполняется в момент вызова, т.е. если "остановить" программу между двумя вызавами "Добавить(…)" — первый фактически будет выполнен, второй — нет.

Использование

Операции над массивом всегда начинаются с ключевого слова ФасадМассива, далее должна быть стартовая функция, указывающая на массив, с которым будем совершать дейтсвия, далее промежуточные функции (одна и более или может даже ни одной), далее может следовать (а может и нет) финишная функция, которая будет возвращать результат действий

Стартовые функции

  • НовыйМассив — указыват, что нужно создать новый массив для дальнейших операций. Например: ФасадМассива.НовыйМассив()…
  • МассивИзЭлементов — принимает от 1 до 10 параметров и делает из них массив для дальнейших операций. Например: ФасадМассива.МассивИзЭлементов(Перечисления.СтавкиНДС.НДС0, Перечисления.СтавкиНДС.НДС10, Перечисления.СтавкиНДС.НДС20)…
  • УстановитьМассив — устанавливает существующий массив для дальнейших операций, например: ФасадМассива.УстановитьМассив(ДопустимыеСтавкиНДС)…

Промежуточные функции

  • Добавить — добавляет элемент в конец массива. Например: ФасадМассива.НовыйМассив().Добавить("строка1")…
  • ДобавитьВсе — принимает на вход массив элементов и добавляет все его элементы в конец своего массива. Например: ФасадМассива.УстановитьМассив(ДопустимыеСтавкиНДС).ДобавитьВсе(ДополнительныеСтавки)…
  • Вставить — вставляет элемент в указанную позицию (сдвигает существующие элементы вправо). Например: ФасадМассива.НовыйМассив().Добавить("строка1").Вставить(0,"строка2")…
  • Установить — вставляет элемент в указанную позицию (перезаписывает элемент на указанной позиции). Например: ФасадМассива.НовыйМассив().Добавить("строка 1").Установить(0,"новый текст строки 1")…
  • ДляКаждого — выполняет операцию для каждого элемента массива. Например: ФасадМассива.УстановитьМассив(МассивНовыхОбъектов).ДляКаждого("Элемент.Записать()");
  • Отображение — выполняет операцию для каждого элемента массива, записывая результат операции на место исходного элемента. Фактически, так можно "перейти" от массива одних элементов к массиву других, соответствующих им элементов. Например: ФасадМассива.УстановитьМассив(ЗарплатыСНДФЛ).Отображение("Элемент * 0,87");
  • Удалить — удаляет элемент массива с соответствующим индексом. Например: ФасадМассива.УстановитьМассив(МассивНовыхОбъектов).Удалить(0)…
  • Подмассив — получает массив, эквивалентный указанному диапазону исходного массива. Например: ФасадМассива.УстановитьМассив(МассивНовыхОбъектов).Подмассив(0, 1)…
  • Различные — удаляет из массива дублирующие элементы. Например: ФасадМассива.УстановитьМассив(МассивНоменклатуры).Отображение("Элемент.СтавкаНДС").Различные()…

Финишные функции

  • ПолучитьМассив — возвращает массив, полученный в результате выполненных операций. Например: ФасадМассива.МассивИзЭлементов(Перечисления.СтавкиНДС.НДС0, Перечисления.СтавкиНДС.НДС10, Перечисления.СтавкиНДС.НДС20).ПолучитьМассив();
  • ПолучитьЭлемент — возвращает элемент с указанным индексом. Например, ФасадМассива.УстановитьМассив(МассивНовыхОбъектов).ПолучитьЭлемент(0);
  • ПолучитьЭлементИлиЗначениеПоУмолчанию — возвращает элемент с указанным индексом или, если элемента с таким индексом нет — значение по умолчанию. Позволяет задать поведение на случай, если элементов не нашлось. Например: ФасадМассива.УстановитьМассив(СтавкиНДС).ПолучитьЭлементИлиЗначениеПоУмолчанию(0, Перечисления.СтавкиНДС.НДС20)
  • ПолучитьЭлементИлиВызватьИсключение — возвращает элемент с указанным индексом или, если элемента с таким индексом нет — вызвает исключение. Если заведомо известно, что отсутствие элементов — исключительная ситуация — позволяет не проверять наличие элемента отдельно. Например: ФасадМассива.УстановитьМассив(СтавкиНДС).ПолучитьЭлементИлиВызватьИсключение(0, "Ставки НДС не найдены, обратитесь к администратору")

Как уже упоминалось, в базовом варианте, Фасад используется так:
[стартовая функция].[промежуточные функции].[финишная функция]
Например:

ФасадМассива.НовыйМассив().Добавить("строка1").ПолучитьМассив()

Но может иметь смысл и использование фасада без промежуточных функций:

ФасадМассива.МассивИзЭлементов(Перечисления.СтавкиНДС.НДС10, Перечисления.СтавкиНДС.НДС20).ПолучитьМассив();

Также, может иметь смысл фасад без финишной функции:

ФасадМассива.УстановитьМассив(МассивНовыхОбъектов).ДляКаждого("Элемент.Записать()");

Способ записи

Записывать код можно двумя способами, в одну строку:

Результат = ФасадМассива.НовыйМассив().Добавить(Перечисления.СтавкиНДС.НДС20).ПолучитьМассив();

или, если операций много, в несколько строк:

Результат = ФасадМассива.НовыйМассив()
.Добавить(Перечисления.СтавкиНДС.НДС0)
.Добавить(Перечисления.СтавкиНДС.НДС10)
.Добавить(Перечисления.СтавкиНДС.НДС20)
.ПолучитьМассив();

В прилагаемой концигурации, кроме основной функциональности, присутствует общий модуль ФасадМассиваТест, содержащий функции, тестирующие все функции. Платформа 8.3.9.2233. Навскидку, ничто не мешает даунгрейду хоть до 8.0. Код полностью открыт.

Для тех, кто дочитал до этого места в попытках понять "как, чёрт возьми, работает вся эта чёрная магия???" и не хочет качать конфу — отвечаю: ФасадМассива — это общий модуль. НовыйМассив, УстановитьМассив, Добавить и т.п. — это функции этого общего модуля. "Вытянуть" все функции в одну строчку можно благодара тому, что каждая функция (кроме финишных) возвращает ссылку на свой собственный общий модуль (Возврат Вычислить("ФасадМассива")). А контекст между функциями гуляет через ПараметрыСеанса, закэшированные через повторноиспользуемый модуль (отсюда и хождения на сервер при работе на клиенте).

Удачи!

21 Comments

  1. DNN13

    Что за бред

    Reply
  2. spacecraft

    Идея не нова и имеет право на жизнь.

    Только вот, «Такое иногда называю функциональным стилем.», кто fluent стиль называет функциональным?

    Reply
  3. m-rv

    (2) спасибо что обратили внимание, там опечатка, по задумке должно было быть «Такое иногда называюТ функциональным стилем»

    Идея не нова в разработке вообще или на 1Се кто-то такое уже пилил?

    Reply
  4. nicxxx

    Плагины для xUnitFor1C посмотрите. Ожидаем.что(переменная).не_().равно(23);

    Reply
  5. spacecraft

    (3) и на 1С тоже пилили. Даже попытки использовать ООП в 1С делали. А уж паттерны использовали неоднократно. На этом же сайте есть статьи.

    Reply
  6. m-rv

    (4) ага, крутая штука, спасибо. Все это, как я понимаю, аналог джавовского assert, а я отталкивался от stream, так что можно сказать родственные разработки )

    Reply
  7. cool99

    (2)

    кто fluent стиль называет функциональным

    Ты это расскажи тем, кто разработал Linq… лично я в EF предпочитаю именно флюент синтаксис вместо запросного. ИМХО он более нагляден, т.к. действия разбиты на последовательность, что легче читать. Тем более, что само выражение в linq to sql в итоге равно IQuerable, т.е. сам запрос формируется при непосредственном получении данных уже в IEnumerable. Да и с самими коллекциями он используется чаще.

    Reply
  8. spacecraft

    (7) еще раз прочти. Речь про «Функциональный стиль», а не функциональность. Ничего против Fluent interface не имею и он меня устраивает.

    Reply
  9. neikist

    Тоже для себя такое писал когда задолбался от неудобства стандартных методов. Но без функций первого класса и замыканий до функциональщины тут как до луны((

    Reply
  10. kote

    (9) (6) функции первого класса

    у меня получилось реализовать на 1С

    как то) но реакция сообщества как то вселило тоску..

    Пишу с телефона, ссылку не скину. Но посмотрите в публикациях, тут есть.

    Reply
  11. neikist

    (10)

    Я в итоге решил что проще не мучаться а свалить на язык который все это из коробки поддерживает. Я работаю ради удовольствия а не для того чтобы задолбываться и мучаться.

    Reply
  12. kote

    (0) Хождение через ПараметрыСеанста контекста — вот тут я бы уже бросил разработку по избранному Вами пути, т.к. в серьёзном проекте это будет источником постоянных проблем..

    Параметры сеанса — это глобальные переменные. Как только возникнет ситуация параллельного использования написанного таким образом кода — в параметре сеанса окажется непредсказуемое значение (какая из параллельно работающих функций туда сохранила значение?).

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

    Контекст должен быть.. не вот так -> ДОЛЖЕН БЫТЬ изолированным у каждой функции. Никаких совместно используемых параметров сеанса для функционирования всего этого быть не должно.



    Поэтому я запретил своим ребятам использование Вашей разработки..

    Но дайте знать, как решите эту проблему.

    Reply
  13. m-rv

    (12)

    я может не понимаю сути проблемы.. что значит «параллельное использование кода»? если речь о разных сеансах — то и параметры сеанса будут разные. в одном сеансе мне не известно способов затавить работать два потока исполнения

    Reply
  14. spacecraft

    (13) фоновые задания.

    Reply
  15. ImHunter

    (0) Ну хз. В текучих утверждениях BDD — да, в таком стиле писать красиво и приятно.

    Но вот в реализации для массивов — это как-то слишком мелко (для ФП). Мне кажется, букв меньше не становится.

    Reply
  16. m-rv

    (14) фоновое задание — это отдельный сеанс

    Reply
  17. m-rv

    (15)

    была идея стримы полностью запилить, решил забросить «пробный шар» с массивами. но по реакции сообщества понимаю, что стримы 1Су не нужны (

    Reply
  18. spacecraft

    (16) в том-то все и дело. Перенесли часть кода, где использовались параметры сеанса, в фоновые задания и все полетело.

    Reply
  19. kote

    (13) (14)не, в фоновых все будет изолировано. Там это будет работать.

    Но если использовать оповещения и ассинхронный вызов процедур, то получите в параметре сеанса непредсказуемое значение

    Reply
  20. kote

    (17) вы не так поняли. То что вы сделали нужно. Но реализация должна быть иной.

    Reply
  21. m-rv

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

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *