Прокси-функции

20.11.13

Разработка - Универсальные функции

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

Давайте рассмотрим такую ситуацию.

У нас две базы: одна основана на конфигурации "Управление торговлей" (далее по тексту УТ), а другая на конфигурации "Розница" (далее по тексту РТ). Между базами настроен типовой онлайн обмен. Обе конфигурации сняты с поддержки и мы вольны дорабатывать их как пожелаем. В базе "Розница" учет себестоимости не ведется.

Вдруг возникает необходимость определения себестоимости номенклатуры в базе РТ. Сразу скажу, что первая мысль которая приходит: "Наверное, в нашей архитектуре что-то не так!". Но сейчас мы не будем это обсуждать.

Вторая мысль, которая приходит: "Подключусь к базе УТ из базы РТ используя ComConnector и получу любые данные, которые потребуются". Если воплотить эту мысль в функцию ПолучитьСебестоимостьНоменклатуры(Номенклатура), реализованую в конфигурации РТ, то мы совершим 2 ошибки:

  • Смешаем технические особенности написания кода при использовании ComConnector-а и требуемую логику работы
  • Разместим логику работы не в подходящем месте - вдали от нужных данных

Как сделать лучше?

А давайте функцию по получению себестоимости реализуем в конфигурации УТ, в области видимости для внешних соединений (пусть это будет модуль внешнего соединения).

Процедура ПолучитьСебестоимостьНоменклатуры(Номенклатура) Экспорт
     //Понятная всем реализация алгоритма получения себестоимости номенклатуры
     Возврат Себестоимость;
КонецПроцедуры

В свою очередь, в конфигурации РТ добавим общий модуль "МодульПроксиУТ", и все описанные ниже функции разместим в нем. Слово "Прокси" в названии общего модуля - это напоминание того как на самом деле все устроено.

Функция ПолучитьСебестоимостьНоменклатуры(Номенклатура, СоединениеУТ = Неопределено) Экспорт //прокси-функция
    //Соединямеся с УТ, если нам не передали соединение
    Если СоединениеУТ = Неопределено Тогда
        СоединениеУТ = ПолучитьСоединениеУТ();
    КонецЕсли;
    //Переводим входящие параметры в объекты понятные УТ
    НоменклатураУТ = ПолучитьСсылкуУТ(СоединениеУТ, Номенклатура);
    //Вызываем функцию, содержащую основную логику
    Себестоимость = СоединениеУТ.ПолучитьСебестоимостьНоменклатуры(НоменклатураУТ);
    //Обрабатываем результат и переводим в объекты понятные РТ, в данном примере этого не требуется
    Возврат Себестоимость;
КонецФункции

Процедура ПолучитьСоединениеУТ() Экспорт
     //Определяем данные для подключения к базе УТ и создаем соединение
     Соединитель = Новый COMObject("V82.COMConnector");
     Соединение = Соединитель.Connect(СтрокаПодключения);
     Возврат Соединение;
КонецПроцедуры

Функция ПолучитьУзелОбменаУТ()   
    //Можно придумать более изящный способ
    Возврат ПланыОбмена.ОбменУправлениеТорговлейРозничнаяТорговля.НайтиПоКоду("001");    
КонецФункции

Функция ПолучитьСсылкуУТ(СоединениеУТ, СсылкаРТ)
    Запрос = Новый Запрос();
    Запрос.Текст = "ВЫБРАТЬ
                   |    СоответствиеОбъектовДляОбмена.СобственнаяСсылка КАК СсылкаРТ,
                   |    СоответствиеОбъектовДляОбмена.СсылкаВДругойИБ КАК СтроковаяСсылкаУТ
                   |ИЗ
                   |    РегистрСведений.СоответствиеОбъектовДляОбмена КАК СоответствиеОбъектовДляОбмена
                   |ГДЕ
                   |    СоответствиеОбъектовДляОбмена.УзелОбмена = &УзелОбменаУТ
                   |    И СоответствиеОбъектовДляОбмена.СобственнаяСсылка = &СсылкаРТ";
    Запрос.УстановитьПараметр("СсылкаРТ", СсылкаРТ);
    Запрос.УстановитьПараметр("УзелОбменаУТ", ПолучитьУзелОбменаУТ());
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат СоединениеУТ.ЗначениеИзСтрокиВнутр(Выборка.СтроковаяСсылкаУТ);
    Иначе
        ВызватьИсключение "Понятный текст исключения";
    КонецЕсли;   
КонецФункции

Как использовать?

Получение себестоимости номенклатуры в РТ станет красивым и понятным (про производительность и архитектурность я молчу).

В случае если нам нужно получить себестоимость по одной номенклатуре, то достаточно:

Себестоимость = МодульПроксиУТ.ПолучитьСебестоимостьНоменклатуры(Номенклатура);

В случае если нам нужно получить себестоимость сразу по нескольким номенклатурам, то прозрачность кода несколько теряется:

СоединениеУТ = МодульПроксиУТ.ПолучитьСоединениеУТ();
Себестоимость1 = МодульПроксиУТ.ПолучитьСебестоимостьНоменклатуры(Номенклатура1, СоединениеУТ);
Себестоимость2 = МодульПроксиУТ.ПолучитьСебестоимостьНоменклатуры(Номенклатура2, СоединениеУТ);

Особенности прокси-функции

  • Количество параметров в прокси-функции на 1 больше чем в реальной - последним параметром передается необязательный параметр соединения
  • Первый логический блок функции содержит код по установлению соединения, в случае, если его не установили ранее
  • Второй логический блок функции содержит код для перевода входящих параметров в понятные для УТ объекты
  • Третий логический блок функции содержит, собственно, вызов реальной функции.
  • Четвертый логический блок функции содержит код для перевода результата в понятные для РТ объекты и возвращает результат.

Особенности реализации примера

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

Чего мы добились?

  • Разделили технические особенности при работе с ComConnector-ом и основную логику работы
  • Разместили код основной логики в правильном месте
  • Ну и, как следствие, получили более понятный и удобный для поддержки код

Заключение

Статья не о том как получить себестоимость номенклатуры в РТ по данным УТ, а о том как, используя идею прокси-функций, писать более "чистый" код. Я считаю, что описанный подход нужно по возможности использовать всегда при интеграции с другими системами, а не только при использовании ComConnector-a.

Спасибо за внимание!

 

 

Прокси COM OLE

См. также

Вставляем картинку из буфера обмена (платформа 1С 8.3.24)

Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    2682    1    John_d    8    

55

GUID в 1С 8.3 - как с ними быть

Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    4614    atdonya    22    

45

Переоткрытие внешних обработок

Универсальные функции Платформа 1С v8.3 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    3965    ke.92@mail.ru    16    

61

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    8847    YA_418728146    6    

141

Печать непроведенных документов для УТ, КА, ERP. Настройка печати по пользователям, документам и печатным формам

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    2078    21    progmaster    7    

3

Расширение: Быстрые отборы через буфер [Alt+C] Копировать список, [Alt+V] Вставить список, [Ctrl+C] Копировать из файлов

Инструментарий разработчика Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Зарплата и Управление Персоналом 3.x Абонемент ($m)

Копирует в буфер значения из списков, из ячеек отчетов, таблиц, настроек списков, других отборов и вставляет в выбранную настройку отбора. Работает с Объект не найден. Работает как в одной так и между разными базами 1С. Использует комбинации [Alt+C] Копировать список, [Alt+V] Вставить список. Также для копирования данных используется стандартная [Ctrl+C] (например из открытого xls, mxl, doc и т.п. файла скопировать список наименований)

1 стартмани

13.10.2022    16156    133    sapervodichka    112    

129

Система контроля ведения учета [БСП]

Универсальные функции Механизмы типовых конфигураций БСП (Библиотека стандартных подсистем) Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

В данном материале рассмотрим типовой алгоритм подсистемы контроля учета БСП в конфигурациях на примерах.

18.07.2022    7243    quazare    8    

109
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. dmpas 418 14.10.13 08:57 Сейчас в теме
    //Соединямеся с УТ, если нам не передали соединение
    Если СоединениеУТ = Неопределено Тогда
        СоединениеУТ = ПолучитьСоединениеУТ();
    КонецЕсли;

Ещё было бы неплохо отсоединиться, если нам не передали соединение и не передали переменную, в которую мы должны вернуть это соединение. Думаю, что передача соединения в качестве необязательного параметра не самый "чистый" код в данном случае.
2. rtnm 614 14.10.13 09:59 Сейчас в теме
(1) baton_pk,

Ещё было бы неплохо отсоединиться, если нам не передали соединение и не передали переменную, в которую мы должны вернуть это соединение.

А разве мы автоматически не отсоединимся как только завершится функция МодульПроксиУТ.ПолучитьСебестоимостьНоменклатуры(Номенклатура)? Как бы вы хотели чтобы было произведено отключение?

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

Я в статье упомянул, что в этом случае теряется прозрачность. Если она вам нужна, то сделайте так:

Функция ПолучитьСебестоимостьДвухНоменклатур(Номенклатура1, Номенклатура2) Экспорт
     Результат = Новый Структура;
     СоединениеУТ = ПолучитьСоединениеУТ();
     Результат.Вставить("Себестоимость1", ПолучитьСебестоимостьНоменклатуры(Номенклатура1, СоединениеУТ);
     Результат.Вставить("Себестоимость2", ПолучитьСебестоимостьНоменклатуры(Номенклатура2, СоединениеУТ);
     Возврат Результат;
КонецФункции
Показать


Конечно же, это будет уже не прокси-функция, но зато прозрачность останется.
3. dmpas 418 14.10.13 10:34 Сейчас в теме
(2)
А разве мы автоматически не отсоединимся как только завершится функция

Перечитал документацию - действительно, это так. Виноват.

Функция ПолучитьСебестоимостьДвухНоменклатур(Номенклатура1, Номенклатура2) Экспорт
     Результат = Новый Структура;
     СоединениеУТ = ПолучитьСоединениеУТ();
     Результат.Вставить(\"Себестоимость1\", ПолучитьСебестоимостьНоменклатуры(Номенклатура1, СоединениеУТ);
     Результат.Вставить(\"Себестоимость2\", ПолучитьСебестоимостьНоменклатуры(Номенклатура2, СоединениеУТ);
     Возврат Результат;
КонецФункции
Показать

Вот это совсем дикость. Я обычно делаю функцию получения для списка номенклатуры, а потом к ней обёртку для частного случая с одной номенклатурой.
4. rtnm 614 14.10.13 11:29 Сейчас в теме
(3) baton_pk,

Вот это совсем дикость. Я обычно делаю функцию получения для списка номенклатуры, а потом к ней обёртку для частного случая с одной номенклатурой.


Мне казалось что код будет воспринят более абстрактно. Я лишь показал как можно поступить если требуется сохранить прозрачность вызова. Перепишу код чтобы к нему было сложнее придраться.

Процедура СделатьЧтоТо(Номенклатура) Экспорт
   СоединениеУТ = ПолучитьСоединениеУТ();
   НоменклатураУТ = ПолучитьСсылкуУТ(СоединениеУТ, Номенклатура);
   СоединениеУТ.СделатьЧтоТо1(НоменклатураУТ);
   СоединениеУТ.СделатьЧТоТо2(НоменклатураУТ);
КонецПроцедуры


В некоторых ситуациях возможно обойтись одним вызовом прокси-функции, в других их потребуется несколько. Как поступать в конкретной ситуации решать вам.
5. dmpas 418 14.10.13 11:49 Сейчас в теме
(4)
В общем, я Вашу мысль понял, принял и одобрил плюсом. Свои мысли, видимо, плохо до Вас доношу, ну да и ладно.
6. rtnm 614 14.10.13 11:59 Сейчас в теме
(5) baton_pk, Спасибо. Видимо, мне свои мысли тоже трудно доносить однозначно :)
7. Franco 82 14.10.13 12:22 Сейчас в теме
Может быть, вместо использования Com-соединения, сделать возврат через веб-сервис
Прикрепленные файлы:
8. Yashazz 4709 14.10.13 12:39 Сейчас в теме
(7) Да уж, если делать, то лучше веб-сервис, нежели ком.
9. rtnm 614 14.10.13 13:08 Сейчас в теме
(7) Franco, Веб-сервис - это хороший пример воплощения в жизнь идеи прокси-функций. Я никогда не буду отвергать возможности использования веб-сервисов SOAP, хотя по духу мне больше близки сервисы REST.

В общем случае использование веб-сервисов SOAP между двумя базами 1С может быть невозможным или будет усложнением. Например, чтобы работал веб-сервис SOAP нужен HTTP веб-сервер и его поддержка. Или например, если конфигурация УТ, которая в примере была снята с поддержки, не будет снята с поддержки, тогда реализовать веб-сервис SOAP будет невозможно, а между тем нужная для вызова функция может быть уже реализована конфигурации. Так же есть мнение, что на серии вызов прокси-функций преимущество в производительности COM перед SOAP будет видно невооруженным глазом.

Конечно же подход с COM практически не маштабируется и не является кроссплатформенным, и может быть без опаски использован в тех случаях ,когда выполнение таких операций скорее исключение, чем правило.
Оставьте свое сообщение