mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-09-05 12:17:00 +00:00
refactor: Eliminate Optional from fields and parameters for async method invocation (#2830)
This commit is contained in:
@@ -57,7 +57,8 @@ public interface AsyncResult<T> {
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public interface AsyncCallback<T> {
|
public interface AsyncCallback<T> {
|
||||||
void onComplete(T value, Optional<Exception> ex);
|
void onComplete(T value);
|
||||||
|
void onError(Exception ex);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ package com.iluwatar.async.method.invocation {
|
|||||||
+ main(args : String[]) {static}
|
+ main(args : String[]) {static}
|
||||||
}
|
}
|
||||||
interface AsyncCallback<T> {
|
interface AsyncCallback<T> {
|
||||||
+ onComplete(T, Optional<Exception>) {abstract}
|
+ onComplete(T) {abstract}
|
||||||
|
+ onError(Exception) {abstract}
|
||||||
}
|
}
|
||||||
interface AsyncExecutor {
|
interface AsyncExecutor {
|
||||||
+ endProcess(AsyncResult<T>) : T {abstract}
|
+ endProcess(AsyncResult<T>) : T {abstract}
|
||||||
@@ -32,7 +33,7 @@ package com.iluwatar.async.method.invocation {
|
|||||||
~ COMPLETED : int {static}
|
~ COMPLETED : int {static}
|
||||||
~ FAILED : int {static}
|
~ FAILED : int {static}
|
||||||
~ RUNNING : int {static}
|
~ RUNNING : int {static}
|
||||||
~ callback : Optional<AsyncCallback<T>>
|
~ callback : AsyncCallback<T>
|
||||||
~ exception : Exception
|
~ exception : Exception
|
||||||
~ lock : Object
|
~ lock : Object
|
||||||
~ state : int
|
~ state : int
|
||||||
@@ -40,12 +41,15 @@ package com.iluwatar.async.method.invocation {
|
|||||||
~ CompletableResult<T>(callback : AsyncCallback<T>)
|
~ CompletableResult<T>(callback : AsyncCallback<T>)
|
||||||
+ await()
|
+ await()
|
||||||
+ getValue() : T
|
+ getValue() : T
|
||||||
|
~ hasCallback() : boolean
|
||||||
+ isCompleted() : boolean
|
+ isCompleted() : boolean
|
||||||
~ setException(exception : Exception)
|
~ setException(exception : Exception)
|
||||||
~ setValue(value : T)
|
~ setValue(value : T)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CompletableResult ..+ ThreadAsyncExecutor
|
ThreadAsyncExecutor ..|> AsyncCallback
|
||||||
|
ThreadAsyncExecutor ..|> AsyncResult
|
||||||
ThreadAsyncExecutor ..|> AsyncExecutor
|
ThreadAsyncExecutor ..|> AsyncExecutor
|
||||||
|
CompletableResult ..+ ThreadAsyncExecutor
|
||||||
CompletableResult ..|> AsyncResult
|
CompletableResult ..|> AsyncResult
|
||||||
@enduml
|
@enduml
|
||||||
@@ -118,12 +118,16 @@ public class App {
|
|||||||
* @return new async callback
|
* @return new async callback
|
||||||
*/
|
*/
|
||||||
private static <T> AsyncCallback<T> callback(String name) {
|
private static <T> AsyncCallback<T> callback(String name) {
|
||||||
return (value, ex) -> {
|
return new AsyncCallback<>() {
|
||||||
if (ex.isPresent()) {
|
@Override
|
||||||
log(name + " failed: " + ex.map(Exception::getMessage).orElse(""));
|
public void onComplete(T value) {
|
||||||
} else {
|
|
||||||
log(name + " <" + value + ">");
|
log(name + " <" + value + ">");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception ex) {
|
||||||
|
log(name + " failed: " + ex.getMessage());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-6
@@ -24,8 +24,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.async.method.invocation;
|
package com.iluwatar.async.method.invocation;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AsyncCallback interface.
|
* AsyncCallback interface.
|
||||||
*
|
*
|
||||||
@@ -34,10 +32,16 @@ import java.util.Optional;
|
|||||||
public interface AsyncCallback<T> {
|
public interface AsyncCallback<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete handler which is executed when async task is completed or fails execution.
|
* Complete handler which is executed when async task is completed.
|
||||||
*
|
*
|
||||||
* @param value the evaluated value from async task, undefined when execution fails
|
* @param value the evaluated value from async task
|
||||||
* @param ex empty value if execution succeeds, some exception if executions fails
|
|
||||||
*/
|
*/
|
||||||
void onComplete(T value, Optional<Exception> ex);
|
void onComplete(T value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error handler which is executed when async task fails execution.
|
||||||
|
*
|
||||||
|
* @param ex exception which was thrown during async task execution(non-null)
|
||||||
|
*/
|
||||||
|
void onError(Exception ex);
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-4
@@ -81,7 +81,7 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
static final int COMPLETED = 3;
|
static final int COMPLETED = 3;
|
||||||
|
|
||||||
final Object lock;
|
final Object lock;
|
||||||
final Optional<AsyncCallback<T>> callback;
|
final AsyncCallback<T> callback;
|
||||||
|
|
||||||
volatile int state = RUNNING;
|
volatile int state = RUNNING;
|
||||||
T value;
|
T value;
|
||||||
@@ -89,7 +89,11 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
|
|
||||||
CompletableResult(AsyncCallback<T> callback) {
|
CompletableResult(AsyncCallback<T> callback) {
|
||||||
this.lock = new Object();
|
this.lock = new Object();
|
||||||
this.callback = Optional.ofNullable(callback);
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasCallback() {
|
||||||
|
return callback != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,7 +105,9 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
void setValue(T value) {
|
void setValue(T value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.state = COMPLETED;
|
this.state = COMPLETED;
|
||||||
this.callback.ifPresent(ac -> ac.onComplete(value, Optional.empty()));
|
if (hasCallback()) {
|
||||||
|
callback.onComplete(value);
|
||||||
|
}
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
lock.notifyAll();
|
lock.notifyAll();
|
||||||
}
|
}
|
||||||
@@ -116,7 +122,9 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
void setException(Exception exception) {
|
void setException(Exception exception) {
|
||||||
this.exception = exception;
|
this.exception = exception;
|
||||||
this.state = FAILED;
|
this.state = FAILED;
|
||||||
this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception)));
|
if (hasCallback()) {
|
||||||
|
callback.onError(exception);
|
||||||
|
}
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
lock.notifyAll();
|
lock.notifyAll();
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-26
@@ -25,15 +25,8 @@
|
|||||||
package com.iluwatar.async.method.invocation;
|
package com.iluwatar.async.method.invocation;
|
||||||
|
|
||||||
import static java.time.Duration.ofMillis;
|
import static java.time.Duration.ofMillis;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTimeout;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.ArgumentMatchers.isNull;
|
|
||||||
import static org.mockito.Mockito.timeout;
|
import static org.mockito.Mockito.timeout;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
@@ -58,7 +51,7 @@ import org.mockito.MockitoAnnotations;
|
|||||||
class ThreadAsyncExecutorTest {
|
class ThreadAsyncExecutorTest {
|
||||||
|
|
||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<Optional<Exception>> optionalCaptor;
|
private ArgumentCaptor<Exception> exceptionCaptor;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Callable<Object> task;
|
private Callable<Object> task;
|
||||||
@@ -118,11 +111,8 @@ class ThreadAsyncExecutorTest {
|
|||||||
verify(task, times(1)).call();
|
verify(task, times(1)).call();
|
||||||
|
|
||||||
// ... same for the callback, we expect our object
|
// ... same for the callback, we expect our object
|
||||||
verify(callback, times(1)).onComplete(eq(result), optionalCaptor.capture());
|
verify(callback, times(1)).onComplete(eq(result));
|
||||||
|
verify(callback, times(0)).onError(exceptionCaptor.capture());
|
||||||
final var optionalException = optionalCaptor.getValue();
|
|
||||||
assertNotNull(optionalException);
|
|
||||||
assertFalse(optionalException.isPresent());
|
|
||||||
|
|
||||||
// ... and the result should be exactly the same object
|
// ... and the result should be exactly the same object
|
||||||
assertSame(result, asyncResult.getValue());
|
assertSame(result, asyncResult.getValue());
|
||||||
@@ -200,11 +190,8 @@ class ThreadAsyncExecutorTest {
|
|||||||
|
|
||||||
// Our task should only execute once, but it can take a while ...
|
// Our task should only execute once, but it can take a while ...
|
||||||
verify(task, timeout(3000).times(1)).call();
|
verify(task, timeout(3000).times(1)).call();
|
||||||
verify(callback, timeout(3000).times(1)).onComplete(eq(result), optionalCaptor.capture());
|
verify(callback, timeout(3000).times(1)).onComplete(eq(result));
|
||||||
|
verify(callback, times(0)).onError(isA(Exception.class));
|
||||||
final var optionalException = optionalCaptor.getValue();
|
|
||||||
assertNotNull(optionalException);
|
|
||||||
assertFalse(optionalException.isPresent());
|
|
||||||
|
|
||||||
// Prevent timing issues, and wait until the result is available
|
// Prevent timing issues, and wait until the result is available
|
||||||
asyncResult.await();
|
asyncResult.await();
|
||||||
@@ -295,14 +282,12 @@ class ThreadAsyncExecutorTest {
|
|||||||
assertNotNull(asyncResult, "The AsyncResult should not be 'null', even though the task was 'null'.");
|
assertNotNull(asyncResult, "The AsyncResult should not be 'null', even though the task was 'null'.");
|
||||||
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
||||||
assertTrue(asyncResult.isCompleted());
|
assertTrue(asyncResult.isCompleted());
|
||||||
verify(callback, times(1)).onComplete(isNull(), optionalCaptor.capture());
|
verify(callback, times(0)).onComplete(any());
|
||||||
|
verify(callback, times(1)).onError(exceptionCaptor.capture());
|
||||||
|
|
||||||
final var optionalException = optionalCaptor.getValue();
|
final var exception = exceptionCaptor.getValue();
|
||||||
assertNotNull(optionalException);
|
|
||||||
assertTrue(optionalException.isPresent());
|
|
||||||
|
|
||||||
final var exception = optionalException.get();
|
|
||||||
assertNotNull(exception);
|
assertNotNull(exception);
|
||||||
|
|
||||||
assertEquals(NullPointerException.class, exception.getClass());
|
assertEquals(NullPointerException.class, exception.getClass());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user