【问题标题】:Can somebody recommend a java 8 pattern to replace a switch statement?有人可以推荐一个 java 8 模式来替换 switch 语句吗?
【发布时间】:2014-09-17 22:15:31
【问题描述】:

我有以下代码:

 public class A {
    private String type;
    String getType() { return type;}
 }

现在在很多代码地方我都有这样的代码

 switch (a.geType()) {
  case "A" : return new Bla();
  case "B" : return new Cop();
 }

或其他地方

switch (a.geType()) {
  case "A" : return new Coda();
  case "B" : return new Man();
 }

(请注意,我知道我应该在生产代码中使用枚举)。

我想要实现的是,当向 A 类添加新类型时,编译器应该标记所有需要调整的 switch 语句?

有没有 java 惯用的方法来做到这一点?

【问题讨论】:

  • 如果你使用枚举,我很确定 java 确实会标记不处理该条件的 switch 语句,只要 switch 没有 default 条件。跨度>
  • 我刚试了一下。它不会立即标记缺少的枚举
  • 当我这样做时,我在 Eclipse 中收到警告。它可能不是 java 编译器本身的一部分。

标签: java design-patterns java-8


【解决方案1】:

当向class A 添加新类型时,编译器应该标记所有需要调整的switch 语句?

解决此问题的一个好方法是将switch 语句替换为多次调度 的更健壮的实现,例如Visitor Pattern:

interface VisitorOfA {
    Object visitA(A a);
    Object visitB(B b);
}
class A {
    Object accept(VisitorOfA visitor) {
        return visitor.visitA(this);
    }
}
class B extends A {
    Object accept(VisitorOfA visitor) {
        return visitor.visitB(this);
    }
}

有了这个基础设施,您可以删除您的 switch 语句,将它们替换为访问者的实现:

Object res = a.accept(new VisitorOfA() {
    public Object visitA(A a) { return new Bla(); }
    public Object visitB(B b) { return new Cop(); }
});

当您向A 添加新子类型时,例如class C,您只需向VisitorOfA 添加新方法:

Object visitC(C c);

现在编译器会发现所有没有实现这个新方法的地方,帮助你避免在运行时出现问题。

【讨论】:

  • switch 语句看起来更轻量级,因此对于阅读代码的人来说更容易理解。但我不得不承认你的答案解决了“编译器标记”的要求
  • 这种方法的一个很好的改进是将每个实现的实例存储到 Map 中。此外,您可以从 .properties 文件加载地图。
  • @jack 我同意,访客模式对于第一次看到它的人来说看起来很棘手。我在这里有很大的偏见,因为我多年来一直在使用这种模式。然而,优点是人们只需要学习一次这种模式就可以在未来可靠地识别它。这就像学习骑自行车:)
  • 访问者模式对于预期会发生变化的类层次结构来说是一个糟糕的解决方案。 GoF 在第 333 页描述了访问者的这种成本/收益。“当定义对象结构的类很少更改时使用访问者模式,但您通常希望在结构上定义新操作。更改对象结构类需要重新定义所有访问者的接口,这可能是昂贵的。如果对象结构类经常更改,那么最好在这些类中定义操作。"
【解决方案2】:

不要忘记良好的老式多态性。在类中具有带有 switch 语句的“类型”字段通常是一种气味,表明子类化可能有用。考虑:

public abstract class CommonSuperClass {
    public abstract One getOne();
    public abstract Two getTwo();
}

public class A extends CommonSuperClass {
    @Override public One getOne() { return new Bla(); }
    @Override public Two getTwo() { return new Coda(); }
}

public class B extends CommonSuperClass {
    @Override public One getOne() { return new Cop(); }
    @Override public Two getTwo() { return new Man(); }
}

如果您要添加新的子类 C,则需要提供抽象方法的实现(除非您将 C 本身设为抽象)。

【讨论】:

    【解决方案3】:

    你可以有一个字符串/供应商的地图:

    Map<String, Supplier<Object>> map = new HAshMap<> ();
    map.put("A", Bla::new);
    map.put("B", Cop::new);
    

    您的示例代码将变为:

    return map.get(a.getType()).get(); //need null check
    

    【讨论】:

      【解决方案4】:

      从抽象的角度来看,还有另一种方法可供您使用。一种方法是通过Polymorphism as shown here。

      一些简单的例子:

      public void EverythingYouWant (Animal animal) {
          return animal.move();
      }
      

      更多的是关于 重构 replace type code/checking with State/Strategy 模式。首先考虑是否有任何原因阻止子类化是一个很好的解决方案。

      【讨论】:

        猜你喜欢
        • 2011-05-26
        • 1970-01-01
        • 2016-01-01
        • 1970-01-01
        • 2012-03-31
        • 1970-01-01
        • 1970-01-01
        • 2011-10-14
        • 2012-04-10
        相关资源
        最近更新 更多