mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-09-17 20:19:39 +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. */
|
/** Program entry point. */
|
||||||
public static void main(final String[] args) {
|
public static void main(final String[] args) {
|
||||||
var task = new SimpleTask();
|
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;
|
package com.iluwatar.callback;
|
||||||
|
|
||||||
/** Callback interface. */
|
/** Callback interface. */
|
||||||
|
@FunctionalInterface
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
|
|
||||||
void call();
|
void call();
|
||||||
|
|||||||
@@ -25,15 +25,30 @@
|
|||||||
package com.iluwatar.callback;
|
package com.iluwatar.callback;
|
||||||
|
|
||||||
import java.util.Optional;
|
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 {
|
public abstract class Task {
|
||||||
|
|
||||||
/** Execute with callback. */
|
/** Execute the task and call the callback method synchronously upon completion. */
|
||||||
final void executeWith(Callback callback) {
|
final void executeWith(Callback callback) {
|
||||||
execute();
|
execute();
|
||||||
Optional.ofNullable(callback).ifPresent(Callback::call);
|
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();
|
public abstract void execute();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ package com.iluwatar.callback;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
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;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,19 +42,44 @@ class CallbackTest {
|
|||||||
private Integer callingCount = 0;
|
private Integer callingCount = 0;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test() {
|
void testSynchronousCallback() {
|
||||||
Callback callback = () -> callingCount++;
|
var counter = new AtomicInteger();
|
||||||
|
Callback callback = counter::incrementAndGet;
|
||||||
var task = new SimpleTask();
|
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);
|
task.executeWith(callback);
|
||||||
|
assertEquals(1, counter.get(), "Callback should be called once");
|
||||||
assertEquals(Integer.valueOf(1), callingCount, "Callback called once");
|
|
||||||
|
|
||||||
task.executeWith(callback);
|
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