需要Java模式:基于其他对象的类实例化对象

时间:2013-02-26 13:41:22

标签: java spring design-patterns

在我的应用中,有代表任务的对象(存储在数据库中)。 还有一些对象具有任务的实际实现(一些业务逻辑) - 让我们称之为跑步者。 因此,跑步者可以实现以下界面:

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会更好。

3 个答案:

答案 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

包裹