1

在我的应用程序中有代表任务的对象(存储在数据库中)。还有一些对象具有任务的实际实现(一些业务逻辑)——我们称它们为运行器。所以跑步者可以实现以下接口:

public interface Runner<T> {
    void run(T task);
}

给定一个任务对象,我需要一些好的方法来创建/获取一个运行器,以便我可以运行它。

我宁愿避免类似的事情

if(task instance of DoSomethingTask) {
    return new DoSomethingTaskRunner();
} else if(task instance of DoSomethingElseTask) {
    return new DoSomethingElseRunner();
}

因为每当我创建一个新的跑步者时,我还需要记住在上面的块中添加另一个 if/else。自动获取实现的跑步者会很好

Runner<myTask.getClass()> // pseudocode ;)

这样做的一个好方法是让任务有一个“getRunner”方法(添加一个新任务,我需要实现该方法,所以我不会忘记它)但不幸的是,由于项目依赖性,我的任务对象无法知道关于 Runners 的任何信息。

关于如何以一种好的方式做到这一点的任何想法?

顺便说一句:我正在使用 Spring,因此根据传递的任务获取 Runner bean 会更好。

4

3 回答 3

4

你需要一个 RunnerFactory。如果你不想测试任务的类,你可以使用访问者模式,它基本上允许你做你想做的事(myTask.createRunner()),但任务和运行器之间没有任何耦合:

public interface TaskVisitor<R> {
    R visitTask1(Task1 task1);
    R visitTask2(Task2 task1);
}

public interface Task {
    // ...
    <R> R accept(TaskVisitor<R> visitor);
}

public class Task1 implements Task {
    // ...
    @Override
    public <R> R accept(TaskVisitor<R> visitor) {
        return visitor.visitTask1(this);
    }
}

public class Task2 implements Task {
    // ...
    @Override
    public <R> R accept(TaskVisitor<R> visitor) {
        return visitor.visitTask2(this);
    }
}

public class RunnerFactory implements TaskVisitor<Runner> {
    @Override
    public Runner visitTask1(Task1 task1) {
        return new Task1Runner(task1);
    }

    @Override
    public Runner visitTask2(Task1 task2) {
        return new Task2Runner(task2);
    }
}

现在,要从任务中获取跑步者,您只需要调用

RunnerFactory factory = new RunnerFactory();
Runner runner = someTask.accept(factory);

如果您添加一个新的 Task 类型,您将被迫实现它的accept()方法,这将迫使您向访问者添加一个新方法,以及一个新的 Runner 实现。

于 2013-02-26T13:53:31.430 回答
1

如果您遵循 bean 命名约定,例如 runnerBeanName = Taskclass name + 'Runner',您可以使用ApplicationContextas Runnerfactory。

例如:

public class RunnerFactory implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Runner getRunner(Task task) {
        return (Runner) applicationContext.getBean(task.getClass().getSimpleName() + "Runner");
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;

    }
}

您可以使用别名将一个跑步者绑定到多个任务。

如果您不喜欢与 Spring 耦合的工厂,您还可以自动装配一个Map<String, Runner>Runners通过 bean 名称在上下文中注入的所有内容。

于 2013-02-26T16:19:52.283 回答
0

嗯...制作地图如下:

Map<Class<? extends MyTask>, MyRunner> runners;

然后在 xml 或 @Configuration 添加映射。在方法中getRunner(Class<? extends MyTask> myTask)

 MyRunner getRunner(Class<? extends MyTask> myTask){
    return runners.get(myTask);
 }

就这样。整个“ifology”现在被标准地图包裹#get

于 2013-02-26T13:52:29.670 回答