Java 字符串或枚举作为引用模型字段的参数

Java 字符串或枚举作为引用模型字段的参数,java,design-patterns,enums,Java,Design Patterns,Enums,我有一个模型,它有5个字段,分别是getter和setter,比如id、name、age、dob、city 服务类包含一个方法,该方法绑定为仅返回数组形式的特定字段数据。就像我只想要所有人的名字和年龄一样 List<Object> getEmpDataAsList(List<Object> fields); 但我认为,这是不安全的。若有一天我计划更改一个字段的名称,或者从模型中删除一个字段,那个么我需要在3个位置进行更改。我是否应该在模型类中创建一个枚举并使用枚举字段来

我有一个模型,它有5个字段,分别是getter和setter,比如id、name、age、dob、city

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

List<Object> getEmpDataAsList(List<Object> fields);
但我认为,这是不安全的。若有一天我计划更改一个字段的名称,或者从模型中删除一个字段,那个么我需要在3个位置进行更改。我是否应该在模型类中创建一个枚举并使用枚举字段来创建相同的
字段
数组并使用它?或者还有其他更好的方法吗

若有一天我计划更改一个字段的名称,或者删除一个 字段,然后我需要在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());
        }
    }
}
公共抽象类GenericDataInterface{
公共抽象对象getData(KEY);
公共最终列表getEmpDataAsList(列表字段){
列表结果=新的ArrayList(fields.size());
用于(关键字段:字段){
result.add(getData(field));
}
返回结果;
}
}
示例模型类:

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());
        }
    }
}
公共类扩展了GenericDataInterface{
公共枚举比萨饼属性{名称、配料、价格};
私有字符串名称;
私人设置浇头;
//警告:不要在实际应用中使用浮动价格!
私人浮动价格;
@凌驾
公共对象getData(PizzaAttributes键){
开关(钥匙){
案例名称:
返回名称;
外壳浇头:
返回浇头;
箱子价格:
退货价格;
违约:
抛出新的IllegalArgumentException(
没有为“+Pizza.class.getCanonicalName())定义“Key”+Key+”;
}
}
}
结果还不是很方便,因为顺序的扭曲会导致看不见的问题。因此,您可以使用地图作为结果:

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;
    }
}
公共抽象类GenericDataInterface{
公共抽象对象getData(KEY);
公共最终映射getEmpDataAsList(列表字段){
映射结果=新的HashMap(fields.size());
用于(关键字段:字段){
result.put(字段,getData(字段));
}
返回结果;
}
}
您也可以使用真实模型类的克隆来代替映射,并仅填充指定的属性

进一步思考:

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());
        }
    }
}
  • 您真的需要这样一个通用接口吗
  • 如果是关于网络通信,你不能使用XML或JSON吗

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

这样,您就可以拥有一个更具意图的API

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

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

Map<Fields,Object> getEmpData(Fields...fields)
Map getEmpData(字段…字段)
如果有一天我计划更改一个字段的名称,或者从模型中删除一个字段,那么我需要在3个位置进行更改。


对于枚举,如果重命名该枚举,IDE可以为您自动重命名所有引用。如果删除枚举,将出现编译器错误,以便知道需要更改的内容。

服务
getEmpDataAsList(List)
提供了还是允许您更改它?@Chris Ya我已经完成了项目,我可以随时更改它。您不应该使用原始类型
键扩展枚举
。要么将其键入某个内容,要么将其保留为未知
Enum
,而不是原始类型。@Bohemian:为什么?无论如何,您都不会从Enum手动继承。因为Enum是泛型类。绑定到枚举的正确方法是
我喜欢模型的
getData()
方法,它非常适合场景。但我并不真正理解创建
GenericDataInterface
的必要性。dkatzel answer不是更清晰了吗,因为最终的目标是提供一个API,如果模型中发生任何更改,它会导致编译错误,并且让开发人员知道他可以在这个方法调用中使用哪些字段?我不是在寻找网络交流。请帮助我理解
GenericDataInterface
的必要性,它将如何提供更多帮助?@Ahbi:我假设您有多个模型类,因此您可以重用
GenericDataInterface
中的一些代码
getEmpDataAsList
可能已经是接口或抽象类的一部分,所以这应该不是问题。dkatzels的答案只是我的一个简单版本。我的也会导致编译错误。使用dkatzerls解决方案,您无法拥有一个公共接口,因为每个模型类都有一个不同的枚举作为参数。非常感谢,我非常喜欢它的简单性,我将在我目前的情况下使用它:)
Map<Fields,Object> getEmpData(Fields...fields)