Skip to content

extends

ТестированиеСтатус порога качества

extends - это библиотека для реализации наследования через композицию и делегирование в OneScript.

Установка

Для установки библиотеки используйте команду:

sh
opm install extends

Описание

Язык 1С:Предприятие как и OneScript не поддерживает концепцию наследования классов. Однако же нам доступны принципы композиции и делегирования, а так же механизм кодогенерации и загрузки сценария из строки.

Имея на руках два объекта, один из которых выступает в роли родителя, а другой в роли наследника, мы можем сгенерировать новый объект, который будет обладать всеми методами родителя и наследника.

Библиотека extends активно использует возможности, предоставляемые библиотеками reflector и decorator.

Лицензия

Этот проект лицензируется под лицензией MIT. Подробности см. в файле LICENSE.md.


Наследование

Для создания наследника от родителя используется класс ПостроительНаследника. Для создания наследника необходимо создать экземпляр класса ПостроительНаследника, передав ему объекты родителя и наследника; и вызвать метод Построить для получения объекта с общим интерфейсом.

Предположим, у нас есть класс Родитель со следующим описанием:

bsl
Функция ВернутьЗначение(Значение) Экспорт
    Возврат Значение;
КонецФункции

Для объявления, что класс наследника должен унаследовать поведение класса Родитель, используется аннотация &Расширяет:

bsl
Функция ФункцияНаследника() Экспорт
    Возврат 0;
КонецФункции

&Расширяет("Родитель")
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Для построения объекта, наследующего поведение класса Родитель, используется класс ПостроительНаследника:

bsl
#Использовать extends

Наследник = Новый Наследник;
Родитель = Новый Родитель;

Построитель = Новый ПостроительНаследника(Наследник, Родитель);
ОбъектНаследник = Построитель.Построить();

Сообщить(ОбъектНаследник.ФункцияНаследника()); // 0
Сообщить(ОбъектНаследник.ВернутьЗначение(42)); // 42

Полученный объект ОбъектНаследник будет обладать методами ФункцияНаследника из класса Наследник и ВернутьЗначение из класса Родитель.

Переопределение методов

При необходимости класс-наследник может переопределить методы родителя.

bsl
Функция ВернутьЗначение(Значение) Экспорт
    Возврат 7;
КонецФункции

&Расширяет("Родитель")
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Созданный ОбъектНаследник будет возвращать 7 вместо переданного значения.

Доступ к родителю

При генерации объекта-наследника в него добавляется поле _ОбъектРодитель, в котором хранится ссылка на объект-родитель, который был передан в ПостроительНаследника. Чтобы не запоминать волшебное название поля, в классе-наследнике можно объявить поле с аннотацией &Родитель.

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

bsl
&Родитель
Поле СсылкаНаРодителя;

Функция ВернутьЗначение(Значение) Экспорт
    Возврат 7 + СсылкаНаРодителя.ВернутьЗначение(Значение);
КонецФункции

&Расширяет("Родитель")
Процедура ПриСозданииОбъекта()
КонецПроцедуры

При вызове метода ВернутьЗначение объекта-наследника будет вызван метод родителя, который вернет переданное значение, и к нему будет прибавлено 7.


Работа с интерфейсами

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

Для семантического обозначения интерфейсов рекомендуется использовать аннотацию &Интерфейс на конструкторе класса-интерфейса.

bsl
&Интерфейс
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Процедура МойМетод() Экспорт
КонецПроцедуры
bsl
Процедура МойМетод()
    Сообщить("Реализация метода");
КонецПроцедуры

&Реализует("МойИнтерфейс")
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Проверка реализации интерфейса осуществляется с помощью класса ВалидаторРеализации. В валидатор можно передавать как сам объект, который необходимо проверить, так и его тип.

bsl
#Использовать extends

ВалидаторРеализации = Новый ВалидаторРеализации();
ВалидаторРеализации.ИнтерфейсыРеализованыКорректно(Тип("МойКласс"));

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

Класс может объявлять, что он реализует несколько интерфейсов, повторяя аннотацию &Реализует для каждого интерфейса.

bsl
&Реализует("МойИнтерфейс")
&Реализует("МойДругойИнтерфейс")
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Расширение интерфейсов

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

bsl
&Интерфейс
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Процедура БазовыйМетод() Экспорт
КонецПроцедуры
bsl
&Интерфейс
&Расширяет("БазовыйИнтерфейс")
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Процедура ДополнительныйМетод() Экспорт
КонецПроцедуры
bsl
Процедура БазовыйМетод()
    Сообщить("Реализация базового метода");
КонецПроцедуры

Процедура ДополнительныйМетод()
    Сообщить("Реализация дополнительного метода");
КонецПроцедуры

&Реализует("РасширенныйИнтерфейс")
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Класс МойКласс объявляет, что он реализует интерфейс РасширенныйИнтерфейс и должен содержать как метод ДополнительныйМетод, так и унаследованный метод БазовыйМетод из БазовыйИнтерфейс.


Комбинирование наследования и интерфейсов

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

bsl
&Интерфейс
Процедура ПриСозданииОбъекта()
КонецПроцедуры

Функция ВернутьЗначение(Параметр) Экспорт
КонецФункции
bsl
Перем КакоеТоПоле Экспорт;

// Класс объявляет, что он реализует интерфейс,
// однако его текст не содержит этой реализации.
&Реализует("МойИнтерфейс")
Процедура ПриСозданииОбъекта()
    КакоеТоПоле = 42;
КонецПроцедуры
bsl
&Родитель
Перем СсылкаНаРодителя;

Функция ВернутьЗначение(Параметр) Экспорт
    Возврат Параметр + СсылкаНаРодителя.КакоеТоПоле;
КонецФункции

&Расширяет("АбстрактныйМойКласс")
Процедура ПриСозданииОбъекта()
КонецПроцедуры
bsl
#Использовать extends

Наследник = Новый Наследник;
Родитель = Новый АбстрактныйМойКласс;

Построитель = Новый ПостроительНаследника(Наследник, Родитель);
ОбъектНаследник = Построитель.Построить();

// Исключение об отсутствующей реализации интерфейса, требуемой классом-родителем,
// не будет вызвано, так как реализация интерфейса находится в наследнике.
Сообщить(ОбъектНаследник.ВернутьЗначение(7)); // 49