Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Интерфейсы в 1С #15

Open
DoublesunRUS opened this issue Mar 11, 2021 · 11 comments
Open

Интерфейсы в 1С #15

DoublesunRUS opened this issue Mar 11, 2021 · 11 comments
Labels
EDT plugin wanted Нужен такой плагин для 1C:EDT

Comments

@DoublesunRUS
Copy link

DoublesunRUS commented Mar 11, 2021

БСП содержит в себе механизмы, которые по сути требуют от подключенного объекта реализацию некоторого интерфейса. Например https://its.1c.ru/db/bsp314doc#content:4:1:issogl2_%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B037

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

Пробовал на примере подсистемы Подключаемые команды сделать такие проверки:
https://github.com/DoublesunRUS/ru.capralow.dt.ssl.checks

В процессе реализации понял:

  1. Ручное прописывание проверок очень трудоемкое
  2. Очень много повторяющегося кода

У меня сложилось мнение, что данную задачу можно решить через декларативное описание подобных интерфейсов.
Так как БСП не является открытой библиотекой, то в качестве декларативного описания нужно использовать внешний файл. Например json или yaml.

См. основной концепт

@marmyshev
Copy link
Owner

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

Да, в БСП механизмы этим особенно часто пользуются... Но в ERP например тоже есть таки интерфейсы и реализации.

@marmyshev marmyshev added the EDT plugin wanted Нужен такой плагин для 1C:EDT label Mar 11, 2021
@DoublesunRUS
Copy link
Author

В ЗУП тоже есть такие механизмы. Просто в БСП это явно в документации по внедрению написано, поэтому проще начинать разработку тому, кто не знает специфики ЗУП или ERP.

@RedMammoth
Copy link

А что если описывать не json/yaml, а где-нибудь хранить объекты-шаблоны, их и редактировать было бы проще уже имеющимися редакторами форм, модулей и т.п. , и, как мне кажется, реализовывать проверки на соблюдение будет проще

@RedMammoth
Copy link

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

@marmyshev
Copy link
Owner

marmyshev commented Jan 21, 2022

Еще одна из идей реализации интерфейсов:

  1. Объявляем интерфейс
name: МойВолшебныйИнтерфейс
  baseType: СправочникМенеджер
  methods:
    Метод1:
      returns: Строка
      parameters:
        - Параметр1:
           type: Число 
        - Параметр2:
           type: СправочникСсылка.Товары
        - Параметр3:
          type: Интерфейс.МойВторойИнтерфейс
name: МойВторойИнтерфейс
baseType: УправляемаяФорма
  attributes:
   Атрибут1:
    type: СправочникСсылка.Товары
...
  1. Используем интерфейс в коде:
// Параметры:
// ОбъектИнтерфейса - См. Интерфейс.МойВолшебныйИнтерфейс - тут в док коменте ссылка на интерфейс
// ОбъектИнтерфейса2 - См. Интерфейсы.МойВторойИнтерфейс
Процедура Тест(ОбъектИнтерфейса, ОбъектИнтерфейса2) Экспорт

ОбъектИнтерфейса.Метод1(12, Ссылка, ОбъектИнтерфейса2);

плагин будет генерит по описанию специфичные типы и загружать их в систему ЕДТ.
Расширить ссылочную систему документирующих комментариев - чтобы подгружали эти типы.

Upd: не валидный, см. ниже

@marmyshev
Copy link
Owner

marmyshev commented Jan 22, 2022

Пример того как это может работать

image

Upd: не валидный, см. ниже

@marmyshev
Copy link
Owner

marmyshev commented Jan 23, 2022

1. Декларация интерфейсов

Модель файлов описания интерфейса для модуля src/Interfaces/МойВолшебныйИнтерфейс/ВторичныйИнтерфейс.interface :

name: МойВолшебныйИнтерфейс.ВторичныйИнтерфейс # уникальное имя интерфейса с поддержкой name-space
  # базовый/родительский тип к которому добавляются методы и свойства, 
  # может быть пустым - тогда тип самостоятельный
  parentType: СправочникМенеджер 
  environments: # контроль доступности типа на Клиенте/Сервере
    - SERVER
    - THIN_CLIENT
  methods: # поддерживается добавление методов, если в родительском типе их нет
    Метод1:
      returns: Строка # Возвращаемый тип, означает метод является функцией
      export: true # признак что метод должен быть экспортным
      parameters: # описание параметров метода
        - Параметр1:
           type: Число 
        - Параметр2:
           type: СправочникСсылка.Товары
        - Параметр3:
          type: Интерфейс.МойВторойИнтерфейс
        - Параметр4:
          typeLink: ОбщегоНазначения.ФункцияКонструкторТипа # Любая ссылка на тип или объект, поддерживаемый в док.комментах
      # Здесь возможно указать  начальный шаблон заполнения текста метода при первом создании метода по имплементации
      template: |
        Если ОбменДанными.Загрузка Тогда
            Возврат;
        КонецЕсли;

Интерфейс для описания объекта метаданных

src/Subsystems/СтандартныеПодсистемы/ИнтерфейсаОбъектаМД.interface

name: ИнтерфейсаОбъектаМД # уникальное имя интерфейса с поддержкой name-space
  parentType: СправочникОбъект # базовый/родительский тип к которому добавляются методы и свойства
  methods: # поддерживается добавление методов, если в родительском типе их нет
    ПриЗаписи: # Возможно указывать метод, существующий в типе, описывать сигнатуру метода не нужно
       # Выполняется поиск кода в тексте метода - валидация, что такой код существует в методе
      containsCode: | 
        Свойства.ПриЗаписи(ЭтотОбъект, Отказ);
  attributes: # возможность описания реквизитов объекта
    МойРеквизит:
      type: СправочникСсылка.Товары
  tabularSections: # описание наличия таб.частей и их реквизитов
    Товары:
      attributes:
        Товар:
          type: СправочникСсылка.Товары
        Количество:
          type: Число(15,3)
        Цена:
          type: ОпределяемыйТип.Валютный
        Сумма:
          type: ОпределяемыйТип.Валютный
  commands: # описание команд объекта
    МояКоманда: # имя команды и далее свойства команды...

Интерфейс для описания форм

src/Subsystems/СтандартныеПодсистемы/Subsystems/КонтактнаяИнформация/ФормаКИ.interface

name: КонтактнаяИнформация.ФормаКИ # уникальное имя интерфейса с поддержкой name-space
  parentType: УправляемаяФорма # базовый/родительский тип к которому добавляются методы и свойства
  methods: # поддерживается добавление методов в модуля формы, если в родительском типе их нет
    Метод1:
      returns: Строка # Возвращаемый тип, означает метод является функцией
      export: true # признак что метод должен быть экспортным
      pragma: AtClient # контроль прагмы для метода
      parameters: # описание параметров метода
        - Параметр1:
           type: Число 
        - Параметр2:
           ...
  attributes: # реквизиты формы
   ИмяРеквизитаФормы:
     type: ТаблицаЗначения
  items: # элементы формы, которые должны присутствовать на форме
    ДополнительнаяГруппаКИ
      itemType: FormGroup

2. Имплементация интерфейсов

Для имплементации интерфейса у конкретного объекта можно сделать поддержку 2х режимов:

  1. указание во внешнем файле для любого TOP-объекта (файла)

Например:

  • файл /src/Documents/ПоступлениеТоваров/ПоступлениеТоваров.mdo - документ, для которого указываем интерфейсы
  • файл /src/Documents/ПоступлениеТоваров/ПоступлениеТоваров.implements - файл описания имплементаций объекта
  • Файл /src/Documents/ПоступлениеТоваров/ManagerModule.bsl - модуль менеджера
  • Файл /src/Documents/ПоступлениеТоваров/ManagerModule.implements - файл имплементаций модуля (только для модуля
  • /src/Documents/ПоступлениеТоваров/Form/ФорматДокумента/Form.form - форма объекта
  • /src/Documents/ПоступлениеТоваров/Form/ФорматДокумента/Form.implements - имплементация для формы (может включать модуль)
  • /src/Documents/ПоступлениеТоваров/Form/ФорматДокумента/Module.bsl - модуль формы
  • /src/Documents/ПоступлениеТоваров/Form/ФорматДокумента/Module.implements - имплементации только для модуля фомры
implements:
  - КонтактнаяИнформация.ФормаКИ
  - ИнтерфейсаОбъектаМД
  1. Для модулей - указание в заголовке модуля аннотации @implements где через запятую указывается список интерфейсов модуля
// Заголовок модуля
// Копирайт модуля

// @strict-types

// @implements КонтактнаяИнформация.ФормаКИ, ИнтерфейсаОбъектаМД

Процедура ПриЗаписи()
...

3. Использование интерфейсов в коде

В документирующих комментариях станет возможным указывать типы интерфейсов и по-честному использовать их возможности.

  1. Вариант записи типов интерфейсов:
// @strict-types

// Параметры:
// Фомра - КонтактнаяИнформация.ФормаКИ - тут честный тип обладающий свойствами/методами управляемой формы и дополнительным описанным в интерфейсе
Процедура ПриЗаписиВФорме(Форма) Экспорт
   НоваяСтрока = Форма.ИмяРеквизитаФормы.Добавить(); // Обращение к реквизиту формы
   ...
   Форма.Метод1(10); // Обращение к экспортным методам формы
  1. Вариант записи типов интерфейсов - со специальным префиксом "Интерфейс" чтобы отделить типы от честных типов:
// @strict-types

// Параметры:
// Фомра - Интерфейс.КонтактнаяИнформация.ФормаКИ - тут честный тип, но записывается с префиксом
// ПараметрыЗаписи - Структура
// Отказ - Булево
Процедура ПриЗаписиВФорме(Форма, ПараметрыЗаписи, Отказ) Экспорт
   НоваяСтрока = Форма.ИмяРеквизитаФормы.Добавить(); // Обращение к реквизиту формы
   ...
   Форма.Метод1(10); // Обращение к экспортным методам формы

Необходимо выбрать вариант предоставления типов. Плюсы и минусы?

3.2 Контроль вызова методов с интерфейсами

В строгой типизации мы можем контролировать что параметры, передаваемые в метод - совпадают с имплементациями объекта:

// Заголовок модуля
// Копирайт модуля

// @strict-types

// @implements КонтактнаяИнформация.ФормаКИ, ИнтерфейсаОбъектаМД

&НаСервере
Процедура ПриЗаписи(ПараметрыЗаписи, Отказ)
  КонтактнаяИнформация.ПриЗаписиВФорме(ЭтотОбъект, ПараметрыЗаписи, Отказ);

В этом примере все параметры проверяются на пересечение типов - т.к. "интерфейсы" теперь станут честными типами системы типизации ЕДТ - то так же будут проверяться по имплементации.

4. Доработки UI "интерфейсов"

В навигаторе необходимо добавить:

  1. В раздел Common/Интерфейсы (или м.б. "Программный интерфейс", "Абстрактные типы"?) - в котором будет отображение всех доступных интерфейсов в конфигурации.
  2. С учетом примера размещения интерфейсов в подсистемах или в принципе, где угодно - можно отображать список интерфейсов подчиненные текущему ТОП-объекту
  3. Добавить в навигатор контекстную команду для ТОП-объектов, которая открывает имплементации для текущего объекта "Edit implementations"

5. Доработки ядра "интерфейсов"

  1. Сделать BM-модель для интерфейсов и имплементаций - которую импортировать в BM проекта для быстрого доступа.
  2. Сделать Yaml-reader (deserializer) который загружает в модель при чтении файла
  3. Определить, нужно ли делать сериализацию объекта из БМ (кажется что не нужно, но хз)
  4. Сделать DerivedData сегмент создания Type объекта на основе модели интерфейса через tranasient-поле модели "инетерфейса"
  5. Сделать реализацию IExternalMetaTypesProvider - которая по проекту предоставляет список типов из интерфейсов
  6. Сделать валидатор модели интерфейсов - на всякие логические пересечения (что можно и что нельзя делать с типами, свойствами и прочим в описании самого интерфейса)
  7. Сделать вариацию объектов метаданных/форм и модулей по интерфейсам, если есть имплементация.

@marmyshev
Copy link
Owner

А что если описывать не json/yaml, а где-нибудь хранить объекты-шаблоны, их и редактировать было бы проще уже имеющимися редакторами форм, модулей и т.п. , и, как мне кажется, реализовывать проверки на соблюдение будет проще

@RedMammoth Хорошая идея, надо только продумать, как разделять вспомогательную часть свойств, которые нужны для демонстрации возможностей - от тех что нужно контролировать в реализации.
Такое решение можно делать уже сейчас (без контроля наследования) - но никто не делает так, почему-то. Интересно почему?

И еще - это значит что будут мусорные объекты (формы, справочники, документы и т.д.) которые пойдут в рантам в ИБ. Это минус решения.

@sfaqer
Copy link

sfaqer commented Feb 24, 2022

@marmyshev есть пара комментариев к концепту #15 (comment)

  1. Декларация интерфейсов Модулей

    1. image
      СправочникМенеджер не доступен в тонком клиенте, как следствие видится тут ещё контроль родителя на соответствие окружению
    2. Указание "экспортности" метода видится бессмысленным, интерфейс идеологически описывает внешнее поведение типа на который могут опираться потребители этого интерфейса, соответственно все описанные методы должны быть экспортными, иначе интерфейс "навязывает" детали технической реализации конкретного типа что видится несколько не правильным. Или я не понял "мотивацию" данного флага?
    3. В параметрах я бы дополнил:
      1. Примеры с коллекциями, "Массив из SomeType"
      2. Примеры с несколькими типами: "Массив из SomeType, SomeType" (Вероятно "type" в yml это всё же будет коллекция а не значение)
      3. Примеры со "сложными" типами:
        Массив из Структура:
           * Ключ1 - Тип - Описание
           * Ключ2 - Тип - Описание
        
  2. Декларация интерфейсов метаданных

    1. image
      Мне кажется стоит на уровне с methods добавить поле events, в которым бы можно было описать события наследуемые из родительского типа, как-то ПриЗаписи() в приведённом примере, вероятно с разной семантикой т.е в событиях мы описывает код который должен содержаться в его обработчике, а в методах уже описываем сигнатуру без привязки к деталям реализации
    2. А в чём сакральный смысл описания команд? Ну т.е из кода с ними работать нет возможности, какая внешняя логика у нас может опираться на наличие\отсутствие команды?
  3. Декларация интерфейсов форм

    1. В целом тот же комментарий про экспортность методов
    2. Я думаю тут тоже нужна секция "events" т.к в формах имена методов событий могут отличаться от имени события, ну и я думаю мы так же хотим проверять наличие кода внутри метода обработчика события "ПодключаемыеКоманды.ПриСозданииНаСервере(ЭтотОбъект)" и т.п
  4. Использование интерфейсов в коде

    1. Необходимо выбрать вариант предоставления типов. Плюсы и минусы?

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

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

@marmyshev
Copy link
Owner

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

Ваши рассуждения справедливы, но:

  1. в 1С у нас есть "управляемые формы" с программным добавлением элементов. Поэтому да, интерфейс будет навязывать "детали тех. реализации" в таком случае.
  2. Еще есть стандартные события в менеджерах объектов и в модулях объектов которые не экспортные - тут тоже "навязывание реализации" интерфейсом будет.

Согласен, что этот вопрос нужно тщательно рассмотреть отдельно.

@marmyshev
Copy link
Owner

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

Да, нужно объяснение.

С типом "ссылка" - рассуждения не верные. В 1С объект типа "модуль" не существует в полной мере (есть только тип "ОбщийМодуль") - но де-факто - передача модуля через параметры осуществляется очень-часто.
Достаточно посмотреть в любой менеджер в "типовой" - увидим сколько подсистем БСП туда обращается: Свойства, Подключаемые команды, Отчеты, Даты запрета редактирования, Блокировка редактирования, различные системы проведения и т.д.

Что это означает: что где-то в общем модуле есть код, которому на вход приходят различные "модули" у которых нужно вызывать методы. Коду всё равно какой именно модуль пришел (это общий модуль или модуль менеджера). Для таких случаев нужно описывать общий интерфейс модуля.

Аналогичный пример с общими модулями - взять например подсистему БСП "Обновление ИБ" где необходимо создать общие модули "ОбновлениеИнформационнойБазыХХХ" для каждой подсистемы - в которой должна быть определенная сигнатура методов.

Поэтому подменять "модуль" на "ссылку" - как-то совсем не правильно. Да, при этом по ссылке получать модуль - можно/нужно... но дальше мы передаем модуль куда-то и там с ним можно было бы работать как с интерфейсом.

Отдельно стоит рассмотреть необходимость интерфейсов на "Ссылки" - нужны ли они? На самом деле - они уже есть в метаданных 1С - это "определяемые типы" в которых можно указать ссылки (ну как и другие типы объектов) - да, тут есть условность: что это не "интерфейс как нечто общее", а коллекция полных типов (имплементаций). Но всё же потребность их объединять есть. Наверное этот вопрос нужно детальнее исследовать.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
EDT plugin wanted Нужен такой плагин для 1C:EDT
Projects
None yet
Development

No branches or pull requests

4 participants