在我的应用中,有代表任务的对象(存储在数据库中)。 还有一些对象具有任务的实际实现(一些业务逻辑) - 让我们称之为跑步者。 因此,跑步者可以实现以下界面:
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”方法(添加一个我需要实现方法的新任务,所以我不能忘记它)但遗憾的是由于项目依赖性,我的Task对象无法知道任何事情关于跑步者。
关于如何以一种好的方式做到这一点的任何想法?
顺便说一下:我正在使用Spring,因此根据传递的任务获取Runner bean会更好。
答案 0 :(得分: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);
如果你添加一个新的任务类型,你将被要求实现其accept()
方法,这将迫使你向访问者添加一个新方法,以及一个新的Runner实现。
答案 1 :(得分:1)
如果您遵循bean命名约定,例如runnerBeanName = Task
类名+'Runner',您可以使用ApplicationContext
作为Runner
工厂。
例如:
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耦合的工厂,您还可以通过bean名称自动装配将在上下文中注入所有Map<String, Runner>
的{{1}}。
答案 2 :(得分: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
包裹