mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-09-12 16:18:37 +00:00
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:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user