我有一个简单实用的界面:
public interface Callback<T> {
void invoke(T param);
}
我做了很多异步操作,如:
public void getSubfolders(Folder folder, Callback<FolderList> result){
asyncExecutor.submit(() -> {
FolderList list = folder.get_SubFolders();
result.invoke(list);
});
}
必须在main thread
上处理结果。为此,我有一个JavaFX
方法:
Platform.runLater(Runnable task);
这使得我的代码像这样一个混乱(并且这种模式在其他50种方法中重复):
public void getSubfolders(Folder folder, Callback<FolderList> result){
asyncExecutor.submit(() -> {
FolderList list = folder.get_SubFolders();
Platform.runLater(() -> result.invoke(list));
});
}
我想用Platform.runLater(...)
包装每个回调调用。
我想出的唯一一件事是default method
:
public interface Callback<T> {
void invoke(T param);
default void invokeOnMain(T param){
Platform.runLater(() -> invoke(param));
}
}
然后,我只是致电result.invokeOnMain(list)
。
对于像这样的模式,是否有更好的方法?
答案 0 :(得分:3)
您可以比其他答案中的建议更进一步,并将Platform.runLater()
抽象为java.util.concurrent.Executor
(毕竟,它是执行Runnable
s的东西。)< / p>
所以你可以这样做:
import java.util.concurrent.Executor ;
import java.util.function.Consumer ;
import java.util.function.Supplier ;
public class Invoker {
private final Executor backgroundExecutor ;
private final Executor foregroundExecutor ;
public Invoker(Executor backgroundExecutor, Executor foregroundExecutor) {
this.backgroundExecutor = backgroundExecutor ;
this.foregroundExecutor = foregroundExecutor ;
}
public <T> void invoke(Supplier<? extends T> task, Consumer<? super T> callback) {
backgroundExecutor.execute(() -> {
T result = task.get();
foregroundExecutor.execute(() -> callback.accept(result));
});
}
}
现在您的示例代码变为:
Invoker invoker = new Invoker(asyncExecutor, Platform::runLater);
// ...
invoker.invoke(folder::getSubFolders, result::invoke);
这里的好处是你可以在Swing中使用相同的Invoker
类:只需创建一个
new Invoker(asyncExecutor, SwingUtilities::invokeLater)
[注意:我自己没有提出这个问题;几年前我在这里的帖子中看到了它。我现在找不到该帖子给予适当的信任,但如果我设法将其挖掘出来,我会编辑它。如果最初发布此想法的人看到了这个,请发表评论,我会相信你的信息]
答案 1 :(得分:2)
它以某种方式击败了default method intention:
默认方法使您可以向界面添加新功能 您的库并确保与编写的代码的二进制兼容性 对于那些接口的旧版本。
为什么不将此代码放在特定的类中:
public class PlatformUtil {
public static <T> void invoke(Callback<T> result, T param){
Platform.runLater(() -> result.invoke(param));
}
}
从客户端,您还可以使用static import
PlatformUtil.invoke
来进一步减少锅炉铭牌代码。
它可以给:
import static PlatformUtil.invoke;
...
public void getSubfolders(Folder folder, Callback<FolderList> result){
asyncExecutor.submit(() -> {
FolderList list = folder.get_SubFolders();
invoke(result, list);
});
}
当然你可以用实例方法做同样的事情。
答案 2 :(得分:1)
使用Decorator Pattern后,您发现根本不需要更改getSubfolders
方法。然后,当您将组件放入其自己的包中时,您可以编写一个定义良好的层系统,例如:
// v--- move the UiCallback into ui package
package com.projectx.ui;
public class UiCallback<T> implements Callback<T> {
private final Callback<T> target;
private UiCallback(Callback<T> target){
this.target = Objects.requireNonNull(target);
}
public void invoke(T param){
Platform.runLater(() -> target.invoke(param));
}
public static <T> Callback<T> runOnMainThread(Callback<T> source){
return source instanceof UiCallback? source : new UiCallback<>(source);
}
}
一切都很好,只需要改变一个地方即可调用getSubfolders
,例如:
Callback<T> origin = ...
getSubfolders(folder, runOnMainThread(origin));
如果您发现需要在UI模块中多次调用runOnMainThread
,可能会在UI层中丢失一些域概念。你应该通过新的类或接口来提取新的域概念,例如:FolderExplorer
。