【问题标题】:What is the right way to define constants when there is a huge number of constants?当有大量常量时,定义常量的正确方法是什么?
【发布时间】:2019-10-31 20:51:41
【问题描述】:

我正在编写一些需要定义大量常量的代码。它主要处理Marketplace 常量(可能是美国、英国、印度、日本)和关联的MarketplaceMerchantMapping,它基本上将MerchantID 映射到MarketplaceID

例如:

public enum Marketplace {

    US("US"),
    JP("JP"),
    UK("UK"),
    IN("IN"),
    NZ("NZ"),
    CA("CA"),
    FR("FR"),
    ... 
    ...

  // This could go up to some 400 marketplaces

    private final String stringValue;

    public boolean isWest() {
        return this == US || this == CA || this == UK;
    }

    public boolean isEast() {
        return this == IN || this == NZ || this == JP;
    }

}
public enum MarketplaceMerchantMapping {

    USMAP(MarketplaceID.US, MerchantID.US, Marketplace.US),
    JPMAP(MarketplaceID.JP, MerchantID.JP, Marketplace.JP),
    UKMAP(MarketplaceID.UK, MerchantID.UK, Marketplace.UK),
    NZMAP(MarketplaceID.NZ, MerchantID.NZ, Marketplace.NZ),
    INMAP(MarketplaceID.IN, MerchantID.IN, Marketplace.IN),
    CAMAP(MarketplaceID.CA, MerchantID.CA, Marketplace.CA),
    FRMAP(MarketplaceID.FR, MerchantID.FR, Marketplace.FR),
    ...
    ... 

   // THis can go up to 400 Marketplaces * Number of merchantIds in each marketplace.


}

还有其他类似的常量定义为枚举或静态常量。

这确实是不可扩展的,因为每次我们添加对新市场和商家的支持时,我们都需要更新大量文件并对其进行测试,而这些配置更改本身会占用大量开发人员时间。

理想情况下,我想知道是否有某种方法可以在某个配置文件中定义这些常量并读取这些文件以创建常量。有没有办法通过读取和解析一些配置文件来创建这样的枚举常量?

如果我有一个包含以下条目的配置文件:

配置文件.cfg:

WestMarketplaces = ("US", "AG", "MX", "CA", ...) // Expand this later as required
EastMarketplaces = ("IN", "AU", "SG", "JP", ...) // Expand this later as required
EUMarketplaces = ("UK", "FR", "SP", ...) // Expand this later as required

WestMerchantIds = ("WA", "WB", "WC", "WD", ...) // Expand this later as required
WestMerchantIds = ("EA", "EB", "EC", "WD", ...) // Expand this later as required
EUMerchantIds = ("EUA", "EUB", "EUC", "WDD", ...) // Expand this later as required

marketplaceMerchantMapping = {
  "US" = "WA";
  "CA" = "WB";
  "MX" = "WC";
  "AU" = "EA";
  "IN" = "EB";
  "JP" = "EC";
  "UK" = "EUA";
  "FR" = "EUB";
...
} // Expand this later as required

那么,它可以从配置文件中读取这些常量并构建适当的枚举或静态常量吗?

这可能吗?

【问题讨论】:

  • 这可以达到 400 个 Marketplaces * 商家 ID 的数量...持续存在数据存储区,有什么害处?
  • 如果所有值在编译时都已知,则使用枚举。如果您打算稍后扩展列表,则不应使用枚举。
  • 哇哦!我们有接近相同的东西,但非常接近。我们所做的是构建一个很小的 ​​xsd 模式并从中生成 xml 文件,并确实读取属性文件并映射它们……好吧,您将它们映射到枚举,我们将它们映射到 BitSets。例如,像美国这样的某个市场是一个由 52 个州组成的 BitSet——取决于我们需要在哪些州做不同的事情——我们应用一个掩码……虽然在评论中解释起来有点复杂。
  • @Naman 的建议很棒,但 OP 可能也适合我 - 我们没有 应用程序的这一部分 的数据存储 - 真的不想要介绍一个
  • 当然可以,但是您使用它的方式决定了代码的实际外观。如果这些枚举常量没有为封装的字符串增加任何价值,甚至应该是可扩展的,那么与仅使用包含两个字母代码的字符串常量相比,该提议有什么实际优势?

标签: java enums config constants configuration-files


【解决方案1】:

枚举必须在编译时“已知”。所以只要你使用 Enums (我知道你因为类型安全、IDE 支持等原因愿意使用它们) 您必须编写它们,如果它们中有很多相互连接,这确实很乏味。

现在你要求的是(如果我理解正确的话):

  • 我想定义一个配置文件 (json/yaml) 随便
  • 基于此文件,我需要创建“某物”以在运行时使用。

我在这里建议一种相当不普及的方法(至少根据我的经验),但这肯定有助于完成工作:

想法 1:如果它很乏味,为什么不自动化呢?

想法 2如果您喜欢使用枚举,为什么不创建枚举?

所以基本上我建议在构建时生成代码(并且生成枚举很简单,因为它们没有任何实际逻辑要完成)。

所以如果你决定走这条路,你需要做几件事:

  • 确保在编译实际类之前完成代码生成,否则您将无法在类中使用自动生成的代码。

  • 确保正确生成代码。现在在过去这是一个非常痛苦的过程,但现在有一个不错的小库,名为 Java Poet,它使代码生成变得非常简单。

  • 确保此方法将与您的构建工具集成。例如,如果您使用 maven,请考虑创建一个 maven 插件,该插件将触发代码生成过程并将其保存在 target/generated-sources 中。然后将其附加到generated-sources 阶段(显然在compile 阶段之前运行),当Java 编译器开始编译您的类时,生成的源代码将无法区分。

为了给你一个例子,我可以向你推荐我最近为一个非常相似的问题做的一个宠物项目(如果这听起来像是一个自我广告,我很抱歉,我从来没有“推销自己”所以,我这样做是因为我真的相信从技术上讲,如果你决定走这条路,这会有所帮助,因为从技术上讲,它包含所有粘合在一起的部分):

所以我的目标是自动化异常创建(可以说比枚举生成稍微复杂一些),就像你一样,我认为创建一些配置 yaml 文件是一个好主意,将有关异常的信息(traceIds ,消息,参数等)并让构建过程创建实际的Java异常代码。

源代码在 Github 上一个名为 Simplex(简单异常处理库)的项目中可用。它有 maven 插件供参考,使用 Java Poet 的生成器等。

【讨论】:

  • 谢谢。对此进行调查。
  • +1 用于建议代码生成。我还将代码生成部分添加为子模块,以便您可以对其进行版本控制并独立从其他项目中提取它,这样这些项目就不必担心使用过时的版本(yay versioning)或处理代码生成管道。
  • 顺便说一句,感谢您分享示例代码。让你更容易理解你在说什么。
  • @SunilKumar,当然,不客气,如果您对此有任何疑问,请告诉我 - 我会尽力监控此线程...
【解决方案2】:

当无法使用数据库时,维护良好 XLS 表可能是下一个最佳选择。连同经过良好测试的获取工作表数据的过程,以从中生成代码,或用作某种属性配置输入。

正如 OP 自己认为的那样:当需要维护如此大量的“关键”属性时,Java 源代码并不理想,但更重要的是,需要定期更新。

当然,这里的关键方面是:维护良好。每个人都必须遵循一个明确定义的过程,并且整个“链”,从对 XLS 进行更改(理想情况下以版本控制的方式),到生成构建时所需的工件应该简单且经过良好测试。

【讨论】:

  • 谢谢。我会调查一下。但我想知道是否有更好的方法。如果不是,这可能是最好的方法。
  • @oleg.cherednik 这在很大程度上取决于。一个设计精美的表格,无论是 MS office,还是 Libre,或者其他什么……我认为负责更新此类业务相关信息的“业务”人员……有些事情告诉我,业务人员会更喜欢可视化工具。理想情况下,有一些宏可以防止误用,而且还可以轻松排序等。 YAML ... 对业务人员来说就像 Java 代码:他们不想看的胡言乱语。更糟糕的是:您不希望不了解文件布局的人在文本编辑器中进行编辑!
  • @GhostCat 我们一起工作吗?我不知道吗? 负责更新此类业务相关信息... 正是为什么不为我们使用yaml
  • @Eugene 你知道,伟大的思想家会一起思考。但老实说:这是常识。我已经看到当人们被“启用”以编辑以某种方式改变系统行为的文本文件时会发生什么。而且这并不总是一个好的体验......
  • @GhostCat 非常感谢您提供详细的 cmets。真的很有帮助。
【解决方案3】:

如果你只是想以更易读的方式编写枚举,你可以为它们添加一个属性:

public enum Marketplace {
     US("US", false, true),
     JP("JP", true, false),
     UK("UK", false, true),
     IN("IN", true, false),
     NZ("NZ", true, false),
     CA("CA", false, true),
     FR("FR", false, false);

     // This could go uptp some 400 marketplaces

     private final String stringValue;

     /** Markers for the getters */
     private final boolean isWest, isEast;

     Marketplace(String stringValue, boolean isEast, boolean isWest) {
         this.stringValue = stringValue;
         this.isEast = isEast;
         this.isWest = isWest;
     }

     public boolean isWest() {
         return isWest;
     }

     public boolean isEast() {
         return isEast;
     }
}

从技术上讲,如果您将其设为枚举的属性,您可以轻松地将它们保存在数据库中。如果您没有可用的数据存储,则必须在源代码中维护它们。

【讨论】:

  • 不是很有帮助,但我也想知道为什么有人会否决它...
  • 这不是我想要的。但我不确定为什么有人对此表示反对。不过谢谢。
  • 我对此投了反对票,因为它没有回答问题
猜你喜欢
  • 1970-01-01
  • 2011-01-12
  • 2011-06-27
  • 1970-01-01
  • 2018-11-05
  • 1970-01-01
  • 2011-10-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多