How to surround a callback call with another piece of code in the best way?

I have a simple, functional interface:

public interface Callback<T> {
    void invoke(T param);
}

I do a lot of asynchronous operations like:

public void getSubfolders(Folder folder, Callback<FolderList> result){
    asyncExecutor.submit(() -> {
        FolderList list = folder.get_SubFolders();
        result.invoke(list);
    });
}

Results should be processed on main thread. For this, I have a method JavaFX:

Platform.runLater(Runnable task);

What makes my code messy, like this one (and this pattern is repeated in 50 other methods):

public void getSubfolders(Folder folder, Callback<FolderList> result){
     asyncExecutor.submit(() -> {
         FolderList list = folder.get_SubFolders();
         Platform.runLater(() -> result.invoke(list));
     });
}

I would like to combine each callback call with Platform.runLater(...). The only thing I came up with is default method:

public interface Callback<T> {
    void invoke(T param);

    default void invokeOnMain(T param){
        Platform.runLater(() -> invoke(param));
    }
}

And then I just call result.invokeOnMain(list).

Is there a better approach for patterns like this?

+6
source share
3 answers

, , Platform.runLater() java.util.concurrent.Executor (, , -, Runnable s).

, :

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);

, Invoker Swing:

new Invoker(asyncExecutor, SwingUtilities::invokeLater)

[: ; . , , , . , , , , ]

+3

- :

.

:

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

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));

If you find that you need to be called many times runOnMainThreadin your user interface module, you may lose some domain concepts in your user interface. you must extract a new domain concept of new classes or interfaces for these things, for example: FolderExplorer.

+1
source

All Articles