feat: Refactor Callback pattern to support asynchronous callbacks (#3274)

* refactor: Refactor Task execution to support asynchronous callbacks

* fix: CallbackTest

* chore: code style

* feat: display synchronous and asynchronous callbacks
This commit is contained in:
HY-love-sleep
2026-05-31 18:17:23 +03:00
committed by GitHub
parent 9e65023a9f
commit b93a202d60
4 changed files with 61 additions and 12 deletions
@@ -39,6 +39,11 @@ public final class App {
/** Program entry point. */
public static void main(final String[] args) {
var task = new SimpleTask();
task.executeWith(() -> LOGGER.info("I'm done now."));
LOGGER.info("=== Synchronous callback ===");
task.executeWith(() -> LOGGER.info("Sync callback executed."));
LOGGER.info("=== Asynchronous callback ===");
task.executeAsyncWith(() -> LOGGER.info("Async callback executed.")).join();
}
}
@@ -25,6 +25,7 @@
package com.iluwatar.callback;
/** Callback interface. */
@FunctionalInterface
public interface Callback {
void call();
@@ -25,15 +25,30 @@
package com.iluwatar.callback;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
/** Template-method class for callback hook execution. */
/**
* Template-method class for callback hook execution.
*
* <p>Provides both synchronous and asynchronous execution with callback support.
*/
public abstract class Task {
/** Execute with callback. */
/** Execute the task and call the callback method synchronously upon completion. */
final void executeWith(Callback callback) {
execute();
Optional.ofNullable(callback).ifPresent(Callback::call);
}
/** Execute the task and asynchronously call the callback method upon completion. */
final CompletableFuture<Void> executeAsyncWith(Callback callback) {
return CompletableFuture.runAsync(
() -> {
execute();
Optional.ofNullable(callback).ifPresent(Callback::call);
});
}
/** Actual work to be implemented by subclasses. */
public abstract void execute();
}
@@ -26,6 +26,9 @@ package com.iluwatar.callback;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;
/**
@@ -39,19 +42,44 @@ class CallbackTest {
private Integer callingCount = 0;
@Test
void test() {
Callback callback = () -> callingCount++;
void testSynchronousCallback() {
var counter = new AtomicInteger();
Callback callback = counter::incrementAndGet;
var task = new SimpleTask();
assertEquals(Integer.valueOf(0), callingCount, "Initial calling count of 0");
assertEquals(0, counter.get(), "Initial count should be 0");
task.executeWith(callback);
assertEquals(Integer.valueOf(1), callingCount, "Callback called once");
assertEquals(1, counter.get(), "Callback should be called once");
task.executeWith(callback);
assertEquals(2, counter.get(), "Callback should be called twice");
}
assertEquals(Integer.valueOf(2), callingCount, "Callback called twice");
@Test
void testAsynchronousCallback() {
var task = new SimpleTask();
var counter1 = new AtomicInteger();
final CompletableFuture<Void> future1 = new CompletableFuture<>();
Callback callback1 =
() -> {
counter1.incrementAndGet();
future1.complete(null);
};
var f1 = task.executeAsyncWith(callback1);
future1.orTimeout(1, TimeUnit.SECONDS).join();
f1.join();
assertEquals(1, counter1.get(), "Async callback should increment once");
var counter2 = new AtomicInteger();
final CompletableFuture<Void> future2 = new CompletableFuture<>();
Callback callback2 =
() -> {
counter2.incrementAndGet();
future2.complete(null);
};
var f2 = task.executeAsyncWith(callback2);
future2.orTimeout(1, TimeUnit.SECONDS).join();
f2.join();
assertEquals(1, counter2.get(), "Async callback should increment once again");
}
}