【问题标题】:Design - Global Settings from Consumer Module设计 - 消费者模块的全局设置
【发布时间】:2019-03-30 19:55:20
【问题描述】:

我正在尝试创建一个库。我的代码中有一些常量,它们是某种不同的时间延迟和代码中的某些字符串值。我可以将它们提取到一个单独的常量类中以获得类似

Delays.LENGTH_SHORT(看起来更干净)

它们链接到库代码中的许多不同类。

现在,手头的问题是使其可配置,从调用消费者(即,如果消费者提供值,则使用这些值,否则使用预设值)。从消费者端,我决定使用 Builder 模式来创建配置并将其传递到库模块的 init 中(这只会在它的生命周期中发生一次)。

有没有办法在保留上述语法的同时接受消费者的配置(这些设置在init 期间只配置一次,在所有其他时间,它完全表现为常量)?

从文件中读取,似乎很昂贵。

【问题讨论】:

  • 您的库是否应该支持其他应用程序实现?否则,我建议使用SharedPreferences 来持久保存字符串和持续时间。尽管您也可以使用 /res/values/Strings.xml 来保留您的默认字符串值。
  • 该库应该被应用程序用于某些功能。在该消费中,应用程序可以通过这些设置配置init 期间的某些使用行为。这些设置设置一次,然后在库中的许多独立类中使用。 SharedPreferences 也将类似于文件 R/W。如果设置不同于默认设置(客户端配置),strings.xml 将不允许在同一位置进行覆盖,并且需要在所有使用点重新检查。不是这样吗?我错过了什么吗?

标签: java android module configuration


【解决方案1】:

对于只能从一组固定值中获取值的常量,使用 Java 枚举而不是整数、字符串或其他原始数据类型总是更好。在一段时间内,它们更易于理解和维护。理想情况下,应从属性文件中读取默认值以对其进行初始化。但是正如您在案例中提到的那样,出于性能原因,您希望避免从文件中读取的成本。设计问题总是开放式的,可以有多种方法。我推荐的一种方法如下:

public interface Configuration {
    public Continent getContinent(); //For fixed set of values use enum
    public Integer getPoolSize(); //If the config can take any value then use the corresponding data type directly
    public String getDefaultLabel();
}

public enum Continent {
    ANTARTICA, AFRICA, ASIA, AUSTRALIA, EUROPE, NORTH_AMERICA, SOUTH_AMERICA;
}

public class ConfigurationBuilder {

    private DefaultConfiguration configurationInstance;

    private class DefaultConfiguration implements Configuration {

        //Ideally the below values should be read from a property file, instead of hard coding it here.
        private Integer poolSize = Integer.valueOf(50);
        private String defaultLabel = "DEFAULT";
        private Continent continent = Continent.ASIA;

        @Override
        public Continent getContinent() {
            return continent;
        }

        @Override
        public Integer getPoolSize() {
            return poolSize;
        }

        @Override
        public String getDefaultLabel() {
            return defaultLabel;
        }
    }

    public ConfigurationBuilder withContinent(Continent continent) {
        this.configurationInstance.continent = continent;
        return this;
    }

    public ConfigurationBuilder withPoolSize(Integer poolSize) {
        this.configurationInstance.poolSize = poolSize;
        return this;
    }

    public ConfigurationBuilder withDefaultLabel(String defaultLabel) {
        this.configurationInstance.defaultLabel = defaultLabel;
        return this;
    }

    public Configuration build() {
        return this.configurationInstance;
    }

    public ConfigurationBuilder() {
        this.configurationInstance = new DefaultConfiguration();
    }

    public static Configuration buildDefaultConfiguration() {
        return new ConfigurationBuilder().build();
    }
}

public class Library {
    private Configuration configuration;
    public void init(Configuration configuration) {
        this.configuration = configuration;
    }
    public void init() {
        this.configuration = ConfigurationBuilder.buildDefaultConfiguration();
    }
    private Library(Configuration config) {
        this.init(config);
    }
    private Library() {
        this.init();
    }

    /**
     * Library is not singleton here.
     * 
     */
    public static Library getInstance(Configuration configuration) {
        return new Library(configuration);
    }
    public static Library getInstance() {
        return new Library();
    }
}

public class Client {
    public static void main(String args[]) {
        Configuration config = new ConfigurationBuilder()
                .withContinent(Continent.AFRICA)
                .withPoolSize(20)
                .withDefaultLabel("Label")
                .build();
        Library lib = Library.getInstance();
        lib.init(config);
    }
}

请检查库和客户端类的使用情况。 - 它使用 Builder 模式。 - 它具有 init() 和 init(Configuration) 方法以允许完全依赖库默认值。 - ConfigurationBuilder 支持提供部分或全部配置值以覆盖 - 目前所有三个配置选项都是可覆盖的——continent、poolSize 和 defaultLabel。但是,如果某些配置是 Library 私有的,那么只需从 Builder 中删除该属性的 withXXX 方法。

希望这符合您的需要。好问题!

【讨论】:

  • 感谢您提供宝贵意见。他们很有帮助。只是为了澄清,常量可以从消费应用程序客户端获取“任何”值(不是固定集)。 (在库范围内,它们仅对一个值进行操作,无论是股票还是客户提供的,并且时间在初始化),您在示例代码实现中也提到了这一点。该代码似乎很有帮助。有没有办法在没有 DI 的情况下跨库代码使用配置设置?
  • 很高兴这个答案很有用。关于在没有 DI 的情况下跨库代码使用配置设置的后续问题,据我了解,一旦使用配置实例初始化库实例,就可以通过库类中的 getConfiguration 等 getter 方法公开此配置。配置实例在这里是一个不可变对象,因此可以安全地公开它,而不会有任何人修改它的风险。我没有得到DI部分,你的意思是避免在客户端和库之间共享配置接口的依赖?
  • 我用 dagger2 实现了访问部分(仍然想进一步去中心化)。手头的问题是不要让库中的类与“库”交互以获取与这些类相关的配置,而不是与“库”类相关的配置。
猜你喜欢
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-20
  • 2016-04-26
  • 1970-01-01
相关资源
最近更新 更多