Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用Enum作为一个功能齐全的对象,并像在本例中那样使用动态调度,是否意味着误用它或不是一个好的设计?_Java_Oop_Design Patterns_Enums_Polymorphism - Fatal编程技术网

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中获得大多数动态分派功能吗?

一般来说,它不是好的,也不是坏的。答案取决于你的目标

如果要禁止使用其他参数创建此类型的其他实例,可以这样做。如果您希望系统中的管理员动态创建新角色,那么您无法通过这种设计来实现

如果您确信以后不会改变行为,那么这样的设计是好的


如果您希望角色的行为可以针对不同的用例进行扩展或修改(例如,在一个用例中,您得到角色支持多个缩写的要求,在另一个用例中,您得到缩写必须不区分大小写的要求),那么您无法实现它,因为枚举是最终的,它不能有子类。对于每个新需求,您必须更改此枚举。随着时间的推移,这将很难做到:如果你为一个用例更改它,你可以打破另一个用例。

这个问题实际上比乍一看更有趣。我在这里看到两个不同的观点:

  • 您的角色没有可变状态
  • 您需要访问所有不同角色的列表,这是固定的
  • 说到第(1)点,如果一个值是不可变的,那么它就被称为常量,而Java
    enum
    s就是这样的:常量值的集合()

    第(2)点是指Java
    enum
    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()