【问题标题】:Java ExecutorService Task SpawningJava ExecutorService 任务生成
【发布时间】:2013-01-03 15:04:41
【问题描述】:

我有一个ExecutorService 用于处理任务流。这些任务由我的DaemonTask 类表示,每个任务都构建一个响应对象,该对象传递给响应调用(超出本问题的范围)。我正在使用switch 语句根据任务 ID int 生成适当的任务。它看起来像;

//in my api listening thread
executorService.submit(DaemonTask.buildTask(int taskID));

//daemon task class
public abstract class DaemonTask implements Runnable {

    public static DaemonTask buildTask(int taskID) {
        switch(taskID) {
            case TASK_A_ID: return new WiggleTask();
            case TASK_B_ID: return new WobbleTask();
            // ...very long list ...
            case TASK_ZZZ_ID: return new WaggleTask();
        }
    }

    public void run() {
        respond(execute());
    }

    public abstract Response execute();
}


我所有的任务类(例如WiggleTask()extend DaemonTask 并为execute() 方法提供了一个实现。

我的问题很简单;这种模式合理吗?当我查看带有所有返回语句的巨大开关盒时,感觉有些不对劲。我试图以某种方式使用反射提出一个更优雅的查找表解决方案,但似乎无法找到一种可行的方法。

【问题讨论】:

    标签: java coding-style refactoring executorservice


    【解决方案1】:

    你真的需要这么多课程吗?每个 taskId 可以有一个方法。

    final ResponseHandler handler = ... // has many methods.
    
    // use a map or array or enum to translate transIds into method names.
    final Method method = handler.getClass().getMethod(taskArray[taskID]); 
    executorService.submit(new Callable<Void>() {
        public Void call() throws Exception {
             method.invoke(handler);
        }
    });
    

    如果你必须有很多类,你可以这样做

    // use a map or array or enum to translate transIds into methods.
    final Runnable runs = Class.forName(taskClassArray[taskID]).newInstance(); 
    executorService.submit(new Callable<Void>() {
        public Void call() throws Exception {
             runs.run();
        }
    });
    

    【讨论】:

    • 是我试图掌握的基于反射的方法。感谢您通过两个示例进行澄清。单独的类的原因很简单,execute() 方法在许多情况下非常庞大,有些需要其他库不需要的库,等等。每个类都意味着合理大小的源文件并避免数百万次导入。
    【解决方案2】:

    您可以使用enum

    public enum TaskBuilder
    {
        // Task definitions
        TASK_A_ID(1){
            @Override
            public DaemonTask newTask()
            {
                return new WiggleTask();
            }
        },
        // etc
    
        // Build lookup map
        private static final Map<Integer, TaskBuilder> LOOKUP_MAP
            = new HashMap<Integer, TaskBuilder>();
    
        static {
            for (final TaskBuilder builder: values())
                LOOKUP_MAP.put(builder.taskID, builder);
        }
    
        private final int taskID;
        public abstract DaemonTask newTask();
    
        TaskBuilder(final int taskID)
        {
            this.taskID = taskID;
        }
    
        // Note: null needs to be handled somewhat
        public static TaskBuilder fromTaskID(final int taskID)
        {
            return LOOKUP_MAP.get(taskID);
        }
    }
    

    有了这样的枚举,你就可以这样做了:

    TaskBuilder.fromTaskID(taskID).newTask();
    

    另一种可能是使用构造函数字段而不是方法,即使用反射。它更容易编写并且工作正常,但是异常处理就变成了一场噩梦:

    private enum TaskBuilder 
    {                                                 
        TASK_ID_A(1, WiggleTask.class),
        // others
    
        // Build lookup map
        private static final Map<Integer, TaskBuilder> LOOKUP_MAP
            = new HashMap<Integer, TaskBuilder>();
    
        static {
            for (final TaskBuilder builder: values())
                LOOKUP_MAP.put(builder.taskID, builder);
        }
    
        private final int index;
        private final Constructor<? extends DaemonTask> constructor;
    
        TaskBuilder(final int index, final Class<? extends DaemonTask> c)
        {
            this.index = index;
            // This can fail...
            try {
                constructor = c.getConstructor();
            } catch (NoSuchMethodException e) {
                throw new ExceptionInInitializerError(e);
            }
        }
    
        // Ewww, three exceptions :(    
        public DaemonTask newTask()
            throws IllegalAccessException, InvocationTargetException,
            InstantiationException
        {
            return constructor.newInstance();
        }
    
        // Note: null needs to be handled somewhat
        public static TaskBuilder fromTaskID(final int taskID)
        {
            return LOOKUP_MAP.get(taskID);
        }
    }
    

    这个枚举可以和另一个枚举一样使用。

    【讨论】:

    • 很好地使用了重载,我会记住这个模式,谢谢!
    • 还有更简单的方法:您可以添加一个类作为枚举的实例变量,然后.newTask() 将只是一个theClass.newInstance()。您的选择 ;) 抽象方法的优点是您可以在初始化时做更多的事情。
    • 我可能最终会使用它,如果我这样做了,我会移开接受。再次感谢您的想法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-14
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    相关资源
    最近更新 更多