1. Введение. Зачем нам чистый код?
«Чистый код» - это код, написанный с соблюдением стандартов и рекомендаций от компании «1С», который при этом легко читать и поддерживать. Иными словами, практика написания «чистого кода» позволяет нам быстро исправлять, дорабатывать или даже искать ошибки в своем или чужом коде. Однако программисты приходят к этому далеко не сразу. Я был не исключением.
Помимо очевидных выгод от написания понятного кода для программиста, есть множество плюсов и для «заказчика» (работодателя) – чем понятнее код, тем проще и дешевле его дорабатывать программистами, которые не участвовали в первоначальной разработке.
В 1С термин «Чистый код» появился относительно недавно. Однако термин "Чистый код" (или "Clean Code") в не «1С-ной среде» стал популярным благодаря одноимённой книге Роберта Мартинa, вышедшей в 2008 году. Книга и концепция в целом рассматривают принципы написания кода, который будет простым, понятным и поддерживаемым.
2. Структура модуля
Каждый модуль, согласно стандарту 455, должен иметь определенную структура, которая формируется с помощью областей. Области в модулях прописываются с помощью ключевых слов #Область<ИмяОбласти> и #КонецОбласти. При создании нового модуля необходимо следовать данному стандарту, а при добавлении методов необходимо размещать их в соответствующей области.
Данная техника структурирует методы (процедуры и функции) и позволяет максимально быстро находить нужные данные в коде. Кстати, при использовании комбинации клавиш Ctrl+Shift+минус (-), текст модуля будет свернут до заголовков областей.
При открытии такого модуля разработчик легко сможет понять, какие обработчики событий определены для формы или ее отдельных элементов, просмотреть, какие глобальные переменные определены, какие обработчики команд созданы в форме и так далее. В стандарте 455 приведены образцы структуры каждого вида модулей, при создании новых модулей можно воспользоваться стандартной структурой или добавить образцы в шаблоны текста конфигуратора.
Набор областей различается в зависимости от типа модуля. Стандартом предусмотрены следующие виды областей:
Раздел "Программный интерфейс" содержит экспортные процедуры и функции, предназначенные для использования в других модулях конфигурации.
Раздел "Служебный программный интерфейс" предназначен для модулей, которые являются частью функциональной подсистемы. В нем должны быть размещены экспортные процедуры и функции, которые могут быть вызваны только из других функциональных подсистем этой же библиотеки.
Раздел "Служебные процедуры и функции" содержит процедуры и функции, составляющие внутреннюю реализацию модуля.
Раздел "Обработчики событий" содержит обработчики событий соответствующего модуля.
Раздел "Обработчики событий элементов шапки формы" включает процедуры-обработчики элементов, расположенных в основной части формы (все, что не связано с таблицами на форме).
В разделах "Обработчики событий элементов таблицы формы <имя таблицы формы>" размещаются процедуры-обработчики таблиц формы и элементов таблиц. Для каждой таблицы должен быть создан свой раздел.
Раздел "Обработчики команд формы" содержит процедуры-обработчики команд формы (имена которых указаны в свойстве "Действие команд формы").
Поначалу может показаться, что Вам придется делать лишнюю работу, однако это не так. Благодаря данному подходу вы резко сократите время на последующую доработку данного кода.
Если по началу у Вас будут сложности с тем, в какую область поместить тот или иной метод – можно открыть любую типовую конфигурацию и проанализировать код в аналогичном модуле. То есть если вы разрабатываете форму нового документа, откройте модуль формы типового документа. Я уверен, что уже после первого прочтения вам станет понятна логика, которую закладывали разработчики типовых конфигураций.
3. Общие требования к текстам модулей
Общие требования к текстам модулей изложены в стандарте 456.
Все тексты модулей должны быть написаны на русском языке.
Исключением из этого правила являются веб-сервисы, для которых имена методов и параметры рекомендуется задавать на английском языке. Например, в УТ 11.5 есть веб. сервис «CustomerOrdersExchange», который имеет множество функций на английском языке. Это выглядит так:
Функция GetCatalogs(MobileDeviceID)
Возврат МобильноеПриложениеЗаказыКлиентов.ВыгрузитьСправочники(MobileDeviceID);
КонецФункции
Функция GetPriceList(MobileDeviceID, AllPrices = Ложь, Address = "")
Возврат МобильноеПриложениеЗаказыКлиентов.ВыгрузитьПрайсЛист(MobileDeviceID, AllPrices, Address);
КонецФункции
В текстах модулей запрещено использовать букву "Ё". Исключения составляют текстовые сообщения интерфейса, которые выводятся пользователю в сообщениях, формах и справочной информации.
Программные модули не должны содержать неиспользуемые методы. Наличие таких методов отвлекает разработчика и заставляет его тратить время на выяснение их назначения (Однако в типовых конфигурациях часто можно встретить подобное. Находили подобные методы?).
В программных модулях не должно быть закомментированных фрагментов кода, а также ссылки на конкретного разработчика, который вносил правки. Если при разработке используется «хранилище», поиск автора доработки не должно составить проблему.
Тексты модулей должны оформляться по принципу «один оператор на одну строку».
При оформлении текста модуля следует использовать синтаксические отступы. Для этого необходимо применять табуляцию вместо пробелов, чтобы при изменении количества знаков табуляции выравнивание текста оставалось неизменным.
- На крайней левой позиции должны располагаться только:
- операторы Процедура, КонецПроцедуры, Функция, КонецФункции;
- заголовки (описания) процедур и функций;
- объявления переменных модуля;
- операторы основного раздела программы (с учетом синтаксических отступов);
- директивы компилятора, такие как &НаКлиенте, &НаСервере и т.д.;
- инструкции препроцессора (включая #Область и #КонецОбласти).
Согласно стандарту, если длина строки превышает 120 символов, рекомендуется использовать переносы. Строки длиной более 120 символов не следует писать, за исключением случаев, когда переносы невозможны.
4. Переменные
Имена переменных
Правила формирования имен переменных определяются стандартом 454.
Имена переменных следует составлять на основе терминов из предметной области. Это необходимо для того, чтобы из названия переменной было понятно её назначение.
Формирование имен включает в себя удаление пробелов между словами, при этом каждое слово пишется с прописной буквы. Препозиции и местоимения из одной буквы также следует писать с заглавной буквы.
Кстати, стилистика формирования имен переменных в «1С» настолько сильно влилась в мою жизнь, что даже, папки на своем пк я называю в подобной стилистике;)
Примеры корректных имен переменных:
Перем ОтветНаВопрос; //Ответ на вопрос
Перем ПараметрыТовара; //Параметры товара
Перем СтраницаКоманднойПанели; //Страница командной панели
Перем КоличествоСтрок; //Количество строк
Некорректные имена переменных:
Перем стрТов;
Перем Пер1;
Перем тов;
Перем мас_рез;
Исключением из правил при нейминге переменных может быть только счетчик в цикле. В таком случае переменная может состоять из 1 символа, однако я всегда стараюсь находить более осознанные имена для таких случаев. Пример:
ВсеДопустимыеСимволы = Новый Соответствие;
Для Позиция = 1 По СтрДлина(ДопустимыеСимволы) Цикл
ВсеДопустимыеСимволы[Сред(ДопустимыеСимволы, Позиция, 1)] = Истина;
КонецЦикла;
Переменные, отражающие состояние флага, следует называть таким образом, чтобы имя точно описывало истинное значение этого флага. Например:
Перем ЕстьСтрокаКонвертации;
Перем ЕстьОшибки;
Перем ЭтоУслуга;
При выборе имени переменной необходимо стремиться к тому, чтобы оно как можно точнее отражало смысл её создания.
5. Процедуры и функции
5.1. Имена процедур и функций
Работа с именами процедур и функций регламентирована стандартом 647.
Правильно выбранные имена процедур и функций играют ключевую роль в улучшении читаемости кода. В большинстве случаев осмысленное имя процедуры в сочетании с подходящими именами параметров позволяют избежать необходимости дополнительного описания. Когда возникают трудности с выбором названия для процедуры или её параметров, это может быть сигналом о возможных архитектурных проблемах в коде. В свою очередь, если подобрать «самодокументирующееся» имя не составляет труда, это указывает на то, что процедура была спроектирована корректно.
Имена процедур, функций и формальных параметров следует создавать на основе терминов из предметной области таким образом, чтобы их назначение было очевидно. Необходимо стремиться к тому, чтобы названия были «говорящими» и сами себя документировали.
Так же, как и для переменных, названия методов формируются путем устранения пробелов между словами, при этом каждое слово начинается с заглавной буквы. Предлоги и местоимения, состоящие из одной буквы, также пишутся с заглавной буквы.
Рассмотрим несколько примеров неудачных имен функций:
Функция ВыполнитьКоманду(Идентификатор, ОбъектыНазначения)
Из названия этой функции неясно, что именно она проверяет и какие параметры следует передавать. Чтобы понять, как использовать данную функцию, разработчику придется изучить ее код.
Рассмотрим еще несколько примеров:
Функция ОбработатьРезультатТЗ_Заказ(РезультатТЗ)
Исходя из названия данной функции можно предположить, что она обрабатывает какую-то таблицу значения, вероятнее всего данные таблицы значений каким-то образом связанным с «Заказом клиента» или «Заказом поставщика». Однако абсолютно не понятно почему параметр функции называется РезультатТЗ. В названии используется знак «_» (подчеркивание), что является недопустимым в нейминге процедур и функций.
Функция EmailValid(Адрес)
В начале статьи я писал, что в некоторых случая можно использовать латиницу для именования процедур и функций. Данная процедура находилась в модуле формы обработки, где подобный подход не допустим.
Теперь приведу примеры правильного наименования функций и параметров:
Функция ПолучитьРеквизитФормыПоПути(Форма, ПутьРеквизита)
С такой функцией сразу понятно, для чего она предназначена и какие параметры нужно ей передать. Рассмотрим еще пример:
Процедура УстановитьПараметрДинамическогоСписка(Список, ИмяПараметра, Значение, Использование = Истина)
Эти примеры продемонстрируют нам, как важен правильный выбор имен для повышения читаемости, удобства использования и поддержки кода.
Не рекомендуется включать в названия процедур и функций типы принимаемых параметров или возвращаемых значений.
Примеры неправильного наименования:
Функция ПолучитьМассивНомеровСтрок(ТабличнаяЧасть)
Функция ПолучитьСтруктуруРеквизитовПартии()
Корректный вариант:
Функция НомераВыбранныхСтрок(ТабличнаяЧасть)
Функция РеквизитыПартии()
Эта рекомендация обычно применима в большинстве случаев, за исключением редких ситуаций, когда без указания типа возвращаемого значения невозможно понять назначение процедуры или функции.
Например, в следующей функции:
Функция ТаблицаЗначенийВМассив(ТаблицаЗначений)
Функция СтруктураВСтрокуJSON(Значение)
Функция МассивВТаблицуЗначений(МассивСтруктур)
без указания типов параметров и возвращаемого значения становится трудно описать суть функции.
Имена процедур, как правило, следует формировать от неопределенной формы глагола, отражающего суть выполняемого действия.
Пример неправильного наименования:
Процедура ЗагрузкаНоменклатуры(Параметры)
Корректное наименование:
Процедура ЗагрузитьНоменклатуру(Параметры)
Для имен функций рекомендуется использовать описание возвращаемого значения, избегая при этом упоминаний о выполняемых действиях.
Неправильные примеры:
Функция ПолучитьНастройкуДляТекущегоПользователя()
Функция ПолучитьТекстЗапросаЗаказыСИстекающимСрокомВыдачи()
Функция ПолучитьСтатусЗаказа(Ссылка)
Правильные варианты:
Функция НастройкуДляТекущегоПользователя()
Функция ТекстЗапросаЗаказыСИстекающимСрокомВыдачи()
Функция СтатусЗаказа(Ссылка)
Иными словами, название функции не должно начинаться с глагола. Существует два исключения из вышеописанного правила.
Первое исключение касается случаев, когда понимание назначения функции требует знания способа, которым было получено возвращаемое значение. Например:
Функция ВыбратьСсылкиДляОбработки(Очередь, ПолноеИмяОбъекта, ДополнительныеПараметры = Неопределено)
Второе исключение возникает, когда выполнение функции предполагает выполнение действия, а возврат значения не является ее первоочередной задачей (например, сигнализируя о успешном завершении действия). В таких случаях названия функций следует формировать от неопределенной формы глагола, как и для процедур. Например:
Функция РазрешитьРаботуПользователей()
Если функция предназначена для создания какого-либо объекта, рекомендуется использовать слово «Новый» в ее названии. Например:
Функция НоваяПодпись()
Функция НовыеНастройкиКонтрагента()
Функция НовыйДокументСверкаВзаиморасчетовПоОборотам(ПараметрыЗаполнения)
Если функция проверяет какое-либо условие, то ее название лучше начинать со слова «Это» или использовать причастия. Такой подход позволяет ясно обозначить, какое значение (Истина или Ложь) будет возвращаться функцией. Например:
Функция ЭтоНоваяОчередь(Претензия, Статус)
Функция ЭтоАдминистративноТерриториальныйАдрес(ТипАдреса)
Функция ЭтоГородФедеральногоЗначения(Город)
Идеально, если каждая функция или процедура будет решать только одну конкретную задачу. Если в названии метода присутствует союз «и» или имеются другие указания на множественные действия, это может свидетельствовать о необходимости разделить метод на несколько отдельных функций или переименовать его.
Неправильный пример:
Функция НайтиТоварыИСоздатьПеремещения(ПараметрыЗаказа)
Правильные примеры:
Функция НайтиТовары(ПараметрыЗаказа)
Функция СоздатьПеремещения(ПараметрыЗаказа)
Таким образом, принципы ясного и лаконичного именования функций помогут сделать код более читаемым и поддерживаемым.
Приведу еще несколько примеров не корректных имен процедур и функций:
Функция ВыполнитьМетодИОбработатьДанные(пДанные = Неопределено, пФорма = Неопределено, ИмяМетода)
Процедура СформироватьИОтправитьПисьмоСОшибкой(стрПараметры, РезультатЗапроса)
Как вы думаете, какие здесь основные проблемы? Если знаете ответ – напишите его в комментариях.
5.2. Параметры процедур и функций
При объявлении формальных параметров процедур и функций важно следовать общим правилам, касающимся именования переменных. Имена параметров должны быть сформированы на основе терминов из предметной области, чтобы их назначение было очевидно.
Следует избегать использования переменных модулей, реквизитов форм и других средств конфигурирования в качестве параметров функций. Это может значительно увеличить область видимости методов и привести к трудно диагностируемым ошибкам.
Параметры функций должны быть расположены в логической последовательности. Рекомендуется организовывать их по принципу «от общего к частному».
Примеры неправильного расположения параметров:
Процедура ПроверитьЗаполнениеДатыПроизводстваСрокаГодности(ИмяТЧ, Отказ, ДокументОбъект) Экспорт
Процедура ПриИзмененииСтатусаДокумента(НовыйСтатус, ПредыдущийСтатус, ДокументСсылка ПараметрыОбновленияСтатуса = Неопределено) Экспорт
Правильное расположение:
Сначала следует указывать основные параметры — ДокументОбъект и ДокументСсылка:
Процедура ПроверитьЗаполнениеДатыПроизводстваСрокаГодности(ДокументОбъект, ИмяТЧ, Отказ) Экспорт
Процедура ПриИзмененииСтатусаДокумента(ДокументСсылка, ПредыдущийСтатус, НовыйСтатус, ПараметрыОбновленияСтатуса = Неопределено) Экспорт
Необязательные параметры (параметры со значениями по умолчанию) должны располагаться после обязательных параметров, которые не имеют значений по умолчанию.
Не рекомендуется объявлять в функциях слишком много параметров. Существует правило: не более семи параметров, с не более чем тремя параметрами со значениями по умолчанию. В противном случае читаемость кода вызывающей функции может сильно ухудшиться, и повышается вероятность ошибок при передаче необязательных параметров, особенно из-за путаницы с количеством запятых.
Если требуется передать в функцию большое количество параметров, рекомендуется в качестве параметра использовать Структуру. А именно группировать однотипные параметры, в один или несколько параметров типа Структура. Пример:
Процедура ПриЗаполненииПоставляемыхПрофилейГруппДоступа(ОписанияПрофилей, ПараметрыОбновления) Экспорт
Эта процедура взята из общего модуля БСП УправлениеДоступом. Если мы посмотри описание Процедуры, то в комментариях мы увидим следующее описание:
// ПараметрыОбновления - Структура:
// * ОбновлятьИзмененныеПрофили - Булево - начальное значение Истина.
// * ЗапретитьИзменениеПрофилей - Булево - начальное значение Истина.
// Если установить Ложь, тогда поставляемые профили можно не только просматривать, но и редактировать.
// * ОбновлятьГруппыДоступа - Булево - начальное значение Истина.
// * ОбновлятьГруппыДоступаСУстаревшимиНастройками - Булево - начальное значение Ложь.
// Если установить Истина, то настройки значений, выполненные администратором для
// вида доступа, который был удален из профиля, будут также удалены из групп доступа.
Благодаря подобному подходу, восприятие кода становится более структурным и логичным. Более того, можно гораздо проще добавлять новые параметры в существующую процедуру.
Таким образом, соблюдение этих рекомендаций поможет улучшить читаемость и поддержку кода, снижая вероятность ошибок при его использовании.
Заключение
В следующей статье мы продолжим разбирать практики «чистого кода», а также стандарты разработки от компании «1С».