【问题标题】:String or enum as parameter to refer to model fields字符串或枚举作为参数来引用模型字段
【发布时间】:2013-12-13 11:55:44
【问题描述】:

我有一个模型有 5 个字段,包括 getter 和 setter,比如 id、name、age、dob、city

服务类包含一个方法,该方法只能以数组的形式返回特定字段数据。就像我只想要所有人的姓名和年龄一样。

List<Object> getEmpDataAsList(List<Object> fields);

所以我的方法看起来像这样,它需要一个参数fields,其中包含需要返回的字段列表。因此,对于这种情况,我应该只将 nameage 发送到 fields 参数,我的 getEmpDataAsList 会检测到请求的字段并相应地构建一个返回数组。

我想知道最好的方法是什么。我是否应该将fields List 作为我目前也在做的字符串传递,例如:

List<String> fields = new ArrayList<>();
fields.add("name");
fields.add("age");

getEmpDataAsList(fields);

但我认为,这并不安全。如果我打算在某一天更改一个字段的名称,或者如果我从模型中删除一个字段怎么办,那么我需要在 3 个地方进行更改。我应该在模型类中创建一个枚举并使用枚举字段来创建相同的fields 数组并使用它吗?或者还有其他更好的方法吗?

【问题讨论】:

  • 服务getEmpDataAsList(List&lt;Object&gt;) 是否已提供或允许更改?
  • @Chris Ya 我已经完成了这个项目,我可以随时更改它。

标签: java design-patterns enums


【解决方案1】:

如果我计划某天更改某个字段的名称或删除某个字段会怎样 来自模型的字段,那么我需要在 3 个地方进行更改。

这个问题没有解决办法。如果你删除一个字段,它就消失了。您必须将其从模型和客户端中删除。但这里有一个快速的解决方案,可以让更改变得明显,这样更改 API 会导致编译错误。

在抽象父类中使用泛型在编译时检查参数列表:

public abstract class GenericDataInterface<KEY extends Enum> {
    public abstract Object getData(KEY key);

    public final List<Object> getEmpDataAsList(List<KEY> fields) {
        List<Object> result = new ArrayList<Object>(fields.size());
        for (KEY field : fields) {
            result.add(getData(field));
        }
        return result;
    }
}

示例模型类:

public class Pizza extends GenericDataInterface<Pizza.PizzaAttributes> {
    public enum PizzaAttributes { NAME, TOPPINGS, PRICE };

    private String name;
    private Set<String> toppings;
    // WARNING: Do not use Float for prices in real applications!
    private float price;

    @Override
    public Object getData(PizzaAttributes key) {
        switch (key) {
            case NAME:
                return name;
            case TOPPINGS:
                return toppings;
            case PRICE:
                return price;
            default:
                throw new IllegalArgumentException(
                        "Key " + key + " isn't defined for " + Pizza.class.getCanonicalName());
        }
    }
}

结果还不是很方便,因为顺序扭曲会导致看不见的问题。因此,您可以使用地图作为结果

public abstract class GenericDataInterface<KEY extends Enum> {
    public abstract Object getData(KEY key);

    public final Map<KEY, Object> getEmpDataAsList(List<KEY> fields) {
        Map<KEY, Object> result = new HashMap<KEY, Object>(fields.size());
        for (KEY field : fields) {
            result.put(field, getData(field));
        }
        return result;
    }
}

您也可以使用真实模型类的克隆来代替 Map,并仅填充指定的属性。

进一步的想法:

  • 您真的需要这样的通用接口吗?
  • 如果是网络通信,不能用XML或者JSON吗?

【讨论】:

  • 您不应该使用原始类型KEY extends Enum。要么将其键入到某处,要么将其保留为未知 Enum&lt;?&gt;,但不是原始类型。
  • @Bohemian:为什么?无论如何,您永远不会手动从 Enum 继承。
  • 因为 Enum 是一个泛型类。绑定枚举的正确方法是&lt;KEY extends Enum&lt;KEY&gt;&gt;
  • 我喜欢你模型的getData() 方法,它非常适合场景。但我真的不明白创建GenericDataInterface 有什么需要。 dkatzel 回答不是更干净吗,因为最终目的是提供一个 API,如果对模型进行任何更改,就会导致编译错误,并且还让开发人员知道他可以在此方法调用中使用哪些字段?而且我不是在寻找网络通信。请帮助我了解GenericDataInterface 的需要,它将如何提供更多帮助?
  • @Ahbi:我假设你有不止一个模型类,所以你可以重用GenericDataInterface 中的一些代码。 getEmpDataAsList 可能已经是接口或抽象类的一部分,所以这应该不是问题。 dkatzels 的答案只是我的一个更简单的版本。我的也会导致编译错误。使用 dkatzerls 解决方案,您不能拥有一个通用接口,因为每个模型类都有不同的枚举作为参数。
【解决方案2】:

您应该为不同的字段类型使用枚举。 (虽然,我不知道 AGE 和 DOB 是否都需要,因为您可以从当前日期和 DOB 计算年龄......)

public enum Fields{
  NAME,
  DOB,
  CITY
   ...


 }

通过这种方式,您可以获得更能揭示意图的 API。

List<Object> getEmpDataAsList(Fields...fields)

List<Object> getEmpDataAsList(Set<Fields> fields)

这会让你做类似的事情

results = getEmpDataAsList(Fields.AGE, Fields.DOB);

results = getEmpDataAsList(EnumSet.of(Fields.AGE, Fields.DOB));

如果您完全控制 API,我建议您还将返回类型从 List 更改为 Map,这样您就可以知道哪个字段的值是什么,而无需知道或关心输入字段的顺序。

Map<Fields,Object> getEmpData(Fields...fields)

如果我计划某天更改某个字段的名称,或者如果我从模型中删除一个字段怎么办,那么我需要在 3 个地方进行更改。

使用枚举,如果您重命名该枚举,IDE 可以为您自动重命名所有引用。如果你删除一个枚举,你会得到一个编译器错误,所以你知道需要改变什么。

【讨论】:

  • 非常感谢,我真的很喜欢这种简单性,我将在我目前的情况下使用它:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-03
  • 1970-01-01
相关资源
最近更新 更多