gifts2017

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

Опубликовал rtnm rtnm (rtnm) в раздел Программирование - Практика программирования

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Заключение

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

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

 

 

См. также

Подписаться Добавить вознаграждение

Комментарии

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

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

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

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

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

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

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


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

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

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

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

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


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

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


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

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

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