Расскажу о необычном подходе к работе с управляемыми формами.
Прежде чем начну свой скучный монолог, давайте немного познакомимся.
В мире 1С я существую с 2010 года. После университета попал в желтые лапы 1С в Первый Бит и почти 10 лет там проработал. Потом успел побыть франчом и сейчас работаю на хоумсайде в Тинькофф банке.
Люблю автоматизировать все, до чего дотягиваюсь. Считаю, что если какая-то рутинная работа появляется больше трех раз, ее надо автоматизировать. То же самое – если пишешь один и тот же код несколько раз, его нужно как-то привести в порядок.
В докладе я хочу обсудить проблемы, которые возникают у программистов при доработке конфигураций на управляемых формах:
-
Во-первых, непонятно, как дорабатывать типовые управляемые формы, чтобы впоследствии при обновлении не было невыносимо больно из-за того, что эти ужасные формы никак не управляются.
-
А во-вторых, если у вас команда работает в EDT или в GIT, не используя блокирующее хранилище, и два разработчика одновременно внесли изменения в одну и ту же форму, у вас опять начнутся очень серьезные боли и проблемы.
Чтобы успешно справиться с такими проблемами, нужно решать конфликты.
Конфликты при доработке форм и их последствия
Конфликтом я называю ситуацию, когда у нас одновременно в двух ветках разработки изменился один и тот же объект: либо код, либо форма, либо все вместе.
Конфликты приводят к боли.
Причина этой боли – в особенностях хранения структуры формы. Самая примитивная форма, у которой меньше десяти элементов, внутри превращается в огромный монструозный XML.
Обратите внимание, что узлы этого XML обозначены идентификаторами и так далее. По сути, это обычная целочисленная переменная, которая при добавлении каждого нового реквизита итерируется. Очевидно, что, когда люди работают параллельно, эта цифра начинает дублироваться и пересекаться.
Чтобы прочувствовать, как проявляется эта боль, давайте попробуем на форме что-то доработать.
Например, наш любимый заказчик сказал, что на форме нужно добавить команду, которая делает все хорошо. Мы потратили на это две недели, взяли 800 тысяч рублей и реализовали эту кнопку. Мы счастливые, довольные.
И тут приходит письмо, что вышло обновление, и поставщик тоже решил добавить на эту форму реквизит.
Скачиваем обновление и пытаемся обновить:
-
Если мы расчехляем конфигуратор и обновляем через сравнение/объединение, то конфигуратор вообще не поймет, что произошло – он просто один реквизит поменяет на другой. Она решит, что кнопка заменилась на поле ввода, потому что идентификаторы пересекаются. Иногда это можно отловить сразу, но иногда вы этого не заметите. Конфигуратор ничего не показывает – у него все хорошо, все сравнилось, объединилось. В режиме 1С:Предприятие на форме даже кнопка останется, но нажать на нее вы уже не сможете. Либо приложение просто при открытии формы упадет. Это настолько непредсказуемая история, что лучше до этого не доводить.
-
Мы у себя ведем разработку в Git, и я объединяю изменения через мой любимый мерж-тул Kdiff3 – это такое программное обеспечение для Git, которое помогает мержить. Он мне сразу подсвечивает, что есть изменения, с которыми нужно разобраться. Но для форм его использовать неудобно: если изменений мало, я еще могу посидеть немного и руками все это смержить, а когда изменений много, нет другого решения, кроме как открыть рядом две конфигурации и начать вручную все это перетаскивать.
Решение – программная манипуляция с элементами форм
Как избежать таких проблем? Ответ на поверхности – нужно использовать исключительно программную доработку форм.
Я к этому снаряду подходил уже несколько раз. Сначала у меня был общий модуль с методами: ДобавитьКнопку, ДобавитьПолеВвода и т.д., которые получали на вход какие-то параметры.
Но поскольку в платформе очень много различных вариантов элементов форм одного типа, количество параметров постоянно увеличивалось. Пользоваться этим становилось неудобно.
В какой-то момент я уже устал добавлять параметры – у меня просто экран закончился, и я все переделал на один параметр ДопПараметры. Это была структура, которая принимала Ключ и Значение – там можно было добавлять параметры до бесконечности.
Обновлять программно доработанные формы удобно – мержить изменения можно автоматически. Принцип такой доработки применим и для расширений, и для внесения изменений непосредственно в конфигурацию.
Но гораздо чаще, чем мы пишем код, мы его читаем. Эту портянку с кучей методов надо помнить и понимать, какие ключи в эту структуру положить.
В какой-то момент этим пользоваться надоедает, и ты думаешь: «Ладно, я сейчас быстро накидаю то, что нужно, на форму. А кто-то другой потом пострадает с обновлением. Может, повезет, и это буду не я».
Паттерн builder для удобной доработки форм
После очередной попытки что-то добавить в этот модуль, я решил почитать всевозможные древние книжки. И на одной из древних скрижалей, где описаны паттерны проектирования программного обеспечения, я прочитал о паттерне builder – построитель или строитель. Он называется по-разному, в зависимости от вашей среды обитания и прочтенной литературы.
Согласно этому паттерну у нас есть объект:
-
У этого объекта есть какое-то свое состояние, которое можно менять.
-
И есть определенные методы.
-
Одни из этих методов меняют состояние объекта и возвращают этот же самый объект.
-
А другие методы результируют накопленную в объекте информацию и возвращают какое-то значение.
-
На слайде показан пример реализации такого паттерна в 1С. Здесь объектом является обработка, содержащая одну переменную и три функции, которые что-то с переменной делают – в данном случае, функции «Плюс» и «Минус» возвращают саму обработку (значение переменной ЭтотОбъект).
И получается, что вы можете создать обработку, установить значение, потом через цепочку вызовов сделать, что вам надо, и получить результат.
Здесь нет никакой магии, никаких хаков платформы – все это общепринятые средства, так можно делать, это не запрещено.
Перейдем непосредственно к библиотеке. Это расширение, в котором есть:
-
один общий модуль, который обеспечивает инфраструктуру по созданию и инициализации объектов;
-
сердце библиотеки – это обработка «ПостроительФорм», которая реализует паттерн builder и позволяет создавать много разных красивых элементов;
-
и две вспомогательные обработки для работы с таблицами и динамическими списками.
Обратите внимание, что здесь мы отошли от длинных методов, которые принимают кучу параметров, и все достаточно хорошо читается.
В обработке «ПостроительФорм» методы условно поделены на несколько типов.
-
Методы, которые создают реквизиты формы – какие-то бизнес-сущности наподобие Контрагент, Договор и т.д.
-
Методы, которые создают команды формы – на эти команды ссылаются кнопочки.
-
Методы, которые непосредственно создают элементы.
-
И методы, которые управляют свойствами этих элементов.
В результате мы ушли от длинных портянок методов с кучей параметров, которые надо помнить.
Нам не важен порядок создания элементов и установки для них свойств.
-
Например, мы можем создать поле ввода, указать для него путь к данным, родителя, видимость – что угодно. И если мы поменяем порядок этих вызовов, ничего не сломается, все будет прекрасно работать.
-
Обратите внимание, здесь создается реквизит ДинамическаяДата, а потом я могу переместить его за «Оплачен», хотя реквизит «Оплачен» еще не создан, он только здесь создается. Здесь тоже ничего не сломается, тоже будет работать. Библиотека поймет, что у вас немного нарушен порядок, подправит это за вас и даже не упадет.
-
Здесь мы в самом начале создаем группу формы «НомерИДата», и потом туда что-то складываем. Но можно сделать и наоборот – сначала создать несколько полей ввода, а потом только где-то внизу определить группу. Тоже ничего не сломается, все будет работать.
Компонентный подход
Небольшой пример из жизни – на слайде наше рабочее место дежурного.
Здесь много табличек, вкладок, кнопочек и всего прочего. Все построено кодом на билдере.
Когда мы только начали этим заниматься, мы поняли, что здесь хорошо заходит компонентный подход.
Например, если нам нужно вывести динамический список со своей логикой и составом командной панели, мы не пишем его код в этой форме, а он у нас хранится отдельно – в общем модуле или в другой обработке. В этом случае мы его можем из разных мест дергать, и он будет везде одинаково меняться. Если мы потом решим на него еще какую-то кнопку добавить – отбор или еще что-то такое – в одном месте добавим, и везде сразу появилось. Прикольно.
Это можно использовать для документов «Реализация», «Заказ», «Счет» и так далее. Табличная часть у них, как правило, одинаковая: товар, характеристика и т.д. Можно все описать в одном месте, вставить кнопку подбора, и это везде будет сразу появляться. Классно.
Под капотом у этой обработки много процедур и функций. Здесь на скриншотах не все, потому что когда я их сюда вставлял, на слайде закончилось место. Я понял, что вы все равно читать не будете, но их там реально много.
Признаюсь, я их сам руками не писал – мы использовали немного магии, распарсили ту часть синтакс-помощника, которая отвечает за работу с элементами формы, и сгенерировали модуль.
Шаблоны для автодополнения
Это конечно, все прикольно, но как писать код программного добавления элементов? Когда вы в конфигураторе делаете эту цепочку вызовов, он вам ничего не подскажет, он не умеет подсказывать так глубоко.(Кстати, крайние версии EDT уже могут в таких случаях давать красивую подсказку)
Чтобы решить эту проблему, мы опять вернулись к кодогенерации, только уже генерировали не код, а файлы шаблона – загрузили в генератор все процедуры из модуля нашей обработки, и сгенерировали файл шаблона, который помогает писать эту цепочку вызовов.
Обратите внимание, здесь у каждого метода есть литера.
-
Р – это создание реквизитов;
-
Э – это создание элементов.
-
С – это управление свойствами.
-
У – управляющие, их не так много, это как раз «Применить», «ТекущийЭлемент» (переключиться на элемент) и так далее.
Упрощение добавления сложных элементов и расширение возможностей
С этим классно работать, но это нужно как-то расширять. Мы попытались сделать так, чтобы это было максимально просто расширять, не залезая в само сердце.
Я вам уже говорил, что у нас есть основная обработка и несколько дополнительных. На самом деле я вас обманул – дополнительных обработок у нас очень много. Но я их вам не покажу, т.к. это просто наша внутренняя бизнес-логика. В качестве примера мы оставили в библиотеке только две дополнительные обработки – «Таблицы» и «Динамические списки». Без них при добавлении таблиц получается много кода, который неудобно читать – а так мы сделали для себя упрощение.
Суть в том, что вы можете завести свою обработку, которая будет начинаться с префикса рсф_ (сокращение от «Работа с формами») и реализовать у нее минимально простой интерфейс из шести строчек.
Тогда при инициализации построителя форм ваша обработка подтянется в доступные компоненты, и ее можно будет вызывать по аналогии с тем, как здесь вызван компонент «Таблицы». И все функции с бизнес-логикой, которые вы там пропишите, будут вам доступны. Но, поскольку здесь все в текучем виде, я советую описывать логику в добавляемой обработке тоже по этому паттерну.
Тем самым, не влезая в недры построителя, вы можете сделать для себя что-то полезное либо как-то сократить то, что вы у себя часто делаете.
Лекарство от боли
Давайте вернемся к боли, которую мы уже испытали, и попробуем от нее избавиться.
Помните тот ужасный XML, который был? А теперь посмотрим, как бы выглядело обновление, если бы мы использовали программную доработку формы с помощью этой библиотеки:
-
слева – доработки одного разработчика, за которые мы взяли 800 тысяч;
-
а справа – труды другого разработчика, который тоже нам чем-то помог.
В этом случае, релиз-инженер, который занимается этим обновлением, увидит в диалоге сравнения то, что легко прочитать. И, исходя из сравниваемого кода, он поймет, что здесь реквизит добавлен, а не изменен. Самое большое и сложное решение, которое ему здесь нужно будет принять – это решить, какой элемент после какого на форму выводить. И все, задача по обновлению решена.
Альтернативы и аналоги
Мы не одни во вселенной. К программному изменению форм подходили многие талантливые люди. Некоторые их решения я нашел на Инфостарте и привел здесь в качестве примера:
-
//infostart.ru/1c/articles/1284403/ Общий модуль, для динамического формирования интерфейса. Дмитрий Котов.
-
//infostart.ru/public/304736/ Генерация кода управляемой формы. Евгения Карук.
-
//infostart.ru/public/1343810/ Серия статей о программной доработке форм. Федор Тимохов.
И многие другие.
Не могу однозначно сказать, что мое решение лучше, чем любое другое из этого списка. У всех нас одна цель, просто путь разный, и я сейчас рассказываю про свой путь.
Ограничения программного управления формой
Ложка дегтя в желтой бочке.
К сожалению, в платформе 1С не все так радужно, как хотелось бы. Она, конечно, развивается, но в ней присутствуют ограничения на программные действия с формами:
-
Например, если вы хотите добавить кнопку, которая ссылается на общую команду, программно это сделать нельзя. Писали в 1С, просили добавить, они сказали, что зарегистрировали наше обращение.
-
Кроме этого, есть ограничение на манипуляции с командными панелями, которые генерятся для таблиц автоматически. С ними тяжело и местами даже невозможно работать программно. Проще эту автоматически сгенерированную командную панель скрыть, а потом добавить свою и разместить на ней нужные кнопки.
-
Еще одно ограничение – в том, что методы для любых обработчиков событий (например, у кнопки есть событие «ПриНажатии», у таблицы есть «ПриДобавлении», у поля ввода – «ПриИзменении» и т.д.) должны быть объявлены исключительно в модуле формы. Согласитесь, было бы здорово, если бы этот код можно было хранить не в модуле формы, а где-то в общем модуле, чтобы, поменяв поведение в одном месте, у нас везде сразу стало хорошо. Такого нет и вряд ли, наверное, будет.
-
И есть еще множество других подобных нюансов, в которые я уже не буду углубляться, иначе получится, как будто я в докладе свой подход ругаю. Нет, не ругаю, работать можно.
Задел на дальнейшее развитие и применимость в разработке
Когда мы на все это смотрим, приходит понимание, что чего-то не хватает.
-
Например, не хватает автоматической генерации кода на основе существующей формы – того, что было реализовано в других инструментах для программной доработки. У нас есть желание реализовать такой конструктор либо генератор, но я пока не нашел для себя золотой середины, где должен заканчиваться конструктор и начинаться программист. Потому что если я буду пользоваться только конструктором, мне при каждой доработке интерфейса нужно будет обратно грузить это в конструктор, чтобы понимать, каким будет результат – здесь нужно найти золотую середину.
-
Мы планируем дальше улучшать шаблоны, потому что в них, возможно, нет каких-то методов. Например, вы все знаете, что в платформе нам постоянно добавляют новые классные диаграммы. Если вы их вдруг захотите использовать в своей форме, возможно, в моем построителе методов под это не будет.
-
И если мы наталкиваемся на что-то, чего в нашем построителе нет, мы это добавляем.
И напоследок:
-
Пользоваться программной доработкой формы удобно, надежно и не так сложно, как кажется. Нужно просто начать, и все будет замечательно.
-
Если у вас развита культура тестирования, будь то unit-тесты или feature-тесты, с этим можно классно подружиться – в результате вы получите синергию, вам это тоже понравится.
-
Подход программной доработки можно использовать даже в расширениях – туда это тоже хорошо ложится. Мы просто затягиваем нужную форму в расширение и добавляем одну строчку в метод ПриСозданииНаСервере. В этом случае обновление формы от поставщика, для вас, по сути, будет бесплатным – он добавляет свои изменения в XML-файл формы, а изменения формы потом генерируются сверху кодом из нашей библиотеки.
-
Оно может сломаться только в том случае, если вы докидываете свои элементы в какую-то типовую группу, а поставщик эту группу удалил – но тогда у вас даже в дымовых тестах будет сразу видно, что форма не открывается, падает.
-
И есть один незначительный минус – когда платформа открывает встроенные в конфигурацию формы в первый раз, они немного подтормаживают, а на второй и третий раз эти XML где-то кешируются и форма открывается быстрее. Но в случае программного управления элементами формы ничего не кешируется, потому что мы каждый раз тратим время на создание самого объекта обработки и на прорисовку. Эти задержки незначительны – когда мы замеряли, разница во времени составляла не более 5%. Цена за последующее снижение боли этого не перевешивает, так что можно пользоваться.
По ссылке https://github.com/Nivanchenko/formhelper вы можете скачать библиотеку для программного управления элементами формы, о которой я говорил, и файл с шаблонами кода.
*************
Статья написана по итогам доклада (видео), прочитанного на конференции Infostart Event.