Единый семинар «1С» для бухгалтеров и руководителей

Использование программных перечислений, ч.1: строковые константы

Программирование - Практика программирования

Часто ли у вас возникает необходимость в коде выполнять сравнение на строку?

Любой разработчик рано или поздно сталкивается с необходимостью использования сравнения на константную строку в коде, а также с флаговыми переменными - когда необходимо сделать ветвление по сложному условию. Как сделать все это наиболее оптимально?

 
 1. Концепция
 
 2. Примеры использования

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

  • Ленивой инициализации - расчет значения будет выполнен только один раз - при первом обращении (см. вики)

  • устранению дублирования кода

  • повышению читабельности кода

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

См. также

Вознаграждение за ответ
Показать полностью
Комментарии
1. Виталий Чебан (VitaliyCeban) 318 12.12.16 12:31 Сейчас в теме
Хорошее решение, разве что только скобки () глаза мусолят, но это терпимо.
Вот бы сделали "свойства", как в C#. С виду, обычные поля, а на деле - методы.
Хотя, если уж вносить изменения в встроенный язык 1С, то относительно этой задачи, надо сразу enum'ы делать.
dj_serega; demkonst; +2 Ответить
6. Алекс Кон (alex-l19041) 10 12.12.16 17:16 Сейчас в теме
(1)
Хорошее решение
- можно уточнить о чем идет речь ? (обсуждается какая-то публикация ?)
7. Роман Уничкин (unichkin) 700 12.12.16 17:26 Сейчас в теме
Что-то на ИС с комментариями, не видел (1) и (2)
(1) Ну да, мозолят) Идея использовать структуру для этих целей и появилась после того как прочитал чей-то коммент про отсутствие таких вещей в 1С, там тоже человек с любовью С вспоминал)
(6) я так понимаю, текущая ;)
3. Константин Гейнрих (CyberCerber) 162 12.12.16 14:06 Сейчас в теме
Главная проблема, что для этого перечисления не сработает контекстная подсказка. Т.е. все равно придется идти и смотреть, какие значения бывают, копировать, вставлять...
myr4ik07; rpgshnik; demkonst; NN2P; pikachu94; TreeDogNight; minimajack; Yakud3a; Zhilyakovdr; 7OH; Krio2; Danila-Master; herfis; kraynev-navi; tormozit; mindcannon; +16 Ответить
4. Виктор Григоренко (JohnGalt) 2 12.12.16 16:56 Сейчас в теме
Актуальная проблема. Исходя из собственного опыта, лучше все настройки, в том числе и сравнения "повесить" на пользователя. Назначить ответственных за механизмы/процессы. Для этого лучше всего сделать справочник или несколько с набором реквизитов/табличных частей для различных настроек. И настройки получать, например, ПриНачалеРаботыСистемы()
5. Роман Уничкин (unichkin) 700 12.12.16 17:12 Сейчас в теме
(4) Ну, тут речь не совсем об этом. Вам к уже введенным настройкам надо как-то обращаться в коде. То что должен быть момент передачи ответственности - полностью согласен, как и с тем что лучше такое реализовать пользовательской настройкой. Только имхо, это не взлетит в 90% случаев фикси. Но это уже тема для отдельной беседы)
8. Роберт В е р т и н с к и й (v3rter) 12.12.16 17:29 Сейчас в теме
9. rjhev korum (корум) 314 12.12.16 17:35 Сейчас в теме
(8) это скорей не автор забыл, а движок инфостарта заглючил...
14. Роман Уничкин (unichkin) 700 12.12.16 19:20 Сейчас в теме
(8)(10) не понял вопросов?
(11) Любую коллекцию параметров удобно так использовать - посмотрите последний пример с планом счетов. В примере - сравниваются значения структуры. Ну добавьте такой элемент:
СтруктураСтатусыЗаявок.Вставить("ТоварВПути"    , "Товар в пути");
10. Роберт В е р т и н с к и й (v3rter) 12.12.16 17:49 Сейчас в теме
Если взять не общий случай - ветвление кода, а частный - выбор набора параметров или макета, то чем плох обыкновенный справочник?
11. Александр Сокол (shurik_shurik) 12.12.16 17:57 Сейчас в теме
Это очень хорошо, если во входящем файле статусы заявок без пробелов записаны. А если с пробелами, как тогда поступать?
Идея конечно интересная.
13. Сан Саныч (herfis) 213 12.12.16 18:16 Сейчас в теме
Честно говоря, профита не так много как хотелось бы.
Но уже одно то, что в рантайме упадет сразу в том месте, где была ошибка в названии перечисления, дорогого стоит.
Т.е. в сложных случаях имеет смысл использовать. В простых - вряд ли.
the1; 7OH; zqzq; +3 Ответить
15. Роман Уничкин (unichkin) 700 12.12.16 19:35 Сейчас в теме
(13) Основной профит - в удобной структуре кода. Кроме того, автоматически получается оптимизация - в том смысле что коллекция параметров не будет рассчитываться по 10 раз подряд. Неоднократно видел как люди в цикле пишут что-то вроде сч70_1_1 = ПланыСчетов.Хозрасчетный.НайтиПоКоду("70.1.1").
В каких случаях использовать - я теперь только так работаю, если есть несколько строковых констант, даже не задаюсь вопросом "надо ли". Это похоже на рассуждение: "зачем оформлять код по правилам ИТС, для своей разовой доработки?" - т. е. человек не понимает зачем вообще его нужно оформлять, не видит в чем удобство. Я например даже для небольших работ все оформляю - это копейки времени, которые окупаются с лихвой если из разовой доработки понадобится ваять что-то серьезное, или передать кому-то на анализ, или вспомнить что делалось через полгода. Это на подкорке. Чтобы я забыл код оформить - никогда такого не было. Также и здесь. Но это конечно мое "имхо")
uncle_Vasya; nihfalck; +2 Ответить
17. Сан Саныч (herfis) 213 13.12.16 10:20 Сейчас в теме
(15) "Основной профит - в удобной структуре кода". Не согласен. "Структура кода" удобнее не становится. Я не про счета и кэширование (это уже несколько про другое), а про саму идею программных перечислений. В плане удобства - получаем сомнительный аргумент "красивенько" против дополнительного времени на реализацию и въезжание постороннего программиста в этот код. Вот своевременное падение в рантайме для легкого поиска ошибки - это профит. Если бы во время компиляции ругалось - это вообще был бы всем профитам профит. А если бы еще и автокомплишн в конфигураторе - практически полноценные перечисления бы вышли.
zqzq; uncle_Vasya; 7OH; +3 Ответить
18. Роман Уничкин (unichkin) 700 13.12.16 11:05 Сейчас в теме
(17) Именно в нем. "Тупой код с хорошей структурой, лучше чем тупая структура с гениальным кодом". Про время на реализацию - расхожее заблуждение. Дровосек рубит деревья, у него спрашивают - мужик, че ты мучаешься, у тебя ж топор тупой. Наточи топор. А он отвечает: "мне НЕКОГДА! мне надо РУБИТЬ". И все "рубят". Код должен читаться как книга.
По приоритету, для меня так:
- "въехать" в это можно достаточно быстро
- при этом можно быть уверенным что коллекция заполняется только в одном конкретном месте, благодаря использованию фиксированной структуры
uncle_Vasya; borrman; +2 Ответить
16. Николай Васильев (vasilev2015) 366 13.12.16 10:15 Сейчас в теме
Может, лучше использовать соответствие ?
Синтаксис похожий, плюшек больше.
Подходит для больших списков.
the1; vvirus; +2 Ответить
19. Роман Уничкин (unichkin) 700 13.12.16 11:06 Сейчас в теме
(16) Используйте то, что вам удобнее) Но соответствие не выдаст исключения при получении отсутствующего значения, а вернет неопределено. У него несколько иное назначение, чем у структуры.
sulfur17; +1 Ответить
20. Александр Беляков (=Kollega=) 2 13.12.16 14:39 Сейчас в теме
Идея хорошая, но все же ИМХО лучше один раз вызвать функцию и вернуть в локальную переменную структуру и работать с ней, чем 10 раз вызывать и проверять, проиницилизирована ли нужная нам структура.
Но это так просто придирка, хотя когда подобных структур будет достаточно много (хотя я даже не представляю себе род такой задачи), поиск и копирование структур может стать достаточно ощутимым.
m_sb; the1; +2 Ответить
21. Сан Саныч (herfis) 213 13.12.16 16:19 Сейчас в теме
(20) Тут как раз все в порядке.
Во-первых, ленивая инициализация - это само по себе хорошо.
Во-вторых, явная инициализация на старте - это еще одна "точка привязки" в коде для реализации этой концепции. Абсолютно лишняя.
ЗЫ. Поиск в структуре по ключу не должен стать ощутимым, даже если ключей будет много. Если я правильно помню - это хэшированная структура данных. Ну, разве что свопить начнет :) А копирования тут нет.
22. Леонид Павлиенко (PLAstic) 183 13.12.16 18:08 Сейчас в теме
Не имею возможности изучить каменты. А автору уже намекнули на то, что надо вынести инфу в табличный макет и грузить соответствия из него?
23. Андрей Акулов (DrAku1a) 1222 14.12.16 09:40 Сейчас в теме
Мне кажется, что автор начиная со первого примера "СтруктураСтатусыЗаявок" забыл про своё-же замечание: (буква "О" <> "о").
ehoes; корум; belchonokh; klinval; dimk@a; +5 Ответить
26. Роман Уничкин (unichkin) 700 14.12.16 13:10 Сейчас в теме
(23)
Мне кажется, что автор начиная со первого примера "СтруктураСтатусыЗаявок" забыл про своё-же замечание: (буква "О" <> "о").
- не вижу ошибки, ткните пальцем если не сложно.
(24) Я думаю, если дорабатываете конфу - то добавьте нормальное перечисление.
Этот способ хорош для константных значений, уникальных в каком-то определенном алгоритме (внешнем или внутреннем). Например - статусы заявок приходят к нам в виде текста. Но в конфигурации это наверняка перечисление. Здесь структура нужна только для того, чтобы удобно и качественно проверять все входящие значения статусов.
Еще пример с потолка (кривая методология, просто чтобы отразить суть вопроса): скажем в документе оператор заполняет строковое поле "Причина отказа". Причина может вводиться как из заранее определенного списка, так и произвольно - если установлен соотв. флаг. Так вот - если не добавлять в конфигурации нормальное перечисление (а лучше добавить, сделав "причину" составным полем), то да, наверное логично так сделать. Только модуль ПовтИсп хранит кэш 20 минут. Возможно лучше использовать какие-то существующие кэши конфигурации. И "мСтруктураКэшПрограммныхПеречислений" не будет нужна только для этого конкретного случая.
(25) Если рассуждать только с позиции загрузки заявок - то конечно, да, лучше дать на откуп пользователю. А еще лучше проговорить это все в ТЗ. Потом при формировании структуры перечисления собрать данные из таблицы формы - да, логично, почему нет. Но загрузка заявок - это ведь частный случай, я его использовал для примера. У меня например была задача, в которой я создавал программное перечисление для событий журнала регистрации. Т.е. было примерно так:
СтруктураСобытийЖурнала = Новый Структура();
СтруктураСобытийЖурнала.Вставить("НачалоСеанса", "_$Session$_.Start");
СтруктураСобытийЖурнала.Вставить("КонецСеанса", "_$Session$_.Finish");
СтруктураСобытийЖурнала.Вставить("Проведение", "_$Data$_.Post");
// ну и т. д.

Потом для фильтров использовал СобытияЖР().Проведение и т.п.
корум; sulfur17; +2 Ответить
24. Николай Орлов (sulfur17) 14.12.16 11:44 Сейчас в теме
А можно функцию СтатусыЗаявок() разместить в глобальном общем модуле с повторным использованием возвращаемых значений на время сеанса?
Тогда и переменная мСтруктураКэшПрограммныхПеречислений не нужна и можно будет использовать СтатусыЗаявок() в других модулях.

Например если я хочу передать СтатусыЗаявок().Отказ в качестве параметра из другого модуля, то в другом модуле функцию СтатусыЗаявок() не видно. А если разместить ее в глобальном модуле, то будет видно.
unichkin, Что об этом думаете?
25. rjhev korum (корум) 314 14.12.16 11:55 Сейчас в теме
(0) Более другой вариант.
Заполнять такие перечисления в таблицы на форме, проводить программное сопоставление, окончательную корректировку оставлять пользователю.
Обработку данных производить с учетом полученных таблиц соответствий (количество значений в которых неопределено и разработчика не волнует - он работает со стандартным справочником/перечислением из соответствия)

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

обработаются одинаково корректно.
27. Игорь Герман (German_Tagil) 6 15.12.16 07:58 Сейчас в теме
28. Владислав Мороз (vlad.frost) 172 15.12.16 09:35 Сейчас в теме
Если инвертировать проверку свойства в мСтруктураКэшПрограммныхПеречислений, то это позволит уменьшить уровни вложенности и код будет читаться легче. Сравните:

Функция СтатусыЗаявок()
	
	Если мСтруктураКэшПрограммныхПеречислений.Свойство("СтатусыЗаявок") Тогда		
		Возврат мСтруктураКэшПрограммныхПеречислений.СтатусыЗаявок;
	КонецЕсли;

	СтруктураСтатусыЗаявок = Новый Структура;
	СтруктураСтатусыЗаявок.Вставить("НаУтверждении", "НаУтверждении");
	СтруктураСтатусыЗаявок.Вставить("ТоварвПути", "ТоварВПути");
	СтруктураСтатусыЗаявок.Вставить("КОтгрузке", "КОтгрузке");
	СтруктураСтатусыЗаявок.Вставить("Отказ", "Отказ");
					
	мСтруктураКэшПрограммныхПеречислений.Вставить("СтатусыЗаявок", Новый ФиксированнаяСтруктура(СтруктураСтатусыЗаявок));
	
	Возврат мСтруктураКэшПрограммныхПеречислений.СтатусыЗаявок;
		
КонецФункции
Показать
ValentinGushchin; dj_serega; +2 Ответить
29. Роман Уничкин (unichkin) 700 15.12.16 10:09 Сейчас в теме
(28) Я обычно стремлюсь к сокращению "Возвратов", если можно рассчитать значение внутри функции и вернуть результат в конце метода - так и делаю; приведенный вами код не кажется мне более читабельным решением, может дело вкуса. Делайте как вам удобнее) Я останусь при своем.
Кстати, сейчас подумал - что в функциях обычно минимизирую возвраты, а в процедурах как раз наоборот - если есть условие прерывания, стараюсь выполнить его в самом начале, чтобы не использовать лишних расчетов.
30. Владислав Мороз (vlad.frost) 172 15.12.16 13:54 Сейчас в теме
(29) Единая точка выхода, ага, нас тоже так в школе учили. Но если уж вы предлагаете способы, как улучшить читаемость кода, идите дальше. Согласен, в приведённом мной примере данное изменение не сильно улучшит читаемость, но примените его к вашей функции ИспользуемыеСчета(), и там такое улучшение будет уже более актуально. Матчасть: Замена вложенных условных операторов граничным оператором.
ValentinGushchin; +1 Ответить
31. Роман Уничкин (unichkin) 700 15.12.16 16:41 Сейчас в теме
(30) Да нет в "ИспользуемыеСчета()" ветка условия всего одна, так что принцип тот же что и в (29). То, что описывает приведенная вами статья я стараюсь соблюдать, и это как-то всегда на уровне спинного мозга осознавал - что не стоит допускать дебрей Case-Of Если-Тогда. Однако за наводку и интерес к моей статье - спасибо)
Мне в данном конкретном случае кажется более логичным заполнять структуру именно внутри условия. Возможно потому, что основные цели этих методов (для вызова программного перечисления) - это заполнение соотв. кэша, и более логично в начале алгоритма видеть инициализацию коллекции по условию ее отсутствия. Тут с т.з. рефакторинга было бы неплохо вынести код по заполнению структуры счетов в внешний метод, но я не люблю перегружать программный интерфейс методами (пусть даже и служебный). Т.е. использоваться -то он будет все-равно только в одном месте. Вот если его глобальным сделать, тогда другое дело. Было бы нечто вроде такого:
Функция ИспользуемыеСчета()
	
	Если НЕ мСтруктураКэшПрограммныхПеречислений.Свойство("ИспользуемыеСчета") Тогда
		СтруктураИспользуемыеСчета = Новый Структура;
		
		// Чтобы добавить счет, добавьте сюда его код
		СтрокаКодовСчетов = "10, 10.3, 19, 60.1, 60.2, 62, 51, 51, 70, 70.1, 76.АВ, 90, 90.01, 90.02";
		СтруктураИспользуемыеСчета = ОбщегоНазначенияБП.СтруктураСчетовПоСтрокеКодов(СтрокаКодовСчетов );

		мСтруктураКэшПрограммныхПеречислений.Вставить("ИспользуемыеСчета", Новый ФиксированнаяСтруктура(СтруктураИспользуемыеСчета));
	КонецЕсли; 	
	
	Возврат мСтруктураКэшПрограммныхПеречислений.ИспользуемыеСчета;
	
КонецФункции
Показать
32. Кирилл Власов (neikist) 29.12.16 20:38 Сейчас в теме
Понимаю что поздновато отписываюсь, но как вам такой вариант?

#Область ПрограммноеПеречисление_СтатусыЗаявок

Функция _СтатусыЗаявок_НаУтверждении()	Возврат "НаУтверждении"	КонецФункции
Функция _СтатусыЗаявок_ТоварВПути()		Возврат	"ТоварВПути"		КонецФункции
Функция _СтатусыЗаявок_КОтгрузке()			Возврат	"КОтгрузке"		КонецФункции
Функция _СтатусыЗаявок_Отказ()			Возврат	"Отказ"			КонецФункции


#КонецОбласти

Функция ДанныеФайлаУспешноСчитаны() Экспорт
	
	//...
	СтатусЗаявки = СокрЛП(Excel.Cells(1, Сч).Text);

	Если СтатусЗаявки = _СтатусыЗаявок_НаУтверждении() Тогда
		//...		
	ИначеЕсли СтатусЗаявки = _СтатусыЗаявок_ТоварВПути() Тогда
		//...		
	ИначеЕсли СтатусЗаявки = _СтатусыЗаявок_КОтгрузке() Тогда	
		//...		
	КонецЕсли; 

	Возврат Истина; 		
	
КонецФункции
Показать



Плюсы:
1. Можно использовать в формах &НаСервере, вместо того чтобы каждый раз инициализировать переменную после клиента;
2. Есть автодополнение и контекстная подсказка;
3. Если даже набрать имя функции неправильно - ошибка всплывет на этапе синтаксической проверки (при отладке вашего способа не всегда может получится все ветки прогнать, и до ветки с ошибочно набранным значением можно не добраться)
4. По идее возвращаемое значение таких функций должно "инлайниться" в место использования, так что производительность должна быть такая же как при обычном сравнении со значением, а если и нет то что у меня, что у Вас накладные расходы на вызов функции так или иначе идут.

Минусов с ходу могу назвать только два:
1. Засорение контекстной подсказки, для чего и добавил в начало _;
2. Такое "перечисление" не передашь в другой модуль, разве что вместе со всем текущим контекстом (ЭтотОбъект придется передавать из общего модуля или формы) и делать данные функции экспортными.
33. Роман Уничкин (unichkin) 700 29.12.16 23:51 Сейчас в теме
(32)
1. Объявляете реквизит формы "СтруктураКэшПрограммныхПеречислений", с произвольным типом реквизита... А остальное все то-же) Это если для формы. Вообще правильнее все-же к форме относить то, что касается *формы* - интерактив, все расчеты лучше отправлять на сервер, т.е. в модуль объекта. Ну тут можно много рассуждать и говорить - по всякому можно. Если говорим о внешней обработке можно сделать реквизит обработки произвольного типа - он будет доступен и с сервера и с клиента. Если о объекте конфигурации - сделайте реквизит формы. Или - вызов из модуля ПовтИсп.
2. Если учесть что различие только в префиксе - имхо, не очень удобно для набора, когда много коллекций.
С моей точки зрения, когда перечисление обосабливается в свой метод - это и нагляднее и удобнее. Ну да, синтакс контроль не заругается. Пишите аккуратнее) Каждому свое конечно, используйте то что вам больше подходит. Но мне с таким кодом было бы работать тяжеловато. Подчеркивание в начале метода - это нарушение правил образования имен переменных. Ну и плюшки с ленивой инициализацией теряются.
34. Andrey Erastov (tailer2) 20.01.17 18:31 Сейчас в теме
>через предопределенный список элементов плана счетов, т.е. туда надо провалиться, найти код счета, скопировать имя... - долго

на этот случай имена счетов сохранены в отдельном файлике, открыл, скопипастил
35. SergeyFirst (SergeyFirst) 58 19.09.17 12:05 Сейчас в теме
Красиво и лаконично! Молодца!
Оставьте свое сообщение