Java 使用Enum作为一个功能齐全的对象,并像在本例中那样使用动态调度,是否意味着误用它或不是一个好的设计?
我已经实现了一个角色枚举,其中包含具有等级的角色定义。对于Java 使用Enum作为一个功能齐全的对象,并像在本例中那样使用动态调度,是否意味着误用它或不是一个好的设计?,java,oop,design-patterns,enums,polymorphism,Java,Oop,Design Patterns,Enums,Polymorphism,我已经实现了一个角色枚举,其中包含具有等级的角色定义。对于getAssignableRoles()部分,我的同事说这是滥用枚举或过度工程,并表示此表单不可读 public enum Role { USER("ROLE_user", 1), CUSTOMER("ROLE_customer", 2), PRODUCT_OWNER("ROLE_product_manager", 3) { @Override public List<Role
getAssignableRoles()
部分,我的同事说这是滥用枚举或过度工程,并表示此表单不可读
public enum Role {
USER("ROLE_user", 1),
CUSTOMER("ROLE_customer", 2),
PRODUCT_OWNER("ROLE_product_manager", 3) {
@Override
public List<Role> getAssignableRoles() {
return getLowerRankedRoles().stream().filter(e -> !e.equals(CUSTOMER_ADMIN)).collect(Collectors.toList());
}
},
CUSTOMER_ADMIN("ROLE_customer_admin", 3) {
@Override
public List<Role> getAssignableRoles() {
return getLowerRankedRoles().stream().filter(e -> !e.equals(PRODUCT_OWNER)).collect(Collectors.toList());
}
},
USER_MANAGER("ROLE_user_manager", 5),
ADMIN("ROLE_admin", 99);
private final String value;
private final int rank;
public String getValue() {
return value;
}
public int getRank() {
return rank;
}
Role(String value, int rank) {
this.value = value;
this.rank = rank;
}
public static Role findByAbbr(String abbr) {
return Arrays.stream(values()).filter(value -> value.value.equals(abbr)).findFirst().orElse(UNKNOWN);
}
public String getExactValue() {
return value.replaceFirst("^ROLE_", "");
}
// Each role has a distinct grant rank but for some roles even if they have same grant level their role assigning changes in the context.
public List<Role> getAssignableRoles() {
return getLowerRankedRoles();
}
protected List<Role> getLowerRankedRoles() {
return Arrays.stream(values()).filter(value -> value.rank <= rank).collect(Collectors.toList());
}
public static Predicate<String> isInRealm() {
return (String role) -> (Arrays.stream(values()).anyMatch(value -> value.value.equals(role)));
}
}
这样使用枚举是一种好的实践还是设计?我们能从Enum中获得大多数动态分派功能吗?一般来说,它不是好的,也不是坏的。答案取决于你的目标 如果要禁止使用其他参数创建此类型的其他实例,可以这样做。如果您希望系统中的管理员动态创建新角色,那么您无法通过这种设计来实现 如果您确信以后不会改变行为,那么这样的设计是好的
如果您希望角色的行为可以针对不同的用例进行扩展或修改(例如,在一个用例中,您得到角色支持多个缩写的要求,在另一个用例中,您得到缩写必须不区分大小写的要求),那么您无法实现它,因为枚举是最终的,它不能有子类。对于每个新需求,您必须更改此枚举。随着时间的推移,这将很难做到:如果你为一个用例更改它,你可以打破另一个用例。这个问题实际上比乍一看更有趣。我在这里看到两个不同的观点:
enum
s就是这样的:常量值的集合()
第(2)点是指Javaenum
s的values()
预定义方法的威力。事实上,Role.findByAbbr(abbr)
的实现使用其他策略将是一团混乱
让我进一步阐述一下。如果您不希望客户端代码能够创建新角色,一个常见的策略是利用包友好的可见性范围。在包com.example.Role
中声明一个抽象类Role
package com.example.role;
公共抽象类角色{
私有整数秩;
私有字符串值;
角色(字符串值,整数秩){
这个值=值;
这个.等级=等级;
}
//其他方法。。。
}
注意,构造函数上没有显式的可见性修饰符(因此它是包友好的)。只有同一个包内的类才能调用类角色的构造函数,因此继承只能在同一个包内进行***
package com.example.role;
公共类UserRole扩展了角色{
私有静态最终用户角色实例=新用户角色();
私有用户角色(){
超级(“角色用户”,1);
}
公共用户角色getInstance(){
返回实例;
}
}
我们试图使用普通Java类来模拟您的enum
,因此我们还使用单例模式来为每个角色创建一个实例
一切都很简单,但是当涉及到values()
预定义方法时,情况就变了
package com.example.role;
公共最终类角色{
私有静态集合值=集合.unmodifiableList(Arrays.asList(
UserRole.getInstance(),
CustomerRole.getInstance(),
ProductOwnerRole.getInstance(),
CustomerAdminRole.getInstance(),
UserManagerRole.getInstance(),
AdminRole.getInstance());
私人角色(){
}
公共静态集合值(){
返回值;
}
//其他方法。。。
}
每次添加新的角色
时,必须记住将其添加到值
列表中。那太难看了
客户端代码的接口仍然很漂亮,如下所示:
Collection assignables=Roles.getAssignableRolesFor(role);
总之,您对enum
属性的利用看起来并没有那么糟糕。您的角色是常量,预定义的values()
方法提供了很多现成的功能。将所有的等级和名称放在一个地方,而不是分散在多个文件中,这也非常好
***用户可以在他的客户端代码中创建一个具有相同名称的包,并创建一个新的<代码>角色<代码>,但是这是一个糟糕的实践,我甚至不考虑。
因为这个代码工作,它似乎更适合于或。
Role.findByAbbr(role).getAssignableRoles()