【问题标题】:Function array in Java?Java中的函数数组?
【发布时间】:2013-08-08 15:40:17
【问题描述】:

也许我在 C 中想了很多,但我没有看到如何在 java 中正确解决这个问题的解决方案。 我收到来自服务器的响应,该响应发送如下字符串:

command params <xml...>

客户端接收该字符串并提取命令。现在我想调用一个知道如何处理命令的函数。在 C 方面,解决方案是显而易见的。我用命令名称和相关的函数指针实现了一个数组,所以我可以简单地遍历数组并调用函数。

在 Java 上也有办法做到这一点吗?我不知道我可以根据名称调用函数。所以目前我看到以下选项:

  1. 做一系列if(command.euqals(COMMAND)
  2. 对于每个命令,我可以创建一个单独的对象,我可以将其存储在一个数组中(非常混乱)。
  3. 使用反射,所以我可以有一个函数名与命令名的映射。

还有其他选择吗?

if 语句不是最好的 IMO,但至少它允许编译器错误和类型检查。使用反射至少更优雅,因为我可以更轻松地循环和扩展它,但当然,这意味着如果我输错名称,我只能看到运行时错误。

【问题讨论】:

    标签: java jump-table


    【解决方案1】:

    我最近看到了一个函数技巧,虽然它很简单,但非常有效:用另一个函数“包装”一个函数或一组函数。

    下面是我为了展示这个技巧而编写的代码示例。 我的示例只为一个命令提供一个函数,但如果需要,它可以很容易地扩展到返回的一堆函数(因此包含可以使用的 Guava Mutlimap 类型)......我返回一个 Optional 所以当一个命令不匹配,它“安全地”返回一个空值; Contact 和 ContacUnit 是域类型,它们都扩展了一个组织类型...这应该使下面的代码有意义。

    package com.xxx.component;
    
    import com.google.common.collect.ArrayListMultimap;
    import com.google.common.collect.Multimap;
    import com.xxx.domain.Contact;
    import com.xxx.domain.ContactUnit;
    import com.xxx.domain.Organization;
    
    import java.util.Optional;
    import java.util.function.Function;
    
    /**
     * This serves up the functions used for the domain
     * to validate itself.
     * Created by beezerbutt on 06/04/2017.
     */
    public class MapSetDomainFunctionFactory {
    
        public static final Function<String, Optional<Organization>> toContactFromCwid = s-> Optional.ofNullable(s).map(Contact::new);
        public static final Function<String, Optional<Organization>> toContactUnitFromKey = s-> Optional.ofNullable(s).map(ContactUnit::new);
    
        public static final Function<String, Function<String, Optional<Organization>>> commandToFunctions = command -> {
            if (command.equalsIgnoreCase("toContactFromCwid")) {
                return MapSetDomainFunctionFactory.toContactFromCwid;
            } else {
                return null;
            }
        };
    }
    

    }

    为了让生活更轻松,我包含了域类代码:

    /**
     * Created by beezerbutt on 06/04/2017.
     */
    public class Contact implements Organization {
    }
    public class ContactUnit implements Organization {
    }
    public interface Organization {
    }
    

    下面是我运行的 Spock 测试的快照,以证明代码有效:

    【讨论】:

      【解决方案2】:

      如果您只想拥有一个文件,可以使用enum 来制作。

      试试这样的:

          public class Command {
      
            public enum CommandName {
      
              UNDO,
              PRINT,
              RUN
            }
      
            public static void execute(String command, String[] params, String xml) {
              try {
                CommandName cname = CommandName.valueOf(command);
      
                switch (cname) {
                  case UNDO:
                     undo (params, xml);
                    break;
                  case PRINT:
                    //
                    break;
                }
              } catch (IllegalArgumentException iae) {
                // Unknown command
              }
            }
      
            public static void undo (String[] params, String xml) {
               // ....
            }
          }
      

      运行命令:

          Command.execute(command, params, xml);
      

      【讨论】:

      • 记住 Java 枚举可以有方法——你可以声明一个抽象的 doExecute 而不是 switch 只是做 cname.doExecute()
      【解决方案3】:

      你的第二个想法是惯用的。使用Map&lt;String, Runnable&gt; 存储命令名称和相应的代码,然后commands.get(commandName).run() 执行一个。

      不要害怕创建类!它可能会使您的代码开始时更加冗长,但是编写一个类并且再也不用担心它比使用switchif ... else if ... 做同样的事情要容易得多。如果您的命令变得比单一方法更复杂(可能是toString()undo()...),您会越来越高兴使用多态性而不是条件。

      【讨论】:

      • 我觉得有点乱,因为我必须创建很多类来执行单个任务,它甚至不能自己执行,而是必须更新 GUI 等。另一方面,也许这不是一个坏主意,我必须看看它对 GUI 代码有什么影响。
      • 我一点也不觉得这很乱;它是多态性的基石!
      • 我越想这种方法,我就越喜欢它。我最初的想法是我会有数百个小类,但在重新考虑论点后,它并没有那么糟糕,它肯定有你提到的优点。
      • 现在可以创建匿名类更好了。 new Command(){ run() { /* process command */ } } - 这可以是Command 的数组,其中这是一个接口类。 docs.oracle.com/javase/tutorial/java/javaOO/…
      【解决方案4】:

      我过去曾使用以下方法解决过此类问题,它可能适合您的情况:

      有一个标准的接口:

      public interface Executable {
          public String getCommandName();
          public void execute(String[] params, String xml);
      }
      

      和x个实现:

      public class SaveExecutable implements Executable {
          private static final String COMMAND_NAME = "SAVE";
          public String getCommandName() {
              return COMMAND_NAME;
          }
      
          public void execute(String[] params, String xml) {
               ...
          }
      }
      

      然后将实现存储在 HashMap 中以供查找:

      HashMap<String, Executor> executors = new HashMap<>();
      executors.put("SAVE", new SaveExecutable());
      

      然后你可以有一个方法来处理通用命令(验证和数组修剪省略):

      public void handleCommand(String[] command) {
          executors.get(command[0]).execute(command);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-02-14
        • 2014-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-14
        相关资源
        最近更新 更多