Преамбула
В топ 5 правил гигиены корпоративной разработки входит "Не используй НайтиПоНаименованию. И НайтиПоКоду тоже не стоит." Это хорошее правило, но возникло оно не просто так. Необходимость-то есть. Не в использовании этих конкретных покрытых позором богомерзких методов, а в том, для чего они используются - получить в коде какое-то вполне конкретное значение, слишком специфичное или слишком временное, для того чтобы выделять под него константу. Сюда же относятся случаи так называемых "магических чисел" и "магических строк", когда прямо в коде где-то посреди процедуры вдруг ДнейОтсрочки = 15, или СтрокаComПодкюченияКБазе = "Srvr=...".
Что же делать? Одного универсального ответа нет - иногда стоит завести какое-то дополнительное правило, добавить категорию, организовать, таки, константу или заставить выбирать значение вручную в конце концов. Но иногда вариантов особо нет - надо где-то хранить какую-то папку очень особенных контрагентов, дату, с которой запускается какая-то дополнительная проверка или что-то типа того.
Вне базы можно хранить только примитивные типы данных (ссылочные, конечно, тоже можно теоретически, но лучше не стоит). Внутри базы очевидным решением является регистр сведений. Строковое измерение с именем константы, ресурс составного типа со значением, функция ПолучитьЗначениеДополнительнойКонстанты(ИмяКонстанты) и вот готово хранилище, в которое можно укладывать что угодно. Ну, почти что угодно - строку неопределенной длины нельзя. В смысле, можно конечно, но только в отдельный ресурс - а в составе составного типа нет. И с ХранилищемЗначения такая же фигня. К тому же регистр - достаточно хрупкое образование - одна неловкая строчка и регистр пуст. Плюс интерфейс по хорошему бы переписать, чтобы запись ненароком непосредственно не удалить. И черт бы с ним с удалением - восстановить можно за пару минут, если бы версионирование регистра сведений можно было бы простенько организовать. Плюс когда констант станет больше ста, остро возникнет желание как-то разложить их по папочкам. В общем, решение рабочее, но не совсем удобное. Поэтому вот к чему я пришел.
Реализация
Организовано все достаточно просто - справочник с табличной частью. Элементы справочника представляют что-то типа групп констант, а имена и значения констант хранятся в табличных частях. К этому прикручен программный API, чтобы к значениям можно было обращаться через точку: "ИмяГруппы.ИмяКонстанты". Имя группы уникально в пределах справочника, имя константы в пределах группы.
То есть, для примера, хотим мы сохранить реквизиты для подключения по COM соединению к какой-то базе. Создаем элемент справочника с наименованием "ПодключениеКБухгалтерии" и несколькими строками табличной части для хранения имени базы, сервера, имени пользователя, пароля и версии COM объекта. И теперь в любом месте, где нам надо, мы можем получить, например, имя сервера, на котором хранится наша бухгалтерия:
СерверБух = Справочники.ДопКонстанты.ПолучитьЗначениеКонстанты("ПодключениеКБухгалтерии.ИмяСервера");
Плюс иногда надо сохранить программно что-то, что нельзя выбрать руками - какую-нибудь структуру, массив, или картинку, например. Делаем примерно так:
Справочники.ДопКонстанты.УстановитьЗначениеКонстанты("Алкоголь.ЛоготипНаПивныеЭтикетки", ВыбраннаяКартинка);
Механизм сам поймет, что тип не примитивный и не ссылочный и запакует в ХранилищеЗначения.
Описание API
Два основных метода - сеттер и геттер - УстановитьЗначениеКонстанты и ПолучитьЗначениеКонстанты соответственно. Определяются в модуле менеджера справочника.
Процедура УстановитьЗначениеКонстанты(ПутьККонстанте, Значение,
СоздаватьГруппуПриОтсутствии=Ложь, СоздаватьКонстантуПриОстутствии=Ложь,
ДопускатьСменуТипаСуществующейКонстанты=Ложь,
КакХранилище=Ложь, СтепеньСжатияХранилища=0) Экспорт
Записывает указанное значение по указанному пути.
- ПутьККонстанте (Обязательный) - путь к константе в формате "ИмяГруппы.ИмяКонстанты". Допускаются любые строки. Единственное требование - ровно одна точка, разделяющая имя группы и имя константы.
- Значение (Обязательный) - значение, которое будет записано по переданному пути. Допускаются любые примитивные типы (включая строку неограниченной длины), любые ссылки и любые типы которые упаковываются в хранилище значения (в описании типа должно быть "Сериализуется")
- СоздаватьГруппуПриОтутствии (Необязательный) - указывает на то, будет ли создана группа констант с переданным именем, если ее не существует. Если указана Ложь и группы с требуемым именем не существует, будет вызвано соответствующее исключение.
- СоздаватьКонстантуПриОстутствии (Необязательный) - указывает на то, будет ли создана константа с переданным именем, если ее не существует в указанной группе. Если указана Ложь и константа с требуемым именем в указанной группе не существует, будет вызвано соответствующее исключение. При параметре СоздаватьГруппуПриОтутствии установленном в Истину автоматически переводится в Истину, так как в новой группе точно нет никаких констант и если создаем группу, то надо создавать и константу.
- ДопускатьСменуТипаСуществующейКонстанты (Необязательный) - указывает на то, допускается ли для уже существующей константы установка значения типа, отличного от типа уже установленного значения. Если передана Истина и значение, отличающееся от типа значения уже существующей переменной по заданному пути, то будет вызвано соответствующее исключение.
- КакХранилище (Необязательный) - указывает на необходимость сохранения переданного значения как ХранилищеЗначения. Если установлено в Ложь, то значения примитивных и ссылочных типов будут сохранены как есть, а все остальные будут сохранены как ХранилищеЗначения. Если установить в Истину, то любое переданное значение будет сохранено как ХранилищеЗначения.
- СтепеньСжатияХранилища (Необязательный) - число от 0 до 9 - определяет степень сжатия данных при формировании ХранилищаЗначения.
Функция ПолучитьЗначениеКонстанты(ПутьККонстанте, КонтролироватьСуществование=Ложь,
ОткрытьХранилище=Ложь) Экспорт
Возвращает значение, которое хранится по указанному пути.
- ПутьККонстанте (Обязательный) - путь к константе в формате "ИмяГруппы.ИмяКонстанты". Допускаются любые строки. Единственное требование - ровно одна точка, разделяющая имя группы и имя константы.
- КонтролироватьСуществование (Необязательный) - признак того, надо ли вызывать исключение, если константы по указанному пути не существует. Если указана Ложь, то в случае отсутствия константы будет возвращено Неопределено.
- ОткрытьХранилище (Необязательный) - признак того, надо ли возвращать объект ХранилищеЗначения, или то, что лежит в хранилище значения. Если указана Истина и у получаемого значения тип ХранилищеЗначения, то функция вернет результат функции ХранилищеЗначения.Получить(). В противном случае вернется само ХранилищеЗначения.
И всё. Сохранять и получать значения становится настолько просто, что есть ненулевая вероятность заиграться и хранить в таком виде даже то, для чего есть либо специальные места (типа настроек форм и отчетов), либо то, что вообще хранить не следует, а следует провести лишний этап анализа с заказчиком. Поэтому следите за собой - не стоит превращать аккуратное хранилище в свалку.
А вот хранить допустимое количество дней отсрочки платежа, которое коммерческий директор любит менять по два раза в месяц - самое то. Или, например, дату, с которой надо начинать особенным образом обрабатывать реализации товаров, которые проходят через Меркурий, чтобы раньше времени всех не пугать. Или ссылку на какую-то конкретную внешнюю обработку, которая содержит нужные куски логики (или саму ее в виде двоичных данных, хотя, наверно, лучше не стоит). Или логотипы разные на все случаи жизни. Или строки подключения ко всем базам из вашего зоопарка. Да мало ли еще что.
Остальное
Разработано на версии платформы 8.3.8.1652 (из чувствительного к версиям используется СтрНайти - поменять на Найти несложно). Весь механизм уложен в рамки одного справочника - ДопКонстанты. Есть как управляемые так и обычные формы. Программный API (в модуле менеджера) обложен тестами и стабилен, хотя, честно признаюсь, в боевых условиях испытывал не все дополнительные параметры. Тесты можно позапускать с формы списка.
Занимательный факт
В коде конфигурации четырежды используется НайтиПоНаименованию :)