]>
![]() |
|
|
ООП - объектно-ориентированное программирование, является на сегодняшний день одной из самых популярных технологий создания программных средств. Данная разработка направлена на устранение недостатка отсутствия ООП в языке 1С. Как известно, ООП базируется на трех основных принципах: полиморфизм, наследование и инкапсуляция; в данной разработке для возможной технической реализации принципов, были добавлены некоторые языковые средства. Также добавлена возможность строгой проверки типов для параметров методов классов, определяемых пользователем (КОП) и некоторые другие классы для расширения парадигмы ООП.
Наследование - это обобщение объектов за счет выведения общего поведения в логически связанных сущностях проекта. Возможность повторно использовать код и отделить интерфейс объекта от его реализации с целью повышения взаимозаменяемости и расширения частей системы без перепрограммирования и дополнительного тестирования множества модулей. Наследование позволяет представить на логическим уровне модель проектирования учетной системы более приближенно к проектируемой области. Наследование необходимо реализовать для достижения следующих целей:
В реализации наследования в ВК (внешней компоненте 1С:Предприятия) 1С++ необходимо определить следующий функционал:
Полиморфизм - заключается в переопределении поведения объекта с помощью специализации обобщенного класса, т.е. переопределение методов интерфейсов базового класса (общий класс) его наследником (более специализированным классом). Полиморфизм позволит:
Инкапсуляция - сокрытие деталей реализации классов за четко определенным интерфейсом. Это позволит разрабатывать компоненты и настраивать их взаимодействие с меньшими зависимостями между ними, что в свою очередь, уменьшит время тестирования и вероятность появления ошибок в алгоритмах программы.
Каждый класс, определенный пользователем (КОП), создается в 1С с помощью метода СоздатьОбъект(ИмяКласса).
Для каждого КОП определен стандартный интерфейс, состоящий из следующих методов:
ПолучитьБазовыйКласс (ИмяБазовогоКласса) / GetBaseClass(strNameOfBaseClass) возвращает объект базового класса для класса предка, имя которого передается в качестве строки в параметр метода "ИмяБазовогоКласса". Если объекта базового класса не существует, то метод возвратит 0. Данный метод предназначен для вызова переопределенных методов базовых классов из методов классов наследников и получения/установки атрибутов базовых классов. Пример: имеются следующие справочники: "Необоротные активы", в котором содержатся общие реквизиты всех необоротных активов предприятия, и справочники - спецификаторы, такие как "Основные средства", "Нематериальные активы", которые, в свою очередь, добавляют специфические реквизиты сущностей к общим реквизитам необоротных активов. Исходя из описанной выше идеологии, мы строим следующую иерархию классов: КОП с рабочим названием "ОС" наследует от классов 1С: справочника "Необоротные активы" и "Основные средства" и переопределяет методы "ВыбратьЭлементы()" и "ПолучитьЭлемент()", т.к у КОП "ОС" базовые классы имеют такие же методы, программист обязан разрешить неоднозначность вызова, воспользовавшись в реализации методов "ВыбратьЭлементы()" и "ПолучитьЭлемент()" класса "ОС" методом ПолучитьБазовыйКласс("Основные средства") для вызова его функций выборки и итераций и ПолучитьБазовыйКласс ("Необоротные активы") для проведения аналогичных операций.
НазначитьБазовыйКласс (ИмяБазовогоКласса) / AssignBaseClass(NameOfBaseClass) данный метод позволяет для созданного КОПа назначить динамически базовый класс в модуле, где он используется. Что позволяет строить иерархию базовых классов, исходя из логики программы, "на лету". Методом возможно создать иерархию глубиной всего в один уровень.
ОтправитьСообщениеМодулюХоз (КтоОтправил, ВидСообщения, Данные) / SendMessageOwnMod(WhoSend, KindMessage, Data). Вызывает предопределенную функцию ОбработкаСобытияОтКласса(отКого, стрСобытие, Данные) реализованную в модуле вызывающем работающий в данный момент метод КОП, возвращает любое значение, которое будет получено после вызова ОтправитьСообщениеМодулюХоз() в модуле КОП.
ПолучитьПуть () / GetPathName(). Возвращает полный путь и название файла, в котором хранится модуль реализации класса.
ПолучитьКонтекстОкружения () / GetEnvContext() - Получить контекст окружения из модуля КОП. Возврат: Возвращает контекст, из которого получил управление модуль КОП
ПолучитьСписокПараметров (стрИмяМетода) / GetParamsList(strNameOfMeth) Получить список со значениями неявных параметров, переданных в метод, название которого необходимо передать в качестве параметра. Данный метод можно использовать только в теле метода класса, который был определен с последним формальным параметром ":" в файле определения КОП. Более подробное описание см. в пункте 1.3., раздел "Неопределенное количество параметров".
УстановитьПараметрПоИндексу (стрИмяМетода>,<чИнд>,<нЗнач) / SetOnIndexParams(strNameOfMeth >,<nInd>,<uVal) - Метод предназначен для получения ссылки значения неявного параметра по его порядковому номеру и установке нового значения по данной ссылке. - стрИмяМетода - Имя метода ссылки на неявные параметры, которого необходимо получить. - чИнд - номер неявного параметра, ссылку на который надо получить и заменить - нЗнач - Новое значение. Возврат: 1 - установка успешно произведена, 0 - произошла ошибка при установке
ЗаменитьЭксзБазовогоКласса (стрИмяКласса>, <нЗначениеКласса) / ReplaceInstBaseClasses(strNameClass>, <uValOfClass) - Предназначен для замены экземпляра базового класса в уже созданной иерархии классов. Изменения базового класса отражаются только на объекте, для которого был вызван данный метод. - стрИмяКласса - имя базового класса, экземпляр которого мы собираемся заменять. - нЗначениеКласса - новый экземпляр базового класса. Возврат: 1- замена успешно произведена, 0 - произошла ошибка при замене
_ПриОткрытии / _OnOpen - данный метод используется только в целях подключения отладчика для отладки классов 1С++. Подробней см. главу 1.6.
_ВыброситьИскл (ОбъектИскл) / _Throw(Object) - формирует исключение с объектом- исключением. Вызов этого метода в модуле КОП прекращает его выполнение и данное исключение передается дальше в другие модули для поиска обработчика исключения (раскрутка стека). Если такой обработчик не будет найден, то выполнение последнего модуля будет прекращено с выводом диагностического сообщения в окно сообщений 1С. Получить объект "Исключение" можно с помощью метода GetExeption()/ПолучитьИсключение () дополнительного класса ExecuteModule/ ВыполняемыйМодуль . ОбъектИскл - любой объект 1С.
_ПолучитьКод () / _GetCode() - функция, которая должна вернуть строковое представление объекта;
_SQLCreate (Value, obMDW) - процедура, которую должен реализовать КОП для типизации значения поля выборки объекта ODBCRecordset типом этого КОП (виртуальный конструктор). Должна быть объявлена в модуле с ключевым словом Экспорт. Должна иметь два или меньше параметров. Её вызывает объект ODBCRecordset при получении значения поля выборки, при типизации типом этого КОП. Параметры: - Value - значение поля выборки без типизации; - obMDW - статический объект типа MetaDataWork.
Файл определения классов должен быть расположен в одном каталоге с файлом конфигурации и называться Defcls.prm, если файла с таким именем не обнаружено, ВК осуществляет поиск в текущей конфигурации обработки с именем Defcls. Синтаксис языка определения классов следующий:
// - комментарий для программы класс (class) имя класса = имя файла класса реализации : <имя базового класса>, <имя базового класса> { Объявления методов класса };
Текст КОП возможно хранить в конфигурации в виде обработок, для этого необходимо указать имя обработки и прибавить окончание @MD, с соблюдением регистра букв у окончания.
Пример файла инициализации компоненты:
[alias_path] Псевдоним1 = С:\Классы1С_1\ Псевдоним2 = С:\Классы1С_2\
Далее в файле определения пути к классам необходимо написать следующее:
класс МойКласс = #Псевдоним1\ Псевдоним1.ert {};
Любой тип - может быть заменен ключевым словом Неопределенный (Undefine), что отключает для данного параметравозвращаемого значения проверку типов. Типом может быть Справочник, что означает возможность передавать в параметр метода справичник любойго вида справочника, (аналогичное поведение для типов Документ, и Регистр, и т.п.) - смысл, аналогичный типу "Справочник" или ГрупповойКонтекст, когда типом параметра может быть контекст любого модуля. (обязательный)
Примеры:
класс Базовый_1=Base_1.txt { void Метод1(Число пар1, Строка пар2); Число Метод2(Число пар1, Дата пар2); }; класс Производный_1=Derive_1.txt: Базовый_1, Справочник.Спр1, ТаблицаЗначений { void Метод1(Число пар1, Строка пар2); // Данный метод переопределяет метод //базового класса Число КоличествоСтрок (Число Колич); // В данном случае мы переопределили //метод базового класса ТаблицаЗначений и добавили проверку типов. В реализации этого //метода можно переадресовать вызов базовому классу void Метод3(ОС ОС); // в этот метод мы должны передаем передавать КОП "ОС" }; класс ОС=OC.txt : Справочник.ОсновныеСредства, Справочник.НематериальныеАктивы { void Метод1(Базовый_1 пар1); // В данном случае в пар1 можно передавать //объекты типа "Базовый_1" и его производный класс "Производный_1" void Метод2(Производный_1 пар1); // в отличие от предыдущего случае в пар1 мы //можем передавать объекты типа "Производный_1", но не "Базовый_1" Число ВыбратьЭлементы(Число чРежим); Число ПолучитьЭлемент(Число чРежим); };
Внимание: проверка типов может быть отключена по требованию пользователя, об этом см. ниже.
Для каждого КОП необходим отдельный файл реализации (как текстовый, так и отчет 1С), расположенный в каталоге БД или в папке, указанной в определении имени файла класса, либо в папке, указанной в псевдониме пути. С именем класса связывается файл реализации КОП в файле определения Defcls.prm.
Синтаксис языка файла реализации КОП полностью соответствует синтаксису языка 1С:Предприятия, плюс возможно использовать препроцессорные директивы.
Открытые атрибуты КОП определяются как общие переменные в модуле с ключевым словом Экспорт.
Закрытые атрибуты КОП определяются как общие переменные в модуле без ключевого слова Экспорт.
Обращение к методам компоненты из файла реализации КОП (модуля КОП), таких таким как: ПолучитьБазовыйКласс и НазначитьБазовыйКласс, осуществляется через Контекст данного модуля. Пример:
// В начале каждого модуля определяем закрытую функцию для получения // контекста. Функция GetThis(Конт) Возврат Конт; КонецФункции // Реализация метода "Метод1" класса "Производный_1" см. предыдущий пример Процедура Метод1 (пар1, пар2) Экспорт Конт = GetThis(Контекст); Базовый_1 = Конт.ПолучитьБазовыйКласс("Базовый_1"); Если Базовый_1 <> 0 Тогда КонецЕсли; КонецПроцедуры
Внимание!
Предупреждение: нельзя сохранять контекст класса в его атрибуте, т.е. запрещен следующий алгоритм:
Перем Конт; Функция GetThis(Конт) Возврат Конт; КонецФункции ............... Конт = GetThis(Контекст); ...............
Если Вы будете использовать такое присваивание, объекты класса, созданные Вами в алгоритмах с помощью конструкции СоздатьОбъект("ИмяКлассаКОП"), никогда не уничтожатся (memory leaks), из-за циклической ссылки на объект внутри модуля реализации КОП. Это замечание также справедливо и для взаимных ссылок, когда один экземпляр класса содержит в себе ссылку на другой, и этот другой, в свою очередь, имеет ссылку на первый.
В каждой реализации класса можно создать процедуры Конструктор () англ. Constructor() и Деструктор () англ. Destructor(), которые вызываются, соответственно, в моменты создания экземпляра класса и его уничтожения (Конструктор() - объект создан, Деструктор() - объект уничтожен). Реализация данных процедур не обязательна.
Для контроля установки/записи атрибутов необходимо определить предопределенные методы (Процедура) в модуле реализации КОП (слово Экспорт к данным методам применять не обязательно):
Отладчик: Для получения информации в отладчике по динамическим свойствам класса необходимо определить следующие функции:
Внимание!
Методы обязательно должны реализоваться оба, нельзя реализовать только один из них. Если Вы определили данные методы, то отключаются автоматическая установка/чтение статических атрибутов класса и не вызываются определенные методы обработки установки/чтения "ПриЗаписи_ИмяАтрибута" и "ПриПолучении_ИмяАтрибута". Вся обработка по сохранению/возврату значений атрибутов (свойств) ложится на разработчика класса.
В базовых классах иерархии возможно вызывать открытые методы производных классов (объявленные с ключевым словом "Экспорт"), воспользовавшись для этого контекстом модуля базового класса, также с помощью контекста возможно получить название конечного созданного класса (с помощью функции "СоздатьОбъект").
Экземпляры создаваемых классов можно сохранять в строку, а затем восстанавливать из неё (сериализация КОП). Для этого в классе необходимо определить следующие методы:
Если в классе реализован метод "КлассСохраняемый()", который возвращает значение, не равное 0, и реализован метод СохранитьКлассВСтроку(), то при использовании функции 1С ЗначениеВСтрокуВнутр(ЭкзКласса) вернет строку, сформированную в классе "ЭкзКласса" методом "СохранитьКлассВСтроку()".
Внимание!
Для восстановления классов КОП, поддерживающих сериализацию, необходимо напрямую вызывать его метод "ЗагрузитьИзСтроки()", функция 1С "ЗначениеИзСтрокиВнутр" не может создавать экземпляры классов КОП. Поэтому можно реализовать вспомогательную функцию, которая бы создавала экз. классов КОП и вызывала их метод десериализации. Код может быть следующим:
Функция глКлассИзСтрокиВнутр(стрЗнач) Экспорт сз = СоздатьОбъект("СписокЗначений"); сз.ИзСтрокиСРазделителями(стрЗнач); Если сз.РазмерСписка() > 0 Тогда стрНазваниеКласса = сз.ПолучитьЗначение(1); Если ПустоеЗначение(стрНазваниеКласса)=1 Тогда Возврат -2; КонецЕсли; Попытка о = СоздатьОбъект(стрНазваниеКласса); бКлассСохр = 0; Попытка бКлассСохр = о.IsSerializable(); Исключение Попытка бКлассСохр = о.КлассСохраняемый(); Исключение Возврат -4; КонецПопытки; КонецПопытки; Если бКлассСохр = 0 Тогда Возврат -4; КонецЕсли; Попытка о.LoadFromString(сз.ВСтрокуСРазделителями()); Исключение Попытка о.ЗагрузитьИзСтроки(сз.ВСтрокуСРазделителями()); Исключение Возврат -5; КонецПопытки; КонецПопытки; Возврат о; Исключение Возврат -3; КонецПопытки; Иначе Возврат -1; КонецЕсли; КонецФункции //глКлассИзСтрокиВнутр
Примечание
Автоматическое групповое сохранение и восстановление экземпляров классов, возможно, выполнять с помощью класса "DynaValue" (см. глава 3.6., описание методов "ВыгрузитьВСтроку", "ЗагрузитьИзСтроки", "ВыгрузитьВФайл", "ЗагрузитьИзФайла")
Примеры: Иерархия классов выглядит так:
class База = base.ert { void Метод1(); // Этот метод мы не переопределяем, а вызываем здесь // Метод2 производного класса void Метод2(); }; class Производный = derive.ert : Тест14_База { void Метод2(); // переопределяем метод базового класса };
Модуль реализации класса "База":
Функция GetThis(Конт) Возврат Конт; КонецФункции Процедура Метод2() Экспорт Сообщить("База::Метод2"); КонецПроцедуры Процедура Метод1() Экспорт Сообщить("ТипзначенияСтр(Контекст) = "+ТипзначенияСтр(GetThis(Контекст))); // получаем название текущего класса GetThis(Контекст).Метод2(); // вызываем переопределенный метод КонецПроцедуры
Модуль реализации класса "Производный":
Процедура Метод2() Экспорт Сообщить("Производный::Метод2"); КонецПроцедуры
Модуль:
Сообщить("Создали Производный класс"); копПрозв = СоздатьОбъект("Производный"); копПрозв. Метод1(); Сообщить("Создали База класс"); копБаза = СоздатьОбъект("База"); копБаза. Метод1();
Вывод на экран будет следующим:
Создали Производный класс Производный Производный::Метод2 Создали База класс База База::Метод2
В одном модуле можно хранить код нескольких классов. Для этого необходимо код каждого класса заключить в следующие скобки:
//# ClassBegin <ClassName1>; Перем ПеременнаяКласса1; Процедура Конструктор() ......... //# ClassEnd <ClassName1>; //# ClassBegin <ClassName2>; Перем ПеременнаяКласса2; Процедура Конструктор() ......... //# ClassEnd <ClassName2>;
Где вместо <ClassName1> нужно указать имя класса (без угловых скобок). Ограничения:
Директивы препроцессора 1С++ могут быть в файлах объявления интерфейсов классов (Defcls.prm), в файлах реализации КОП и во всех модулях 1С, кроме модуля глобального модуля и модулей внешних обработок/отчетов. Символы препроцессора, объявленные в файле объявления класса, действительны в области видимости модулей реализации класса, т.е. доступны в файлах реализации КОП. Внимание: из-за раздельной интерпретации файла Defcls.prm и файлов реализации КОП изменения, определения и отключение символов препроцессора осуществляются независимо. Работа препроцессора гарантируется в следующей последовательности: сначала обрабатывается файл Defcls.prm, затем в неопределенной последовательности обрабатываются файлы реализации КОП.
Если символ определен, что равнозначно "истине", или символы, объединённые логическими операторами дают в результате "истину", то код, заключенный между директивами //#if и //#elif или //#else или //#endif, буден включен на выполнение. Symbol - это символ, который будет тестироваться на определенность ранее директивой //#define. Перед символом можно использовать знак ! (логическое отрицание). Operator:
= (равно); != (не равно); & (И); \| (ИЛИ);
тело с кодом, открытое директивой //#if, должно закрываться директивами //#elif , //#else, //#endif;
Для отладки модулей классов необходимо выполнить следующие действия:
В настройках компоненты 1С++ нажать кнопку "Отладка;
В глобальном модуле конфигурации прописать экспортную переменную Перем Форма Экспорт;
Если модуль класса расположен в текстовом файле, то следует сохранить его в модуле отчета;
Форма отчета, в котором хранится модуль класса, обязана иметь хотя бы один диалоговый элемент.
В модуле класса прописать предопределенную функцию "ПриОткрытии" и вставить туда следующий код:
Процедура ПриОткрытии() Форма.Параметр._ПриОткрытии(); КонецПроцедуры
Настройка компоненты осуществляется с помощью настройки параметров 1С, Сервис --> Параметры ... --> Закладка "Настройка 1C++". Данная закладка появляется только после загрузки компоненты методом 1С ЗагрузитьВнешнююКомпоненту.
На данной закладке присутствуют три флажка в виде кнопок: "Проверка типов" и "Оптимизация", "Отладка":