java 一次性加载配置属性的设计模式?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2503489/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 21:31:29  来源:igfitidea点击:

Design Pattern for one-time loaded configuration properties?

javadesign-patternsconfiguration

提问by Stefan Kendall

I'm often faced with the problem of storing, in memory, a few (possibly complex) configuration settings loaded from files on the filesystem. I'm wondering if there's a better way to architect a pattern to this problem, however, than what I've been using.

我经常面临在内存中存储从文件系统上的文件加载的一些(可能是复杂的)配置设置的问题。我想知道是否有比我一直在使用的更好的方法来为这个问题构建一个模式。

Essentially, my current solution involves three steps.

本质上,我目前的解决方案包括三个步骤。

  1. Build a singleton. Since data is persistent and guaranteed not to change through the runtime of the application, only one object instance should ever be needed.

  2. When the first request for the object is made, create the object and read in from a file.

  3. Expose data with getters.

  1. 构建一个单例。由于数据是持久的并且保证在应用程序运行时不会改变,因此只需要一个对象实例。

  2. 当第一次请求对象时,创建对象并从文件中读入。

  3. 使用 getter 公开数据。

This has the effect that a lot of my code looks like this: MyConfiguration.getInstance().getWeightOfBomb(), which looks rather odd to me.

这会导致我的很多代码看起来像这样: MyConfiguration.getInstance().getWeightOfBomb(),这对我来说看起来很奇怪。

Is there a better way to handle this in a more semantic fashion?

有没有更好的方法以更语义化的方式处理这个问题?

回答by noah

Dependency Injection. You don't necessarily have to use a DI framework like Springor Guicebut you really want to avoid littering your code with singletons. You can still use a singleton in the implementation, but there is no reason the rest of your code needs to know that it is a singleton. Singletons are huge pain when unit testing and refactoring. Let your code reference an interface instead. e.g.,

依赖注入。您不一定必须使用像SpringGuice这样的 DI 框架,但您确实希望避免将代码与单例混在一起。您仍然可以在实现中使用单例,但是您的其余代码没有理由需要知道它是单例。在单元测试和重构时,单例是巨大的痛苦。让您的代码引用一个接口。例如,

 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(); 
     }
 }

If you use a DI framework, just setup you classes to have your MyConfigimplementation injected. If you don't, then the laziest approach that still has all the benefits is to do something like:

如果您使用 DI 框架,只需设置您的类即可MyConfig注入您的实现。如果不这样做,那么仍然具有所有好处的最懒惰的方法是执行以下操作:

 class SomeClass {
    private MyConfig myConfig = new MyConfigImpl();        
 }

Really it's up to you. The important thing is that you can replace myConfigon a per instance basis when you later realize that you need the behavior to vary and/or for unit testing.

这真的取决于你。重要的是,myConfig当您稍后意识到需要改变行为和/或进行单元测试时,您可以在每个实例的基础上进行替换。

回答by albur

You could create an interface to represent the configuration:

您可以创建一个接口来表示配置:

public interface Config {
    interface Key {}
    String get(Key key);
    String get(Key key, String defaultValue);
}

And a singleton implementation:

还有一个单例实现:

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; }
    }
}

And then inject the configuration in your classes:

然后在你的类中注入配置:

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
    }
}

回答by weekens

Additional proposal for noah's answer.

诺亚回答的额外建议。

If it is inconvenient for you to write a method for each configuration parameter, then you could use enum for that. Here is what I mean:

如果您不方便为每个配置参数编写一个方法,那么您可以使用 enum 。这就是我的意思:

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());
}

}

}

After that, if you have a new configuration parameter, all you need to do is to add a new enum entry.

之后,如果您有一个新的配置参数,您需要做的就是添加一个新的枚举条目。

You can also extract an interface from Configuration class, of course, moving enum outside.

你也可以从 Configuration 类中提取一个接口,当然,把 enum 移到外面。