【问题标题】:How to replace switch with polymorphism?如何用多态替换switch?
【发布时间】:2017-09-13 13:38:54
【问题描述】:

我有 2 个应用程序:application_1applicaion_2

appplication_1application_2发送不同类型的消息

有几种类型。我可以声明这些类型的枚举。

enum MessageType{
   TYPE_1,
   TYPE_2,
   ...
}

在我使用的 application_2 框架中,建议我编写以下 API

public void handle(Object o){
    //logic
}

我在思考如何构建类来分别处理每条消息。

我知道我可以为所有消息声明通用类型:

abstract class AbstractMessage{
    MessageType type;
    Object o; 
    //...   
}

application_2 里面的句柄中我可以这样写:

MessageType mt = ((AbstractMessage) o).getType();
  switch(mt){
     case TYPE_1: 
        //handle TYPE_1
        break;
     case TYPE_2: 
        //handle TYPE_2
        break;
        ....
  }

但是这段代码看起来很难看。

请帮助找到更好的解决方案。

【问题讨论】:

  • @Downvoter,请解释一下你不明白的地方。我已准备好改进我的问题。我真的很努力提出明确的问题
  • 看来你要找的是Strategy pattern
  • @Thomas Fritsch,可能是,但你能提供更多细节吗?
  • 我在考虑@LorisSecuro 的回答

标签: java oop design-patterns enums polymorphism


【解决方案1】:

application_2 无论如何都需要知道它收到了哪种类型的消息,所以某种switch 是不可避免的。但关键是这个switch 只在一个地方。例如,您可以使用如下方法:

public MessageHandler getHandlerFor(MessageType messageType) {
    switch (messageType) {
        case TYPE_1: return Type1MessageHandler();
        case TYPE_2: return Type2MessageHandler();
        ............
        default: throw new IllegalArgumentException("No handler found for messageType: " + messageType);
    }
}

那么你将需要MessageHandler的层次结构,它对应于策略模式:

public interface MessageHandler {
    void handle();
}

MessageHandler 接口的每个实现都应提供MessageType 特定的处理逻辑。

【讨论】:

    【解决方案2】:

    如果你想使用多态,你可以定义abstract消息类:

    abstract class AbstractMessage { 
        public abstract void doStuff();
        //...   
    }
    

    不要使用enums,而是为每个消息类型创建一个类,扩展抽象类并覆盖方法:

    class Type1Message extends AbstractMessage {
        @Override
        public void doStuff() {
            //handle TYPE_1
        }
    }
    
    class Type2Message extends AbstractMessage {
        @Override
        public void doStuff() {
            //handle TYPE_2
        }
    }
    

    然后在您的handle 方法中:

    ((AbstractMessage) o).doStuff();
    

    【讨论】:

    • 我认为Message 本身不应该知道如何处理。客户端代码负责决定如何处理每种类型的消息。
    • @PavloViazovskyy 好吧,这取决于我猜;这也避免了switch,这是 OP 要求的
    • 看起来我们有 2 个层次结构。处理程序和消息。桥梁有帮助吗?
    • @gstackoverflow 这里只有一个层次结构。 Loris 的建议是使用 AbstractMessage 类(每种消息类型都有子类)而不是 MessageType 枚举。
    • 我不明白如何在具体消息中获取对象类型
    【解决方案3】:

    您可以使用chain-of-responsibility 模式。它与策略模式的不同之处在于您的消息指示每个应用程序执行的命令。这基本上就是switch 正在做的事情。

    您可以使用handle 方法动态加载实现接口的类(Pavlo 的类进行了一些修改以将其与 Loris 的抽象消息相结合):

    public interface MessageHandler
    {
      void handle (AbstractMessage msg);
    }
    

    Java 有service provider 的概念,它是动态加载的一种方法(如果这不符合您的需要,我相信还有其他方法可以使用)。您可以在处理消息时遍历处理程序,将消息实例传递给每个处理程序。每个处理程序决定是否要处理消息。您甚至可以让 handle 返回一个 boolean 以表明如果您愿意,该链可以停止调用后续处理程序。

    您可以在每个应用程序中为您要处理的消息类型实现处理程序。有很多方法可以解决这个问题(加载处理程序并在启动时初始化每个处理程序,在消息处理时加载它们等),因此请选择适合您需要的方法。链接的服务提供者文章有一个简单的循环演示处理程序的加载。

    不需要switch 会随着您的代码被修改而改变,您只需重新配置您的 jar 的构建方式。这也是open-closed principle 的一个很好的示例,您的代码不会更改但可以扩展。

    【讨论】:

      【解决方案4】:

      也许像下面这样。它确实有一个开关,但每种类型的代码都在枚举中。

      public class So43459907 {
          public enum Type {
              m1 {
                  @Override Object create(Object o) {
                      return o;
                  }
                  @Override void handle(Object o) {}
              },
              m2 {
                  @Override Object create(Object o) {
                      return o;
                  }
                  @Override void handle(Object o) {}
              };
              abstract Object create(Object o);
              abstract void handle(Object o);
              public static Object create(Type type,Object o) {
                  switch(type) {
                      case m1:
                          return m1.create(o);
                      case m2:
                          return m2.create(o);
                      default:
                          throw new RuntimeException("oops");
                  }
              }
          }
          public static void main(String[] args) {
              // TODO Auto-generated method stub
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2018-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多