代码之家  ›  专栏  ›  技术社区  ›  user1884155

向java枚举添加子类别

  •  1
  • user1884155  · 技术社区  · 10 年前

    假设我有一个简单的Java枚举:

    public Enum itemType
    {
        FRUITS("fru"),
        VEGETABLES("veg"),
        LIQUOURS("liq"),
        SODAS("sod");
    
        private String dbCode;
    
        public ItemType(String dbCode){
            this.dbCode = dbCode;
        }
    
        public String getDbCode(){
            return this.dbCode;
        }
    }
    

    我现在想在这个枚举中引入一个“类别”,例如区分液体项目和固体项目。我在枚举类中找到了两种实现这一点的方法,如下所示。然而,两者都有相同的反模式:如果类别的数量或项目的数量增加/减少(想象100个项目类型有10个类别!),我有很多更新要做。我可以使用什么模式来尽可能干净和可重用地设计此枚举?

    第一种方法:向枚举添加其他属性

    public Enum itemType
    {
        FRUITS("fru",false),
        VEGETABLES("veg",false),
        LIQUOURS("liq",true),
        SODAS("sod",true);
    
        private String dbCode;
        private boolean liquid;
    
        public ItemType(String dbCode, boolean liquid){
            this.dbCode = dbCode;
            this.liquid = liquid;
        }
    
        public String getDbCode(){
            return this.dbCode;
        }
        public boolean isLiquid(){
            return this.liquid;
        }
    }
    

    第二种方法:使用静态方法询问子类别

    public Enum itemType
    {
        FRUITS("fru"),
        VEGETABLES("veg"),
        LIQUOURS("liq"),
        SODAS("sod");
    
        private String dbCode;
    
        public ItemType(String dbCode){
            this.dbCode = dbCode;
        }
    
        public String getDbCode(){
            return this.dbCode;
        }
    
        public static boolean isLiquid(ItemType type){
            switch(t){
                case SODA:
                case LIQOURS: return true;
                default: return false;
            }
    }
    
    4 回复  |  直到 10 年前
        1
  •  5
  •   user180100 user180100    10 年前

    如何使用 EnumSet 为此?

    public enum ItemType
    {
        FRUITS("fru"),
        VEGETABLES("veg"),
        LIQUOURS("liq"),
        SODAS("sod");
    
        public static final EnumSet<ItemType> LIQUIDS = EnumSet.of(LIQUOURS, SODAS);
    
        // ...
    }
    

    然后您可以使用 ItemType.LIQUIDS.contains(someItemType) 检查是否 someItemType 是一种“液体”。

        2
  •  3
  •   Mario    10 年前

    我会这样做:

    enum Category {
        LIQUID, SOLID;
    }
    
    enum ItemType {
        FRUITS("fru", SOLID),
        VEGETABLES("veg", SOLID),
        LIQUOURS("liq", LIQUID),
        SODAS("sod", LIQUID);
    
        private String dbCode;
        private Category category;
        public ItemType(String dbCode, Category category){
            this.dbCode = dbCode;
            this.category = category;
        }
    
        /* getters / setters */
    }
    

    例如,这将允许您添加新产品和类别(例如。 BUTANE("but", GAS) )而不必修改现有代码(如方法2中所发生的)。

    另一方面,如果类别和项目的数量很长且不断变化,我会考虑使用SQL数据库。

        3
  •  2
  •   Sergey Kalinichenko    10 年前

    因为您正在建模的东西没有可以用算法编码的逻辑(即没有算法可以计算出 "sod" 是液体 "veg" 不是)无法以某种方式枚举所有相关的(项目、类别)对。

    有三种实施方法:

    • 枚举项目侧的类别 -这是您的代码在这两种情况下所做的,或者
    • 枚举类别一侧的项目 -这将建立一个 enum 类别,并为每个类别附上完整的项目列表,或
    • 独立枚举项目+类别对 -当在数据库或配置文件中存储项目/类别映射时,这种方法可能很有用。

    我建议采用第三种方法,因为它是最“对称”的方法。为带有类别代码的类别创建一个表,并添加一个“交叉表”(或交叉文件),其中包含所有类别对及其对应的项目。启动时读取交叉表/文件,并设置双方的依赖关系。

    public Enum ItemType {
        FRUITS("fru")
    ,   VEGETABLES("veg")
    ,   LIQUOURS("liq")
    ,   SODAS("sod");
        public void addCategory(ItemCategory category) ...;
        public EnumSet<ItemCategory> getItemCategories() ...;
    }
    public Enum ItemCategory {
        LIQUIDS("liq")
    ,   SNACKS("snk")
    ,   FAST("fst");
        public void addItem(ItemType type) ...;
        public EnumSet<ItemType> getItemTypes() ...;
    }
    

    交叉文件或交叉表可能如下所示:

    liq liq
    sod liq
    fru snk
    fru fst
    sod fst
    

    通过枚举对并调用 addCategory 两人的 item 侧,并呼叫 addItem 两人的 category 一边

        4
  •  1
  •   YoYo    8 年前

    这是三个很好的答案,但我认为我可以将三个答案组合在一个很好:

    public enum ItemType {
        FRUITS("fru",PERISHABLE),
        VEGETABLES("veg",PERISHABLE),
        LIQUOURS("liq",LIQUIDS),
        SODAS("sod",LIQUIDS),
        FRESH_SQUEEZED_ORANGE_JUICE("orgj",LIQUIDS,PERISHABLE);
    
        private final String dbCode;
        private final EnumSet<ItemCategory> categories;
        private static final Map<ItemCategory,Set<ItemType>> INDEX_BY_CATEGORY = new EnumMap<>(ItemCategory.class);
    
        ItemType(String dbcode,ItemCategory... categories) {
          this.dbCode = dbcode;
          this.categories = EnumSet.copyOf(Arrays.asList(categories));
          //for (ItemCategory c:categories) {
          //  // Illegal Reference to Static Field!
          //  INDEX_BY_CATEGORY.put(c, this);
          //}
        }
    
        static {
          for (ItemCategory c:ItemCategory.values()) {
            INDEX_BY_CATEGORY.put(c, EnumSet.noneOf(ItemType.class));
          }
          for (ItemType t:values()) {
            for (ItemCategory c:t.categories) {
              INDEX_BY_CATEGORY.get(c).add(t);
            }
          }
        }
    
        public boolean is(ItemCategory c) {
          return INDEX_BY_CATEGORY.get(c).contains(this);
        }
    
        public Set<ItemType> getAll(ItemCategory c) {
          return EnumSet.copyOf(INDEX_BY_CATEGORY.get(c));
        }
    
        public String getDbCode() {
          return dbCode;
        }
    }
    

    现在

    • 我们可以很容易地询问其他子类别,而无需编写代码: boolean isVegetableLiquid = VEGETABLES.is(LIQUIDS);
    • 我们可以很容易地为一个项目分配不仅一个类别,而且多个类别 FRESH_SQUEEZED_ORANGE_JUICE .
    • 我们正在使用 EnumSet EnumMap 性能,包括它们的方法 contains .
    • 我们绝对在最小化添加附加项所需的代码量。通过数据库或配置进行设置,这可以进一步最小化。然而,在这种情况下,我们必须避免使用 Enum