extends
extends
- это библиотека для реализации наследования через композицию и делегирование в OneScript.
Установка
Для установки библиотеки используйте команду:
opm install extends
Описание
Язык 1С:Предприятие как и OneScript не поддерживает концепцию наследования классов. Однако же нам доступны принципы композиции и делегирования, а так же механизм кодогенерации и загрузки сценария из строки.
Имея на руках два объекта, один из которых выступает в роли родителя, а другой в роли наследника, мы можем сгенерировать новый объект, который будет обладать всеми методами родителя и наследника.
Библиотека extends
активно использует возможности, предоставляемые библиотеками reflector
и decorator
.
Лицензия
Этот проект лицензируется под лицензией MIT. Подробности см. в файле LICENSE.md.
Наследование
Для создания наследника от родителя используется класс ПостроительНаследника
. Для создания наследника необходимо создать экземпляр класса ПостроительНаследника
, передав ему объекты родителя и наследника; и вызвать метод Построить
для получения объекта с общим интерфейсом.
Предположим, у нас есть класс Родитель
со следующим описанием:
Функция ВернутьЗначение(Значение) Экспорт
Возврат Значение;
КонецФункции
Для объявления, что класс наследника должен унаследовать поведение класса Родитель
, используется аннотация &Расширяет
:
Функция ФункцияНаследника() Экспорт
Возврат 0;
КонецФункции
&Расширяет("Родитель")
Процедура ПриСозданииОбъекта()
КонецПроцедуры
Для построения объекта, наследующего поведение класса Родитель
, используется класс ПостроительНаследника
:
#Использовать extends
Наследник = Новый Наследник;
Родитель = Новый Родитель;
Построитель = Новый ПостроительНаследника(Наследник, Родитель);
ОбъектНаследник = Построитель.Построить();
Сообщить(ОбъектНаследник.ФункцияНаследника()); // 0
Сообщить(ОбъектНаследник.ВернутьЗначение(42)); // 42
Полученный объект ОбъектНаследник
будет обладать методами ФункцияНаследника
из класса Наследник
и ВернутьЗначение
из класса Родитель
.
Переопределение методов
При необходимости класс-наследник может переопределить методы родителя.
Функция ВернутьЗначение(Значение) Экспорт
Возврат 7;
КонецФункции
&Расширяет("Родитель")
Процедура ПриСозданииОбъекта()
КонецПроцедуры
Созданный ОбъектНаследник будет возвращать 7 вместо переданного значения.
Доступ к родителю
При генерации объекта-наследника в него добавляется поле _ОбъектРодитель
, в котором хранится ссылка на объект-родитель, который был передан в ПостроительНаследника
. Чтобы не запоминать волшебное название поля, в классе-наследнике можно объявить поле с аннотацией &Родитель
.
Ссылку можно использовать для вызова методов родителя, в том числе тех, которые переопределяются в классе-наследнике.
&Родитель
Поле СсылкаНаРодителя;
Функция ВернутьЗначение(Значение) Экспорт
Возврат 7 + СсылкаНаРодителя.ВернутьЗначение(Значение);
КонецФункции
&Расширяет("Родитель")
Процедура ПриСозданииОбъекта()
КонецПроцедуры
При вызове метода ВернутьЗначение
объекта-наследника будет вызван метод родителя, который вернет переданное значение, и к нему будет прибавлено 7.
Работа с интерфейсами
Библиотека предоставляет аннотацию &Реализует
, которая позволяет указать, что класс должен реализовать указанный интерфейс. Интерфейс должен быть представлен классом, имя которого и указывается в аннотации.
Для семантического обозначения интерфейсов рекомендуется использовать аннотацию &Интерфейс
на конструкторе класса-интерфейса.
&Интерфейс
Процедура ПриСозданииОбъекта()
КонецПроцедуры
Процедура МойМетод() Экспорт
КонецПроцедуры
Процедура МойМетод()
Сообщить("Реализация метода");
КонецПроцедуры
&Реализует("МойИнтерфейс")
Процедура ПриСозданииОбъекта()
КонецПроцедуры
Проверка реализации интерфейса осуществляется с помощью класса ВалидаторРеализации
. В валидатор можно передавать как сам объект, который необходимо проверить, так и его тип.
#Использовать extends
ВалидаторРеализации = Новый ВалидаторРеализации();
ВалидаторРеализации.ИнтерфейсыРеализованыКорректно(Тип("МойКласс"));
В случае, если класс не реализует все методы интерфейса, будет выброшено исключение. Под реализацией подразумевается наличие всех методов интерфейса с совпадающим количеством параметров метода.
Класс может объявлять, что он реализует несколько интерфейсов, повторяя аннотацию &Реализует
для каждого интерфейса.
&Реализует("МойИнтерфейс")
&Реализует("МойДругойИнтерфейс")
Процедура ПриСозданииОбъекта()
КонецПроцедуры
Расширение интерфейсов
Интерфейсы могут расширять другие интерфейсы через аннотацию &Расширяет
, что обеспечивает наследование интерфейсов. Когда класс реализует интерфейс, он должен также реализовать все методы интерфейсов, которые этот интерфейс расширяет.
&Интерфейс
Процедура ПриСозданииОбъекта()
КонецПроцедуры
Процедура БазовыйМетод() Экспорт
КонецПроцедуры
&Интерфейс
&Расширяет("БазовыйИнтерфейс")
Процедура ПриСозданииОбъекта()
КонецПроцедуры
Процедура ДополнительныйМетод() Экспорт
КонецПроцедуры
Процедура БазовыйМетод()
Сообщить("Реализация базового метода");
КонецПроцедуры
Процедура ДополнительныйМетод()
Сообщить("Реализация дополнительного метода");
КонецПроцедуры
&Реализует("РасширенныйИнтерфейс")
Процедура ПриСозданииОбъекта()
КонецПроцедуры
Класс МойКласс
объявляет, что он реализует интерфейс РасширенныйИнтерфейс
и должен содержать как метод ДополнительныйМетод
, так и унаследованный метод БазовыйМетод
из БазовыйИнтерфейс
.
Комбинирование наследования и интерфейсов
Класс может одновременно наследовать поведение другого класса и реализовывать интерфейсы. Поддерживаются и более сложные случаи, когда класс-родитель объявляет, что он реализует некоторый интерфейс, однако реализация этого интерфейса должна происходить в наследнике. Таким образом можно эмулировать понятие абстрактного класса.
&Интерфейс
Процедура ПриСозданииОбъекта()
КонецПроцедуры
Функция ВернутьЗначение(Параметр) Экспорт
КонецФункции
Перем КакоеТоПоле Экспорт;
// Класс объявляет, что он реализует интерфейс,
// однако его текст не содержит этой реализации.
&Реализует("МойИнтерфейс")
Процедура ПриСозданииОбъекта()
КакоеТоПоле = 42;
КонецПроцедуры
&Родитель
Перем СсылкаНаРодителя;
Функция ВернутьЗначение(Параметр) Экспорт
Возврат Параметр + СсылкаНаРодителя.КакоеТоПоле;
КонецФункции
&Расширяет("АбстрактныйМойКласс")
Процедура ПриСозданииОбъекта()
КонецПроцедуры
#Использовать extends
Наследник = Новый Наследник;
Родитель = Новый АбстрактныйМойКласс;
Построитель = Новый ПостроительНаследника(Наследник, Родитель);
ОбъектНаследник = Построитель.Построить();
// Исключение об отсутствующей реализации интерфейса, требуемой классом-родителем,
// не будет вызвано, так как реализация интерфейса находится в наследнике.
Сообщить(ОбъектНаследник.ВернутьЗначение(7)); // 49