【问题标题】:Best way to refactor this code, if enums are not extensible如果枚举不可扩展,则重构此代码的最佳方法
【发布时间】:2022-01-16 02:40:44
【问题描述】:

考虑以下表示实体的类,并具有返回 EnumA

类型数据的方法

EntityA 类:

public class EntityA
{   
    public Set<EnumA> getProcessedFlags() { ... }
}

枚举

public enum EnumA {
  READ_ONLY,
  READ_WRITE,
  PERMISSION_DENIED,
  COMMENT_ENABLED
}

现在我需要再添加一个EntityB,它类似于EntityA,但逻辑上有所不同。 (相同的方法签名但不同的实现:

还有一件事,EntityA中的EnumA的返回值是EntityB的**EnumB。

EnumAEnumB 是两个不同的集合,它们代表逻辑上相同标志的集合,但每个标志都有一些独占的值:

枚举B

public enum EnumB {
  READ_ONLY,
  READ_WRITE,
  LIKE_ENABLED
}

所以为了重构它,我创建了一个抽象基类 - EntityBase,并创建了 EntityAEntityB,作为这个基类的子类.

问题是枚举不可扩展,有没有好的方法来重构它?

我想创建如下所示的类结构:

public enum EnumBase {}

public enum EnumA extends EnumBase {}

public enum EnumB extends EnumBase {}

public abstract class EntityBase
{
  public Set<? extends EnumBase> getProcessedFlags();
}

public class EntityA extends EntityBase
{   
    public Set<EnumA> getProcessedFlags() { ... }
}

public class EntityB extends EntityBase
{   
    public Set<EnumB> getProcessedFlags() { ... }
}

既然 java 中的枚举是不可扩展的,我们能不能有更好的设计来维护这个 EntityBase 类?

【问题讨论】:

  • 考虑过独占使用类的可能性吗?即完全避免枚举?另外,由于 EntityA 和 EntityB 的标志是通用的,我们可以将标志与实体分开,只需要跟踪它的组合,可能使用 Set?另请注意,枚举作为区分“类型”的一种方式具有 C/C++ 风味!
  • 主要问题是这个 Enum 已经在很多地方被用作 Enum。完全分离实体和枚举,绝对是一个解决方案。但它会随着添加更多类似的实体和枚举而扩展吗?

标签: java class oop enums refactoring


【解决方案1】:

很难在没有更多上下文的情况下提供具体建议,但值得注意的是枚举可以共享接口,因此您可以尝试这样的事情:

interface Base {
    String name();
}

interface A extends Base {}
interface B extends Base {}

public enum EnumBase implements A, B {
    READ_ONLY,
    READ_WRITE
}

public enum EnumA implements A {
    PERMISSION_DENIED,
    COMMENT_ENABLED
}

public enum EnumB implements B {
    LIKE_ENABLED
}

public abstract class EntityBase<F extends Base> {
    public abstract Set<F> getProcessedFlags();
}

public class EntityA extends EntityBase<A> {
    @Override
    public Set<A> getProcessedFlags() { ... }
}

public class EntityB extends EntityBase<B> {
    @Override
    public Set<B> getProcessedFlags() { ... }
}

【讨论】:

  • 感谢@shmosel,这似乎是一个更好、更合理的设计。我可以看到这里的一个主要问题是,如果我们添加另一个实体 - 枚举返回类型为 EnumC 的 EntityC,则需要检查所有其他枚举并相应地对其进行重组。我也很惊讶地检查是否有某种方式对枚举进行分组。我能想到的一种方法是创建一个具有枚举作为其属性的最终类,但这也是不可扩展且不可读的。 public final class PermissionFlags { enum EnumCommon { COMMON } enum EnumA { VAL1 } enum EnumB { VAL1 } }
【解决方案2】:

另一种解决方案是为所有 PermissionsFlags 使用一个枚举,然后添加一个属性,允许您检查 PermissionFlag 是否可以与此类一起使用。

public enum PermissionFlag {
    READ_ONLY,
    READ_WRITE,
    PERMISSION_DENIED(EntityA.class),
    COMMENT_ENABLED(EntityA.class),
    LIKE_ENABLED(EntityB.class);

    private final Class<?> useForClass;

    PermissionFlag() {
        this(null);
    }

    PermissionFlag(Class<?> useForClass) {
        this.useForClass = useForClass;
    }

    /**
     * Check if given class can be used for this PermissionFlag.
     */
    public boolean useForClass(Class<?> clazz) {
        return useForClass == null || useForClass.equals(clazz);
    }

    /**
     * Returns a Stream consisting of all the PermissionFlags that can be used with the given class.
     */
    public static Stream<PermissionFlag> getPermissionsFlags(Class<?> clazz){
        return Stream.of(values()).filter(flag -> flag.useForClass(clazz));
    }
}

然后您可以使用getPermissionFlags(EntityA.class).collect(Collectors.toList()) 获取EntityA 的所有权限标志的列表。

【讨论】:

    猜你喜欢
    • 2010-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-16
    • 2012-10-21
    • 1970-01-01
    相关资源
    最近更新 更多