pkp*_*pnd 4 java design-patterns
我有一个处理Task对象的系统,现在我想进行一些基准测试。具体来说,我将创建许多(~100)个Task对象,每个对象都属于一组任务,并且我想在整个任务组上运行系统。我想要一种设计,可以轻松创建新的Task并将其与组相关联(轻松实现基准套件的多样化)。只有少数几个组,因此可以接受一些每组基础设施。
Tasks 可以包含任意Objects,所以我不能从像 JSON 这样的“数据”文件类型加载它们——只有 Java 代码足够通用来创建Tasks。此外,为了可维护性,我想Task在单独的 Java 文件中创建每个:
// SolveHaltingProblem.java
public class SolveHaltingProblem {
static Task getTask() {
Task task = new Task();
task.setName("SolveHaltingProblem");
... // Create task
return task;
}
static String getGroup() {
return "impossible-tasks";
}
}
Run Code Online (Sandbox Code Playgroud)
然后,应该很容易收集Tasks组:
List<Task> tasks = gatherTasksInGroup("impossible-tasks");
Run Code Online (Sandbox Code Playgroud)
没有一些愚蠢和容易出错的东西,比如:
List<Task> gatherTasksInGroup(String groupName) {
List<Task> list = new ArrayList<>();
if (groupName.equals("impossible-tasks")) {
list.add(SolveHaltingProblem.getTask());
list.add(SomeOtherHardProblem.getTask());
...
} else if (...) {
... // ~100 more lines
}
return list;
}
Run Code Online (Sandbox Code Playgroud)
我提供上面的代码只是为了帮助传达我的需求,设计细节并不是一成不变的。也许最好有SolveHaltingProblem扩展的ImpossibleTaskGroup扩展TaskGroup......
我知道类向其他类注册自己的模式(有这个名字吗?),但我认为这种模式不适用于这里,因为我没有创建 的实例SolveHaltingProblem,因此我不能强制SolveHaltingProblem要运行的任何静态初始值设定项。我也试过在 StackOverflow 上搜索类似的问题,但我发现这个问题很难描述;如果这是重复的,我深表歉意。
总而言之,我的问题是:如何管理Tasks组,以便轻松添加新的Task并将其与组关联?一个好的设计应该具有以下属性(从最重要到最不重要的顺序):
Task都在一个单独的文件中创建Task并将其与组关联只涉及添加/更改一个文件Task为请求的组构造对象一种解决方案是使用Composite Pattern,其中接口是Task,终端类是ExecutableTask(Task您的问题中的内容),复合类是TaskGroup。一个示例实现如下:
public interface Task {
public void execute();
}
public class ExecutableTask implements Task {
@Override
public void execute() {
// Do some work
}
}
public class TaskGroup implements Task {
private final String name;
private final List<Task> tasks = new ArrayList<>();
public TaskGroup(String name) {
this.name = name;
}
@Override
public void execute() {
for (Task task: tasks) {
task.execute();
}
}
public void addTask(Task task) {
tasks.add(task);
}
public String getName() {
return name;
}
}
Run Code Online (Sandbox Code Playgroud)
这将允许您以与执行单个任务完全相同的方式执行任务组。例如,假设我们有一些方法,benchmark,基准 a Task,我们可以提供一个ExecutableTask(单个任务)或TaskGroup:
public void benchmarkTask(Task task) {
// Record start time
task.execute();
// Record completion time
}
// Execute single task
Task oneOffTask = new ExecutableTask();
benchmarkTask(oneOffTask);
// Execute a group of tasks
TaskGroup taskGroup = new TaskGroup("someGroup");
taskGroup.addTask(new ExecutableTask());
taskGroup.addTask(new ExecutableTask());
benchmarkTask(taskGroup);
Run Code Online (Sandbox Code Playgroud)
这种模式也可以通过创建各种Task实现(或扩展ExecutableTask)来扩展,以匹配可以完成的不同任务。例如,假设我们有两种不同类型的任务:彩色任务和定时任务:
public class ColorTask implements Task {
private final String color;
public ColorTask(String color) {
this.color = color;
}
@Override
public void execute() {
System.out.println("Executed task with color " + color);
}
}
public class TimedTask implements Task {
private final long seconds;
public TimedTask(int seconds) {
this.seconds = seconds * 1000;
}
@Override
public void execute() {
Thread.sleep(seconds * 1000);
System.out.println("Executed timed task for " + seconds + " seconds");
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以创建一个TaskGroup不同的Task实现并在没有任何特定于类型的逻辑的情况下运行它们:
TaskGroup group = new TaskGroup("differentTasksGroup");
group.addTask(new ColorTask("red"));
group.addTask(new TimedTask(2));
group.execute();
Run Code Online (Sandbox Code Playgroud)
这将导致以下输出:
Executed task with color red
Executed timed task for 2 seconds
Run Code Online (Sandbox Code Playgroud)
加载新任务
有了这个复合结构,您就可以使用Java 提供的服务提供者接口 (SPI)。虽然我不会在这里详细介绍(链接页面包含了关于如何设置和注册服务的丰富知识),但总体思路是您可以让Task接口充当服务接口,然后加载您需要的各种任务。注册为服务。例如:
public class TaskServiceMain {
public static void main(String[] args) {
TaskGroup rootGroup = new TaskGroup("root");
ServiceLoader serviceLoader = ServiceLoader.load(Task.class);
for (Task task: serviceLoader) {
rootGroup.addTask(task);
}
// Execute all of the tasks
rootGroup.execute();
}
}
Run Code Online (Sandbox Code Playgroud)
使用复合层次结构的根(Task接口)作为服务接口可能会很奇怪,因此创建一个新的服务接口可能是一个好主意。例如,可以创建一个新界面,该界面仅充当标记界面,不添加任何新功能:
public interface ServiceTask extends Task {}
Run Code Online (Sandbox Code Playgroud)
服务加载逻辑则变为:
public class TaskServiceMain {
public static void main(String[] args) {
TaskGroup rootGroup = new TaskGroup("root");
ServiceLoader serviceLoader = ServiceLoader.load(ServiceTask.class);
for (ServiceTask task: serviceLoader) {
rootGroup.addTask(task);
}
// Execute all of the tasks
rootGroup.execute();
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3001 次 |
| 最近记录: |