Шаблон проектирования для одноразовых загруженных свойств конфигурации?
Я часто сталкиваюсь с проблемой хранения в памяти нескольких (возможно сложных) параметров конфигурации, загружаемых из файлов в файловой системе. Мне интересно, есть ли лучший способ архивировать шаблон для этой проблемы, чем то, что я использовал.
По существу, мое текущее решение состоит из трех шагов.
-
Создайте синглтон. Поскольку данные постоянны и гарантированно не изменяются во время выполнения приложения, требуется только один экземпляр объекта.
-
Когда выполняется первый запрос для объекта, создайте объект и прочитайте его из файла.
-
Экспортировать данные с помощью геттеров.
Это приводит к тому, что мой код выглядит так:
MyConfiguration.getInstance().getWeightOfBomb()
, что выглядит довольно странно для меня.
Есть ли лучший способ справиться с этим более семантическим способом?
Ответы
Ответ 1
Инъекция зависимостей. Вам необязательно использовать инфраструктуру DI, например Spring или Guice, но вы действительно хотите избежать засорения вашего кода синглонами. Вы все еще можете использовать синглтон в реализации, но нет причин, по которым остальная часть вашего кода должна знать, что это синглтон. Синглтоны - огромная боль при модульном тестировании и рефакторинге. Пусть ваш код ссылается на интерфейс. например.
interface MyConfig {
double getWeightOfBomb();
}
class SomeClass {
private MyConfig myConfig;
public void doSomething() {
myConfig.getWeightOfBomb();
}
}
class MyConfigImpl implements MyConfig {
public double getWeightOfBomb() {
return MyConfiguration.getInstance().getWeightOfBomb();
}
}
Если вы используете инфраструктуру DI, просто настройте классы, чтобы ваша реализация MyConfig
была введена. Если вы этого не сделаете, то самый ленивый подход, который все еще имеет все преимущества, - это сделать что-то вроде:
class SomeClass {
private MyConfig myConfig = new MyConfigImpl();
}
На самом деле это вам. Важно то, что вы можете заменить MyConfig
на каждый экземпляр, когда позже понимаете, что вам нужно, чтобы поведение менялось и/или для модульного тестирования.
Ответ 2
Дополнительное предложение для ответа noah.
Если вам неудобно писать метод для каждого параметра конфигурации, вы можете использовать для этого перечисление. Вот что я имею в виду:
public class Configuration {
private final Properties properties;
public enum Parameter {
MY_PARAMETER1("my.parameter1", "value1"),
MY_PARAMETER2("my.parameter2", "value2");
private final String name;
private final String defaultValue;
private Parameter(String name, String defaultValue) {
this.name = name;
this.defaultValue = defaultValue;
}
private String getName() {
return name;
}
private String getDefaultValue() {
return defaultValue;
}
}
public Configuration(Properties properties) {
this.properties = (Properties)properties.clone();
}
//single method for every configuration parameter
public String get(Parameter param) {
return properties.getProperty(param.getName(), param.getDefaultValue());
}
}
После этого, если у вас есть новый параметр конфигурации, все, что вам нужно сделать, это добавить новую запись перечисления.
Вы также можете извлечь интерфейс из класса Configuration, конечно же, перемещая перемычку снаружи.
Ответ 3
Вы можете создать интерфейс для представления конфигурации:
public interface Config {
interface Key {}
String get(Key key);
String get(Key key, String defaultValue);
}
И реализация singleton:
public enum MyConfig implements Config {
INSTANCE("/config.properties");
private final Properties config;
MyConfig(String path) {
config = new Properties();
try {
config.load(this.getClass().getResourceAsStream(path));
} catch (IOException | NullPointerException e) {
throw new ExceptionInInitializerError(e);
}
}
@Override
public String get(Config.Key key){
return config.getProperty(key.toString());
}
@Override
public String get(Config.Key key, String defaultValue) {
return config.getProperty(key.toString(), defaultValue);
}
public enum Key implements Config.Key {
PROXY_HOST("proxy.host"),
PROXY_PORT("proxy.port");
private final String name;
Key(String name) { this.name = name; }
@Override
public String toString() { return name; }
}
}
И затем введите конфигурацию в свои классы:
public class SomeClass {
private final Config config;
public SomeClass(Config config) {
this.config = config;
}
public void someMethod() {
String host = config.get(Key.PROXY_HOST);
String port = config.get(Key.PROXY_PORT, "8080");
// Do something
}
}