Я думаю, что этот вопрос должен относиться к большинству программ, которые загружают настройки из файла. Мой вопрос с точки зрения программирования, и это действительно, как справиться с загрузкой настроек из файла с точки зрения различных классов и доступности. Например:
- Если у программы был простой
settings.ini
файл, следует ли загружать его содержимое вload()
метод класса или, возможно, в конструктор? - Должны ли значения храниться в
public static
переменных или должны бытьstatic
методы для получения и установки свойств? - Что должно произойти, если файл не существует или не читается? Как бы вы сообщили остальной части программы, что она не может получить эти свойства?
- и т.п.
Я надеюсь, что я спрашиваю это в нужном месте здесь. Я хотел сделать вопрос как можно более независимым от языка, но я в основном сосредоточился на языках, имеющих такие вещи, как наследование, особенно на Java и C # .NET.
Ответы:
На самом деле это действительно важный вопрос, и он часто делается неправильно, поскольку ему не уделяется достаточного внимания, хотя он является основной частью практически каждого приложения. Вот мои рекомендации:
Ваш конфигурационный класс, который содержит все настройки, должен быть просто старого типа данных struct / class:
Он не должен иметь методов и не должен включать наследование (если это не единственный выбор, который у вас есть на вашем языке для реализации варианта поля - см. Следующий параграф). Он может и должен использовать композицию для группировки настроек в более мелкие конкретные классы конфигурации (например, subConfig выше). Если вы сделаете это таким образом, то будет идеальным переходить в модульных тестах и в приложении в целом, поскольку оно будет иметь минимальные зависимости.
Скорее всего, вам придется использовать типы вариантов, в случае, если конфигурации для разных настроек неоднородны по структуре. Принято считать, что вам нужно поместить динамическое приведение в какой-то момент, когда вы прочитаете значение, чтобы привести его к нужному (под) классу конфигурации, и, без сомнения, это будет зависеть от другого параметра конфигурации.
Вам не следует лениться вводить все настройки в виде полей, просто выполнив это:
Это заманчиво, так как это означает, что вы можете написать обобщенный класс сериализации, которому не нужно знать, с какими полями он имеет дело, но это неправильно, и я объясню почему через минуту.
Сериализация конфига производится в совершенно отдельном классе. Какой бы API или библиотеку вы не использовали для этого, тело вашей функции сериализации должно содержать записи, которые в основном равносильны отображению пути / ключа в файле к полю на объекте. Некоторые языки обеспечивают хороший самоанализ и могут сделать это для вас из коробки, другие вам придется явно написать отображение, но главное, что вам нужно будет написать отображение только один раз. Например, рассмотрим этот фрагмент, который я адаптировал из документации синтаксического анализатора опций программы c ++ boost:
Обратите внимание, что в последней строке в основном говорится, что «оптимизация» отображается на Config :: opt, а также что есть объявление того типа, который вы ожидаете. Вы хотите, чтобы чтение конфигурации не выполнялось, если тип не соответствует ожидаемому, если параметр в файле на самом деле не является float или int или не существует. Т.е. сбой должен произойти, когда вы читаете файл, потому что проблема связана с форматом / проверкой файла, и вы должны выбросить код исключения / возврата и сообщить точную проблему. Вы не должны откладывать это позже в программе. Вот почему у вас не должно быть соблазна перехватить весь словарь в стиле Conf, как упомянуто выше, который не потерпит неудачу при чтении файла - поскольку приведение откладывается до тех пор, пока не понадобится значение.
Вы должны сделать класс Config доступным только для чтения некоторым способом - устанавливая содержимое класса один раз, когда вы создаете его и инициализируете его из файла. Если вам необходимо иметь динамические настройки в вашем приложении, которые изменяются, а также постоянные, которые этого не делают, у вас должен быть отдельный класс для обработки динамических, а не пытаться разрешить битам вашего класса конфигурации быть не только для чтения ,
В идеале вы читаете файл в одном месте вашей программы, т.е. у вас есть только один экземпляр "
ConfigReader
". Однако, если вы боретесь с передачей экземпляра Config туда, где вам это нужно, лучше иметь второй ConfigReader, чем вводить глобальный конфиг (который, я предполагаю, означает, что OP означает «статический» "), что подводит меня к следующему пункту:Избегайте соблазнительной сирены: «Я избавлю вас от необходимости проходить этот урок, все ваши конструкторы будут красивыми и чистыми. Продолжайте, это будет так просто». Правда в том, что с хорошо разработанной тестируемой архитектурой вам вряд ли понадобится проходить класс Config или его части через множество классов вашего приложения. То, что вы найдете в своем классе верхнего уровня, своей функции main () или что-то еще, вы распутаете conf в отдельные значения, которые вы предоставите своим классам компонентов в качестве аргументов, которые вы затем соберете вместе (зависимость вручную) инъекции). Одноэтапный / глобальный / статический conf значительно усложнит реализацию и понимание модульного тестирования вашего приложения - например, он запутает новых разработчиков в вашей команде, которые не будут знать, что им нужно устанавливать глобальное состояние для тестирования.
Если ваш язык поддерживает свойства, вы должны использовать их для этой цели. Причина в том, что это означает, что будет очень легко добавить «производные» параметры конфигурации, которые зависят от одного или нескольких других параметров. например
Если ваш язык изначально не поддерживает идиому свойства, возможно, для достижения того же эффекта есть обходной путь, или вы просто создаете класс-оболочку, который предоставляет настройки бонусов. Если вы не можете иным образом использовать преимущества свойств, в противном случае это будет пустой тратой времени на написание вручную и использование методов получения / установки просто для того, чтобы угодить некоему ОО-богу. Вам будет лучше с простым старым полем.
Вам может понадобиться система для объединения и получения нескольких конфигов из разных мест в порядке приоритета. Этот порядок приоритета должен быть четко определен и понятен всем разработчикам / пользователям, например, рассмотреть реестр Windows HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE. Вы должны сделать этот функциональный стиль, чтобы ваши конфиги оставались только для чтения, т.е.
скорее, чем:
Наконец, я должен добавить, что, конечно, если выбранный вами фреймворк / язык предоставляет собственные встроенные, хорошо известные механизмы конфигурации, вы должны рассмотреть преимущества использования этого вместо того, чтобы использовать свой собственный.
Так. Множество аспектов для рассмотрения - сделайте это правильно, и это окажет глубокое влияние на архитектуру вашего приложения, уменьшая количество ошибок, делая вещи легко тестируемыми и заставляя вас использовать хороший дизайн в другом месте.
источник
.ini
чтобы он был удобочитаемым для человека, но вы предлагаете мне сериализовать класс с переменными в?В общем (на мой взгляд) лучше всего позволить приложению разобраться с тем, как хранится конфигурация, и передать конфигурацию в ваши модули. Это обеспечивает гибкость в том , как настройки будут сохранены , так что вы можете ориентировать файлы или веб - сервисы или базы данных или ...
Кроме того, на приложение ложится бремя «что происходит, когда что-то не получается», кто лучше знает, что означает этот сбой.
И это значительно упрощает модульное тестирование, когда вы можете просто передать объект конфигурации, а не обращаться к файловой системе или решать проблемы параллелизма, возникающие при статическом доступе.
источник
Если вы используете .NET для программирования классов, у вас есть разные опции, такие как Resources, web.config или даже пользовательский файл.
Если вы используете Resources или web.config, данные на самом деле хранятся в файле XML, но загрузка происходит быстрее.
Извлечение данных из этих файлов и хранение их в другом месте будет похоже на двойное использование памяти, поскольку они загружаются в память по умолчанию.
Для любого другого файла или языка программирования ответ выше Бенедикт будет работать.
источник