При обучении программистов, пришедших в сферу 1С из классических языков программирования (таких как Java, C++, C# или Python), часто возникает барьер непонимания. Для разработчика с бэкграундом в традиционном ООП интерфейс Конфигуратора, метаданные, справочники и документы кажутся чем-то чуждым, «вещью в себе». Стандартный подход к обучению — просто показать, как создавать объекты в дереве метаданных — не дает им понимания внутренней архитектуры системы.
В этой статье мы подробно разберем, как спроецировать привычные концепции объектно-ориентированного программирования на платформу 1С:Предприятие 8. Мы проведем четкие параллели, разберем архитектурные ограничения платформы и покажем, что 1С — это зрелый, высокоуровневый компонентный Enterprise-фреймворк со своей специфичной объектной моделью.
Прежде чем переходить к коду, давайте выясним причину, почему объектная модель 1С спроектирована именно так. Платформу 1С:Предприятие 8 нельзя рассматривать как просто «язык программирования». Это специализированная среда разработки и исполнения (Runtime), совмещенная с мощной ORM (Object-Relational Mapping) и генератором интерфейса.
В традиционных языках для построения бизнес-приложения вам приходится самостоятельно описывать классы, настраивать маппинг в базу данных (например, через Hibernate или Entity Framework), создавать контроллеры и верстать UI. В 1С вся эта рутина автоматизирована. Разработчик описывает структуру данных в терминах предметной области, а платформа на лету генерирует таблицы БД, классы для работы с ними и формы ввода.
Поэтому «свободное» ООП в 1С ограничено сознательно. Это сделано для обеспечения жесткой типизации бизнес-сущностей, автоматического контроля ссылочной целостности и высокой скорости разработки.
Рассмотрим базовое понятие ООП — класс. Класс является шаблоном для создания объектов. Что является классом в 1С?
Объект метаданных (справочник, документ, регистр сведений), который мы создаем в дереве конфигурации, — это и есть объявление класса. Когда мы добавляем в конфигураторе справочник Контрагенты, мы описываем новый класс, задаем его свойства (Реквизиты) и методы (процедуры модуля объекта).
Однако здесь кроется ключевое отличие 1С от классических систем. Из-за трехзвенной архитектуры и необходимости работы с базой данных один класс метаданных порождает в оперативной памяти несколько специализированных системных типов (экземпляров):
СправочникСсылка.Контрагенты) — это легковесный неизменяемый объект. Он содержит только уникальный идентификатор (GUID) записи в базе данных и базовые реквизиты (код, наименование). Его можно сравнить с паттерном DTO (Data Transfer Object) или умным указателем. Ссылки можно безопасно передавать между клиентом и сервером.СправочникОбъект.Контрагенты) — это тяжеловесный объект, содержащий в себе все данные записи базы данных. Он создается в оперативной памяти сервера для изменения данных. У него есть методы Записать(), Удалить() и обработчики событий (бизнес-логика).СправочникВыборка.Контрагенты) — объект-итератор, предназначенный для последовательного обхода записей БД.Проанализируем эту концепцию на простом примере кода. Посмотрим, как мы получаем доступ к объекту для его модификации:
// Получаем ссылку на элемент (аналог указателя на объект)
СсылкаНаКонтрагента = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", "7701020304");
Если Не СсылкаНаКонтрагента.Пустая() Тогда
// Переходим от ссылки к тяжеловесному объекту в памяти (инстанцирование для изменения)
ОбъектКонтрагент = СсылкаНаКонтрагента.ПолучитьОбъект();
// Изменяем свойство класса (реквизит объекта)
ОбъектКонтрагент.Комментарий = "Проверен службой безопасности";
// Вызываем метод класса для сохранения состояния в БД
ОбъектКонтрагент.Записать();
КонецЕсли;
Инкапсуляция предполагает сокрытие внутренней реализации объекта и предоставление публичного интерфейса для взаимодействия. Как это реализовано в 1С?
Инструментом инкапсуляции в платформе выступают Модули. В модуле объекта (например, документа или справочника) мы можем объявлять локальные переменные, процедуры и функции. По умолчанию все они являются приватными (скрытыми внутри объекта).
Чтобы сделать метод частью публичного интерфейса класса, мы используем ключевое слово Экспорт. Посмотрим на пример инкапсуляции бизнес-логики внутри модуля объекта документа РеализацияТоваровУслуг:
// Код внутри Модуля объекта документа РеализацияТоваровУслуг
// Приватная переменная модуля (доступна только внутри этого экземпляра)
Перем ЛокальныйКоэффициентСкидки;
// Приватный метод - скрыт от внешнего вызова
Процедура РассчитатьЛокальныеПараметры()
ЛокальныйКоэффициентСкидки = 0.05;
КонецПроцедуры
// Публичный метод - часть внешнего интерфейса класса
Процедура РассчитатьСуммуДокумента() Экспорт
РассчитатьЛокальныеПараметры();
Для Каждого СтрокаТЧ Из Товары Цикл
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена * (1 - ЛокальныйКоэффициентСкидки);
КонецЦикла;
КонецПроцедуры
Внешний код, имея ссылку на этот объект, не может напрямую вызвать процедуру РассчитатьЛокальныеПараметры() или изменить переменную ЛокальныйКоэффициентСкидки. Он взаимодействует только через публичный метод:
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
ДокументОбъект.РассчитатьСуммуДокумента(); // Успешно
// ДокументОбъект.РассчитатьЛокальныеПараметры(); // Вызовет ошибку компиляции/выполнения!
А как же быть с табличными частями, о которых спрашивали на форуме? Табличная часть — это классический пример композиции (агрегации) в ООП. Она представляет собой строго типизированную коллекцию строк, которая не может существовать в отрыве от своего владельца (объекта документа или справочника). Время жизни строк табличной части полностью управляется родительским объектом.
В традиционном ООП разработчик может построить глубокую иерархию классов (например: ТранспортноеСредство -> Автомобиль -> Грузовик). В 1С классическое наследование сущностей базы данных ограничено.
Все объекты метаданных используют жестко заданное платформой одношаговое наследование от абстрактных базовых системных классов.
Когда вы создаете документ ПриходнаяНакладная, платформа неявно наследует его от абстрактного класса ДокументОбъект. Вы не можете создать новый документ и указать, что он наследуется от уже существующего документа РеализацияТоваровУслуг.
Такое ограничение продиктовано требованиями реляционной СУБД. Платформа должна точно знать структуру таблиц в базе данных, чтобы гарантировать транзакционность и высокую скорость выполнения SQL-запросов. Свободное динамическое наследование таблиц в СУБД привело бы к колоссальному падению производительности.
Тем не менее, наследование поведения реализовано через механизмы предопределенных событий платформы. Разработчик переопределяет методы базового класса (такие как ПередЗаписью(), ПриЗаписи(), ОбработкаПроведения()), внедряя в них кастомную бизнес-логику.
Полиморфизм — это способность функции обрабатывать объекты разных типов, имеющих одинаковый интерфейс, без знания конкретного типа объекта.
Поскольку встроенный язык 1С является динамически типизированным, полиморфизм здесь работает «из коробки» на основе принципа «утиной типизации» (Duck Typing): *«Если это выглядит как утка, плавает как утка и крякает как утка, то это, вероятно, и есть утка»*.
Рассмотрим ситуацию: у нас есть разные документы — РеализацияТоваровУслуг, ОказаниеУслуг и ПередачаТоваров. У каждого из этих документов в модуле объекта определен экспортный метод РассчитатьПараметрыДоставки(). Нам нужно написать общую процедуру расчета для любого из этих документов. Проанализируем, как это будет выглядеть:
// Общая процедура в общем модуле
Процедура ВыполнитьРасчетДоставки(ЛюбойДокументОбъект) Экспорт
// Платформе не важен точный тип объекта.
// Главное, чтобы у него был доступен метод "РассчитатьПараметрыДоставки"
ЛюбойДокументОбъект.РассчитатьПараметрыДоставки();
КонецПроцедуры
При передаче в процедуру ВыполнитьРасчетДоставки любого объекта, поддерживающего этот интерфейс, код отработает корректно в рантайме. Если же мы передадим туда объект справочника Номенклатура, у которого такого метода нет, система выбросит исключение во время выполнения.
Также ярким примером системного полиморфизма является групповое проведение документов. Платформа вызывает системный метод Записать(РежимЗаписиДокумента.Проведение) у коллекции разнородных документов. Каждый конкретный документ-потомок выполняет этот метод по-своему: двигает свои регистры накопления, регистры бухгалтерии и т.д.
Если вам в проекте жизненно необходим кастомный класс с произвольной логикой, не привязанный к таблицам базы данных, в 1С для этого используют объекты типа Обработка.
Давайте сопоставим структуру обработки с элементами класса в ООП:
Обработки.ИмяОбработки.Создать()).Разберем практический пример. Спроектируем класс «Калькулятор Скидок» на базе обработки. Создадим обработку с именем КалькуляторСкидок.
В Модуле объекта этой обработки напишем методы экземпляра класса:
// Код Модуля объекта обработки "КалькуляторСкидок"
Перем БазовыйПроцент Экспорт; // Публичное свойство экземпляра
Процедура Инициализировать(Знач НачальныйПроцент) Экспорт
БазовыйПроцент = НачальныйПроцент;
КонецПроцедуры
Функция РассчитатьСтоимостьСоСкидкой(Знач ИсходнаяСтоимость) Экспорт
Возврат ИсходнаяСтоимость * (1 - БазовыйПроцент / 100);
КонецФункция
В Модуле менеджера этой же обработки опишем статический метод, который не зависит от состояния конкретного экземпляра:
// Код Модуля менеджера обработки "КалькуляторСкидок"
Функция ПолучитьМаксимальноДопустимуюСкидку() Экспорт
Возврат 50; // Статическое значение, общее для всего класса
КонецФункция
Теперь посмотрим на пример использования нашего спроектированного класса во внешнем коде:
// 1. Использование статического метода (без создания объекта в памяти)
МаксСкидка = Обработки.КалькуляторСкидок.ПолучитьМаксимальноДопустимуюСкидку();
// 2. Инстанцирование класса - создание экземпляра объекта
Калькулятор = Обработки.КалькуляторСкидок.Создать();
Калькулятор.Инициализировать(15); // Установка начального состояния
// 3. Вызов метода экземпляра
ИтоговаяЦена = Калькулятор.РассчитатьСтоимостьСоСкидкой(10000);
// ИтоговаяЦена будет равна 8500
С помощью такого подхода в 1С можно реализовывать даже сложные паттерны проектирования, такие как «Фабрика» (Factory), «Строитель» (Builder) или «Стратегия» (Strategy).
Если ваши ученики считают, что в 1С нет сложной архитектуры, покажите им код БСП. Библиотека стандартных подсистем целиком построена на паттернах проектирования, адаптированных под платформу.
Рассмотрим два ярких примера:
ПриЗапускеПрограммыПереопределяемый). Платформа предоставляет жесткий каркас алгоритма запуска системы, но оставляет «точки расширения» в виде процедур в специальных модулях, где разработчик может написать свой код, не меняя основную логику вендора.В завершение обучения обязательно расскажите программистам о новой технологии фирмы «1С» — платформе «1С:Предприятие.Элемент» и встроенном языке «1С:Исполнитель».
В этих современных инструментах разработчикам доступно полноценное, классическое объектно-ориентированное программирование без каких-либо компромиссов:
public и private).При обучении программистов платформе 1С не пытайтесь скрыть отсутствие некоторых классических механизмов ООП в стандартной версии 8.3. Напротив, открыто объясните им причины этих ограничений. Преподнесите 1С как специализированный инструмент: