Подтвердить что ты не робот

Шаблон проектирования для одноразовых загруженных свойств конфигурации?

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

По существу, мое текущее решение состоит из трех шагов.

  • Создайте синглтон. Поскольку данные постоянны и гарантированно не изменяются во время выполнения приложения, требуется только один экземпляр объекта.

  • Когда выполняется первый запрос для объекта, создайте объект и прочитайте его из файла.

  • Экспортировать данные с помощью геттеров.

Это приводит к тому, что мой код выглядит так: MyConfiguration.getInstance().getWeightOfBomb(), что выглядит довольно странно для меня.

Есть ли лучший способ справиться с этим более семантическим способом?

4b9b3361

Ответ 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
    }
}