在枚举中覆盖抽象方法时避免代码重复

时间:2014-06-27 13:55:06

标签: java enums state state-machine code-duplication

我在Java中使用枚举实现状态机。我在下面有一个玩具示例,我根据群组成员资格在XYZ州之间进行转换。

问题是,YZ的转换规则是相同的(即,Overriden方法是相同的)。

有没有办法避免代码重复?在我的现实生活中,它有点严重,因此代码重复的可能性更大。

enum Group {
    A,B,C
}

enum Element {
    X(Group.A) {
        @Override
        public Element getNextElement(Element nextElement) {
            if(nextElement.getGroup() == Group.B) {
                return nextElement;
            } else {
                return this;
            }
        }
    },
    Y(Group.B) {
        @Override
        public Element getNextElement(Element nextElement) {
            if(nextElement.getGroup() == Group.A) {
                return nextElement;
            } else {
                return this;
            }
        }
    },
    Z(Group.C) {
        @Override
        public Element getNextElement(Element nextElement) {
            if(nextElement.getGroup() == Group.A) {
                return nextElement;
            } else {
                return this;
            }
        }
    };

    Group group;

    Element(Group group) {
        this.group=group; 
    };

    public Group getGroup() {
        return this.group;
    }

    public abstract Element getNextElement(Element nextElement);

}

3 个答案:

答案 0 :(得分:6)

鉴于您的逻辑与转换规则中的值除了相同,您可以通过以下方式进行参数化:

enum Element {
    X(Group.A, Group.B),
    Y(Group.B, Group.A),
    Z(Group.C, Group.A);

    private final Group group;
    private final Group nextGroup

    private Element(Group group, Group nextGroup) {
        this.group = group; 
        this.nextGroup = nextGroup;
    }

    public Group getGroup() {
        return this.group;
    }

    public Element getNextElement(Element nextElement) {
        return nextElement.getGroup() == nextGroup ? nextElement : this;
    }
}

您仍然可以在某些值中覆盖getNextElement。例如:

enum Element {
    X(Group.A, Group.B) {
        @Override public Element getNextElement(Element nextElement) {
            return someRandomCondition ? nextElement : this;
        }
    }
    Y(Group.B, Group.A),
    Z(Group.C, Group.A);

    // Other code as above
}

答案 1 :(得分:1)

您可以使用strategy pattern,大致如下例所示。通过考虑不同策略如何相互关联并将相似性拉入一个或多个策略基类,可以改进这个例子:

enum Element {

    interface TransitionStrategy {
       Element getNextElement (Element myself, Element nextElement);
    }

    static class NextOnBStrategy implements TransitionStrategy {
       Element getNextElement (Element myself, Element nextElement) {
            if(nextElement.getGroup() == Group.B) {
                return nextElement;
            } else {
                return myself;
            }
        }                
    }

    // other strategies

    X(Group.A, new NextOnBStrategy ()),
    Y(Group.B, new NextOnAStrategy ()),
    Z(Group.C, new NextOnAStrategy ());

    Group group;
    TransitionStrategy strategy;

    Element(Group group, TransitionStrategy strategy) {
        this.group=group; 
        this.strategy=strategy;
    };

    // ...


    public Element getNextElement(Element nextElement) {
       return this.strategy.getNextElement (this, nextElement);
    }

}

答案 2 :(得分:1)

这取决于管理转换条件所需的复杂程度,一个好的面向目标的方法是封装规则,例如:

class RuleSet {
  Map<Element, List<Rule>> rules = new HashMap<Element, List<Rule>>();

  void addRule(Element element, Rule rule) {
    List<Rule> rulesOfElem = rules.get(element);
    if (rulesOfElem == null) {
      rulesOfElem = new ArrayList<Rule>();
      rules.put(element, rulesOfElem);
    }

    rulesOfElem.add(rule);
  }

  Element evaluate(Element element, Environment env) {
    List<Rule> rulesOfElem = rules.get(element);
    if (rulesOfElem != null) {
      for (Rule rule : rulesOfElem) {
        Element next = rule.evaluate(element, env);
        if (next != null) return next;
      }
    }

    return element;
  }
}

abstract class Rule {
  Element evaluate(Element current, Environment env);
}

class GroupRule extends Rule {
  private final Group from, to

  GroupRule(Group from, Group to) {
    this.from = from;
    this.to = to;

    for (each Element in Group)
      ruleSet.add(element, this);
  }

  Element evaluate(Element element, Environment env) {
    ...
  }
}

但是,如果不知道所需的复杂性,很难确定保持简单或保持其可维护性是否更好。