Top.Mail.Ru
Full-time, 5/2
Формат: удаленный
Вакансия «1С-программист»

Пиши код правильно — по стандарту. Часть 2

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

В статье есть ссылки на bsl-ls – подробнее можно ознакомиться по этому адресу.

Плагин для автоматической проверки кода на соответствие стандарту

Для автоматического подсчета когнитивной или цикломатической сложности, а также для проверки соответствия кода положениям стандарта можно использовать различные дополнительные программы и утилиты, например PhoenixBSL.

Скачать установщик можно по этому адресу.

Описание программы и возможности доступно здесь.

После установки в конфигураторе можно выделить любой блок кода, нажать Ctrl + I и в отдельном окне получить информацию с рекомендациями для соблюдения стандарта.

Объем метода

Методы не стоит делать слишком длинными. Нужно стремиться делать методы небольшими, но рекомендуемый размер у разных авторов отличается. По мнению BSL-Language Server, рекомендуется ограничиваться 200 строками кода, другие предлагают ограничиваться количеством строк, которые вмещаются в два-три экрана пользователя.

Для уменьшения объема рекомендуется:

— разбивать большой метод на несколько более мелких логически цельных,

— вынести части внутренних механик в отдельные процедуры и функции.

Принцип единства ответственности

Каждый метод должен выполнять одно действие, используя такой принцип:

1. Объем метода будет небольшим.

2. Легче понять логику.

3. Выделяемые функции и процедуры можно вызывать или дорабатывать отдельно от общего метода.

В конфигуратор встроена функция по выделению части кода в отдельную процедуру или функцию.

Для выделения блока кода в отдельную процедуру в конфигураторе требуется выделить нужный блок, вызвать консольное меню (правой кнопкой мыши), выбрать «Рефакторинг»/ «Выделить фрагмент». После этого программа запросит название новой процедуры и создаст новую процедуру, а в исходном методе добавит вызов созданной новой процедуры.

Когнитивная сложность

Общая когнитивная сложность каждого метода не должна превышать 15 единиц сложности.

Правила для подсчета когнитивной сложности:

— каждые операторные скобки (Для … Цикл/ КонецЦикла) увеличивают когнитивную сложность на 1 сложности,

— каждое условие и тернарный оператор дает +1 сложности,

— каждая бинарная операция (И, Или) добавляет +1 сложности,

— альтернативная ветвь условия +1,

— попытка/ исключение также дают +1 сложности,

— переход на метку «Перейти ~ Метка;» — +1,

— за каждый уровень вложенности блоки получают дополнительно +1 сложности,

— альтернативные ветки, бинарные операции и переход на метку не увеличивают когнитивную сложность за вложенность (остаются +1 при вложенности).

Подробнее с условиями подсчета можно ознакомиться в описании к плагину по этому адресу.

Пример подсчета (в начале каждой строки указан добавочный балл когнитивной сложности):

Функция Когнитивно-СложныйКод(ТипКласса)
	
+1	Если ТипКласса.Неизвестен() Тогда
		Возврат Символы.НеизвестныйСимвол;
	КонецЕсли;

	НеизвестностьНайдена = Ложь;
	СписокСимволов = ТипКласса.ПолучитьСимвол().Потомки.Поиск(«имя»);
+1	Для Каждого Символ Из СписокСимволов Цикл
+2	Если Символ.ИмеетТип(Симовлы.Странное)
+1	И Не Символы.Экспортный() Тогда

+3	Если МожноПереопределять(Символ) Тогда
	Переопределяемость = ПроверитьПереопределяемость(Символ);
+4	Если Переопределяемость = Неопределено Тогда
+5	Если Не НеизвестностьНайдена Тогда
	НеизвестностьНайдена = Истина;
        КонецЕсли;
        +1ИначеЕсли Переопределяемость Тогда
        Возврат Символ;
        КонецЕсли;
+1      Иначе
	Продолжить;
	КонецЕсли;
	КонецЕсли;
	КонецЦикла;

+1	Если НеизвестностьНайдена Тогда
	Возврат Символы.НеизвестныйСимвол;
	КонецЕсли;

КонецФункции

Цикломатическая сложность

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

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

Рекомендуемый предел цикломатической сложности равен 20.

Подсчет отличается от когнитивной сложности.

Следующие операторы увеличивают цикломатическую сложность на одну единицу:

— Для … Цикл

— Если … Тогда

— ИначеЕсли … Тогда

— Иначе

— Попытка … Исключение … КонецПопытки

— Перейти ~ Метка

— Бинарные операции И, ИЛИ

— Тернарный оператор

— Процедура

— Функция

Вложенные операторы не увеличивают цикломатическую сложность.

Пример:

Функция СерверныйМодульМенеджера(Имя) // 1

ОбъектНайден = Ложь; // 0
 
ЧастиИмени = СтрРазделить(Имя, "."); // 0
Если ЧастиИмени.Количество() = 2 Тогда // 1
 
	ИмяВида = ВРег(ЧастиИмени[0]); // 0
	ИмяОбъекта = ЧастиИмени[1]; // 0

	Если ИмяВида = ВРег("Константы") Тогда // 1
		Если Метаданные.Константы.Найти(ИмяОбъекта) <> Неопределено Тогда // 1
			ОбъектНайден = Истина; // 0
		КонецЕсли; // 0
	ИначеЕсли ИмяВида = ВРег("РегистрыСведений") Тогда // 1
	Если Метаданные.РегистрыСведений.Найти(ИмяОбъекта) <> Неопределено Тогда // 1
			ОбъектНайден = Истина; // 0
		КонецЕсли; // 0
	Иначе // 1
		ОбъектНайден = Ложь; // 0
	КонецЕсли; // 0
КонецЕсли; // 0

Если Не ОбъектНайден Тогда // 1
	ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку( // 0
		НСтр("ru = 'Объект метаданных ""%1"" не найден, // 0
		|либо для него не поддерживается получение модуля менеджера.'"), // 0
		Имя); // 0
КонецЕсли; // 0
УстановитьБезопасныйРежим(Истина); // 0
Модуль = Вычислить(Имя); // 0
F = ?(Условие, ИСТИНА, НЕОПРЕДЕЛЕНО); // 1
А = ?(Условие, ИСТИНА, ?(Условие2, ЛОЖЬ, НЕОПРЕДЕЛЕНО)); // 2
M = ИСТИНА ИЛИ 7; // 1
Возврат Модуль; // 0
КонецФункции // итог 12

Глубина вложенности управляющих конструкций

Вне зависимости от цикломатической и когнитивной сложности вложенные управляющие конструкции (Если, циклы, …) не стоит делать глубже 4 уровней.

Работа с оператором Если

Не рекомендуется создавать громоздкое условие в блоке Если.

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

Пример:

// Неправильно:
Если ИдентификаторОъекта = «АнализСубконто»
	Или ИдентификаторОъекта = «АнализСчета»
	Или ИдентификаторОъекта = «ОбортноСальдоваяВедомость»
	Или ИдентификаторОъекта = «ОборотыСчета»
	Или ИдентификаторОъекта = «ГлавнаяКнига»
	Или ИдентификаторОъекта = «ШахматнаяВедомость» Тогда

	ПараметрыРасшифровки.Вставить(«ОткрытьОбъект», Ложь);

КонецЕсли;

//Правильно:

Если НеОткрыватьОбъект(ИдентификаторОбъекта) Тогда
	ПараметрыРасшифровки.Вставить(«ОткрытьОбъект», Ложь);
КонецЕсли;

Функция НеОткрыватьОбъект(ИдентификаторОбъекта)

	Возврат ИдентификаторОъекта = «АнализСубконто»
	Или ИдентификаторОъекта = «АнализСчета»
	Или ИдентификаторОъекта = «ОбортноСальдоваяВедомость»
	Или ИдентификаторОъекта = «ОборотыСчета»
	Или ИдентификаторОъекта = «ГлавнаяКнига»
	Или ИдентификаторОъекта = «ШахматнаяВедомость»;

КонецФункции

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

Пример:

Функция СерверныйМодульМенеджера(Имя) // 1
ОбъектНайден = Ложь; // 0
ЧастиИмени = СтрРазделить(Имя, "."); // 0
Если ЧастиИмени.Количество() = 2 Тогда // 1
	ИмяВида = ВРег(ЧастиИмени[0]); // 0
	ИмяОбъекта = ЧастиИмени[1]; // 0

	Если ИмяВида = ВРег("Константы") Тогда // 1
	Если Метаданные.Константы.Найти(ИмяОбъекта) <> Неопределено Тогда // 1
			ОбъектНайден = Истина; // 0
	КонецЕсли; // 0
	ИначеЕсли ИмяВида = ВРег("РегистрыСведений") Тогда // 1
	Если Метаданные.РегистрыСведений.Найти(ИмяОбъекта) <> Неопределено Тогда // 1
	ОбъектНайден = Истина; // 0
		
        КонецЕсли; // 0
	Иначе // 1
	ОбъектНайден = Ложь; // 0
	КонецЕсли; // 0
        КонецЕсли; // 0

Если Не ОбъектНайден Тогда // 1
	ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку( // 0
		НСтр("ru = 'Объект метаданных ""%1"" не найден, // 0
		|либо для него не поддерживается получение модуля менеджера.'"), // 0
		Имя); // 0
КонецЕсли; // 0
УстановитьБезопасныйРежим(Истина); // 0
Модуль = Вычислить(Имя); // 0
F = ?(Условие, ИСТИНА, НЕОПРЕДЕЛЕНО); // 1
А = ?(Условие, ИСТИНА, ?(Условие2, ЛОЖЬ, НЕОПРЕДЕЛЕНО)); // 2
M = ИСТИНА ИЛИ 7; // 1
Возврат Модуль; // 0
КонецФункции // итог 12
Функция СерверныйМодульМенеджера(Имя) // 1

ОбъектНайден = Ложь; // 0
 
ЧастиИмени = СтрРазделить(Имя, "."); // 0
Если ЧастиИмени.Количество() = 2 Тогда // 1
 
	ИмяВида = ВРег(ЧастиИмени[0]); // 0
	ИмяОбъекта = ЧастиИмени[1]; // 0

	Если ИмяВида = ВРег("Константы") Тогда // 1
		Если Метаданные.Константы.Найти(ИмяОбъекта) <> Неопределено Тогда // 1
			ОбъектНайден = Истина; // 0
		КонецЕсли; // 0
	ИначеЕсли ИмяВида = ВРег("РегистрыСведений") Тогда // 1
	Если Метаданные.РегистрыСведений.Найти(ИмяОбъекта) <> Неопределено Тогда // 1
			ОбъектНайден = Истина; // 0
		КонецЕсли; // 0
	Иначе // 1
		ОбъектНайден = Ложь; // 0
	КонецЕсли; // 0
КонецЕсли; // 0

Если Не ОбъектНайден Тогда // 1
	ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку( // 0
		НСтр("ru = 'Объект метаданных ""%1"" не найден, // 0
		|либо для него не поддерживается получение модуля менеджера.'"), // 0
		Имя); // 0
КонецЕсли; // 0
УстановитьБезопасныйРежим(Истина); // 0
Модуль = Вычислить(Имя); // 0
F = ?(Условие, ИСТИНА, НЕОПРЕДЕЛЕНО); // 1
А = ?(Условие, ИСТИНА, ?(Условие2, ЛОЖЬ, НЕОПРЕДЕЛЕНО)); // 2
M = ИСТИНА ИЛИ 7; // 1
Возврат Модуль; // 0
КонецФункции // итог 12
Функция СерверныйМодульМенеджера(Имя) // 1
ОбъектНайден = Ложь; // 0
ЧастиИмени = СтрРазделить(Имя, "."); // 0
Если ЧастиИмени.Количество() = 2 Тогда // 1
	ИмяВида = ВРег(ЧастиИмени[0]); // 0
	ИмяОбъекта = ЧастиИмени[1]; // 0

	Если ИмяВида = ВРег("Константы") Тогда // 1
	Если Метаданные.Константы.Найти(ИмяОбъекта) <> Неопределено Тогда // 1
			ОбъектНайден = Истина; // 0
	КонецЕсли; // 0
	ИначеЕсли ИмяВида = ВРег("РегистрыСведений") Тогда // 1
	Если Метаданные.РегистрыСведений.Найти(ИмяОбъекта) <> Неопределено Тогда // 1
	ОбъектНайден = Истина; // 0
		
        КонецЕсли; // 0
	Иначе // 1
	ОбъектНайден = Ложь; // 0
	КонецЕсли; // 0
        КонецЕсли; // 0

Если Не ОбъектНайден Тогда // 1
	ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку( // 0
		НСтр("ru = 'Объект метаданных ""%1"" не найден, // 0
		|либо для него не поддерживается получение модуля менеджера.'"), // 0
		Имя); // 0
КонецЕсли; // 0
УстановитьБезопасныйРежим(Истина); // 0
Модуль = Вычислить(Имя); // 0
F = ?(Условие, ИСТИНА, НЕОПРЕДЕЛЕНО); // 1
А = ?(Условие, ИСТИНА, ?(Условие2, ЛОЖЬ, НЕОПРЕДЕЛЕНО)); // 2
M = ИСТИНА ИЛИ 7; // 1
Возврат Модуль; // 0
КонецФункции // итог 12

В случае когда используется несколько конструкций ЕслиИначе, то после последней обязательно должна следовать ветка с Иначе.

Это защитное программирование. Такие конструкции устойчивы к дальнейшим изменениями и не маскируют возможные ошибки.

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

Пример:

// Неправильно:
Если ТипЗнч(ВходящийПараметр) = Тип(«Структура») Тогда
	Результат = ЗаполнитьПоСтруктуре(ВходящийПараметр);
ИначеЕсли ТипЗнч(ВходящийПараметр) = Тип(«ДокументСсылка.ВажныйДокумент») Тогда
	Результат ЗаполнитьПоДокументу(ВходящийПараметр);
КонецЕсли;

//Правильно:
Если ТипЗнч(ВходящийПараметр) = Тип(«Структура») Тогда
	Результат = ЗаполнитьПоСтруктуре(ВходящийПараметр);
ИначеЕсли ТипЗнч(ВходящийПараметр) = Тип(«ДокументСсылка.ВажныйДокумент») Тогда
	Результат ЗаполнитьПоДокументу(ВходящийПараметр);
Иначе
	ВызватьИсключение «Передан параметр неверного типа»;
КонецЕсли;

Магические даты и числа

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

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

Пример:

// Неправильно:
Если ТекДата > ’20151021’ Тогда
	ХоверБордБудетИзобретен = Истина;
КонецЕсли;

// Правильно:
ПредстказаннаяДата = ’20151021’;
Если ТекДата > ПредстказаннаяДата Тогда
	ХоверБордБудетИзобретен = Истина;
КонецЕсли;

// Неправильно:
Функция ПопадаетВИнтервал(Длительность)
	Возврат Длительность < 10 * 60 * 60;
КонецФункции

// Правильно:
Функция ПопадаетВИнтервал(ДлительностьВСекундах)

	МинутВЧасе = 60;
	СекундВМинуте = 60;
	СекундВЧасе = СекундВМинуте * МинутВЧасе;
	ЧасовВИнтервале = 10;

	Возврат ДлительностьВСекундах < ЧасовВИнтервале * СекундВЧасе;

КонецФункции

Использование тернарного оператора

Использование тернарного оператора оправдано, когда условие простое и одно.

Пример:


// Неправильно:
Результат = ?(х % 15 <> 0, ?(х % 5 <> 0, ?(х % 3 <> 0, х, «Fizz»), «Buzz»), «FizzBuzz»);

В данном примере очень сложный набор условий, поэтому использование тернарного оператора не оправдано, требуется использовать условный оператор.

// Правильно:
Если х % 15 = 0 Тогда
	Результат = «FizzBuzz»;
ИначеЕсли х % 3 = 0 Тогда
	Результат = «Fizz»;
ИначеЕсли х % 5 = 0 Тогда
	Результат = «Buzz»;
Иначе
	Результат = х;
КонецЕсли;

Многократное использование одинаковых строковых литералов

При многократном использовании одинаковых строковых литералов рекомендуется выделить его в отдельную функцию или переменную.

Пример:

// Неправильно:
Процедура ПроверитьПараметр(Параметр)
	Если Параметр = «ВРег» Тогда
		Результат = ВРег(«Строковое значение»);
	ИначеЕсли Параметр = «СокрЛП» Тогда
		Результат = СокрЛП(«Строковое значение»);
	Иначе
		Результат = Нрег(«Строковое значение»);
	КонецЕсли
КонецПроцедуры

// Правильно:
Процедура ПроверитьПараметр(Параметр)
	Если Параметр = «ВРег» Тогда
		Результат = ВРег(СтроковоеЗначение());
	ИначеЕсли Параметр = «СокрЛП» Тогда
		Результат = СокрЛП(СтроковоеЗначение());
	Иначе
		Результат = Нрег(СтроковоеЗначение());
	КонецЕсли
КонецПроцедуры
Функция СтроковоеЗначение()
	Возврат «СтроковоеЗначение»;
КонецФункции

Хранение информации в коде

Хранить информацию о сетевых адресах, логинах и паролях и прочую недопустимо по причинам:

— допуск к данной информации всех программистов, что недопустимо по требованиям безопасности;

— при изменении настроек требуется выпускать дополнительный релиз;

— для тестов часто используют другие настройки.

Пример:

// Неправильно:
СетевойАдрес = «192.168.1.1»;
КаталогОбмена = «с:/Обмен»;
Логин = «Администратор»;
Пароль = «12345»;

// Правильно:
ПараметрыАутентификации = СлужебныеПараметры. ПараметрыАутентификации (ТекущийКонтур());

Для хранения информации должна быть создана подсистема хранения служебных данных. В таком случае изменение данных параметров будет доступно в режиме предприятия, соответственно, доступ можно разделить по ролям.

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

Результат выполнения функции

Функция всегда возвращает результат, даже если в теле функции не прописан возврат.

В случае если в функции не прописан «Возврат», то вернется значение «Неопределено».

По стандарту рекомендуется обязательно прописывать «Возврат» функции.

Если в функции ветвление условного оператора Если и в каждой ветви прописан «Возврат», то рекомендуется вместо этого прописать в ветвях ВозвращаемоеЗначение, а возврат функции прописать в конце функции.

Пример:

// Неправильно:
Функция ЗначениеПоПараметру(Параметр) Экспорт
	
	Если Параметр = 1 Тогда
		Возврат 2;
	ИначеЕсли Параметр = 2 Тогда
		Возврат 4;
	Иначе
		Возврат Неопределено;
	КонецЕсли;

КонецФункции

// Правильно:
Функция ЗначениеПоПараметру(Параметр) Экспорт
	
	ВозвращаемоеЗначение = 0;
	Если Параметр = 1 Тогда
		ВозвращаемоеЗначение = 2;
	ИначеЕсли Параметр = 2 Тогда
		ВозвращаемоеЗначение = 4;
	Иначе
		ВозвращаемоеЗначение = Неопределено;
	КонецЕсли;

	Возврат  ВозвращаемоеЗначение;

КонецФункции

Comments

So empty here ... leave a comment!

Добавить комментарий

Sidebar