Compare commits
9 Commits
all-contri
...
balking-ex
Author | SHA1 | Date | |
---|---|---|---|
c2126bef76 | |||
468cb1ec27 | |||
b5ed8b2278 | |||
8fc64a2d38 | |||
7e91322a43 | |||
e9106ccfc5 | |||
77ffae5ecc | |||
74abc7a0d6 | |||
ccf350b611 |
@ -1422,6 +1422,24 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "noamgrinch",
|
||||
"name": "Noam Greenshtain",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/31648669?v=4",
|
||||
"profile": "https://github.com/noamgrinch",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "qfxl",
|
||||
"name": "yonghong Xu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/14086462?v=4",
|
||||
"profile": "https://xuyonghong.cn/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 4,
|
||||
|
2
.github/workflows/maven-pr-builder.yml
vendored
2
.github/workflows/maven-pr-builder.yml
vendored
@ -29,7 +29,7 @@ name: Java PR Builder
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
types: [ opened, reopened, synchronize ]
|
||||
types: [ opened, reopened, synchronize, labeled, unlabeled ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -10,7 +10,7 @@
|
||||
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
||||
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors-)
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
<br/>
|
||||
@ -309,6 +309,10 @@ This project is licensed under the terms of the MIT license.
|
||||
<td align="center"><a href="https://github.com/richardmr36"><img src="https://avatars.githubusercontent.com/u/19147333?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martel Richard</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=richardmr36" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/va1m"><img src="https://avatars.githubusercontent.com/u/17025445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>va1m</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=va1m" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/noamgrinch"><img src="https://avatars.githubusercontent.com/u/31648669?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Noam Greenshtain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=noamgrinch" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://xuyonghong.cn/"><img src="https://avatars.githubusercontent.com/u/14086462?v=4?s=100" width="100px;" alt=""/><br /><sub><b>yonghong Xu</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qfxl" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
|
@ -79,12 +79,12 @@ public abstract class ActiveCreature{
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
We can see that any class that will extend the ActiveCreature class will have its own thread of control to execute and invocate methods.
|
||||
|
||||
For example, the Orc class:
|
||||
|
||||
```java
|
||||
public class Orc extends ActiveCreature {
|
||||
|
||||
@ -96,6 +96,7 @@ public class Orc extends ActiveCreature {
|
||||
```
|
||||
|
||||
Now, we can create multiple creatures such as Orcs, tell them to eat and roam and they will execute it on their own thread of control:
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
var app = new App();
|
||||
@ -121,4 +122,4 @@ Now, we can create multiple creatures such as Orcs, tell them to eat and roam an
|
||||
|
||||
## Class diagram
|
||||
|
||||

|
||||

|
||||
|
@ -1,3 +1,26 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2021 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.activeobject;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
@ -1,3 +1,26 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2021 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.activeobject;
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,26 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2021 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.activeobject;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -1,3 +1,26 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2021 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.activeobject;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
@ -9,21 +9,160 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Asynchronous method invocation is pattern where the calling thread
|
||||
|
||||
Asynchronous method invocation is a pattern where the calling thread
|
||||
is not blocked while waiting results of tasks. The pattern provides parallel
|
||||
processing of multiple independent tasks and retrieving the results via
|
||||
callbacks or waiting until everything is done.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> Launching space rockets is an exciting business. The mission command gives an order to launch and
|
||||
> after some undetermined time, the rocket either launches successfully or fails miserably.
|
||||
|
||||
In plain words
|
||||
|
||||
> Asynchronous method invocation starts task processing and returns immediately before the task is
|
||||
> ready. The results of the task processing are returned to the caller later.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In multithreaded computer programming, asynchronous method invocation (AMI), also known as
|
||||
> asynchronous method calls or the asynchronous pattern is a design pattern in which the call site
|
||||
> is not blocked while waiting for the called code to finish. Instead, the calling thread is
|
||||
> notified when the reply arrives. Polling for a reply is an undesired option.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
In this example, we are launching space rockets and deploying lunar rovers.
|
||||
|
||||
The application demonstrates the async method invocation pattern. The key parts of the pattern are
|
||||
`AsyncResult` which is an intermediate container for an asynchronously evaluated value,
|
||||
`AsyncCallback` which can be provided to be executed on task completion and `AsyncExecutor` that
|
||||
manages the execution of the async tasks.
|
||||
|
||||
```java
|
||||
public interface AsyncResult<T> {
|
||||
boolean isCompleted();
|
||||
T getValue() throws ExecutionException;
|
||||
void await() throws InterruptedException;
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public interface AsyncCallback<T> {
|
||||
void onComplete(T value, Optional<Exception> ex);
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public interface AsyncExecutor {
|
||||
<T> AsyncResult<T> startProcess(Callable<T> task);
|
||||
<T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback);
|
||||
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
|
||||
}
|
||||
```
|
||||
|
||||
`ThreadAsyncExecutor` is an implementation of `AsyncExecutor`. Some of its key parts are highlighted
|
||||
next.
|
||||
|
||||
```java
|
||||
public class ThreadAsyncExecutor implements AsyncExecutor {
|
||||
|
||||
@Override
|
||||
public <T> AsyncResult<T> startProcess(Callable<T> task) {
|
||||
return startProcess(task, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
|
||||
var result = new CompletableResult<>(callback);
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
result.setValue(task.call());
|
||||
} catch (Exception ex) {
|
||||
result.setException(ex);
|
||||
}
|
||||
},
|
||||
"executor-" + idx.incrementAndGet())
|
||||
.start();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T endProcess(AsyncResult<T> asyncResult)
|
||||
throws ExecutionException, InterruptedException {
|
||||
if (!asyncResult.isCompleted()) {
|
||||
asyncResult.await();
|
||||
}
|
||||
return asyncResult.getValue();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then we are ready to launch some rockets to see how everything works together.
|
||||
|
||||
```java
|
||||
public static void main(String[] args) throws Exception {
|
||||
// construct a new executor that will run async tasks
|
||||
var executor = new ThreadAsyncExecutor();
|
||||
|
||||
// start few async tasks with varying processing times, two last with callback handlers
|
||||
final var asyncResult1 = executor.startProcess(lazyval(10, 500));
|
||||
final var asyncResult2 = executor.startProcess(lazyval("test", 300));
|
||||
final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
||||
final var asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Deploying lunar rover"));
|
||||
final var asyncResult5 =
|
||||
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
|
||||
|
||||
// emulate processing in the current thread while async tasks are running in their own threads
|
||||
Thread.sleep(350); // Oh boy, we are working hard here
|
||||
log("Mission command is sipping coffee");
|
||||
|
||||
// wait for completion of the tasks
|
||||
final var result1 = executor.endProcess(asyncResult1);
|
||||
final var result2 = executor.endProcess(asyncResult2);
|
||||
final var result3 = executor.endProcess(asyncResult3);
|
||||
asyncResult4.await();
|
||||
asyncResult5.await();
|
||||
|
||||
// log the results of the tasks, callbacks log immediately when complete
|
||||
log("Space rocket <" + result1 + "> launch complete");
|
||||
log("Space rocket <" + result2 + "> launch complete");
|
||||
log("Space rocket <" + result3 + "> launch complete");
|
||||
}
|
||||
```
|
||||
|
||||
Here's the program console output.
|
||||
|
||||
```java
|
||||
21:47:08.227 [executor-2] INFO com.iluwatar.async.method.invocation.App - Space rocket <test> launched successfully
|
||||
21:47:08.269 [main] INFO com.iluwatar.async.method.invocation.App - Mission command is sipping coffee
|
||||
21:47:08.318 [executor-4] INFO com.iluwatar.async.method.invocation.App - Space rocket <20> launched successfully
|
||||
21:47:08.335 [executor-4] INFO com.iluwatar.async.method.invocation.App - Deploying lunar rover <20>
|
||||
21:47:08.414 [executor-1] INFO com.iluwatar.async.method.invocation.App - Space rocket <10> launched successfully
|
||||
21:47:08.519 [executor-5] INFO com.iluwatar.async.method.invocation.App - Space rocket <callback> launched successfully
|
||||
21:47:08.519 [executor-5] INFO com.iluwatar.async.method.invocation.App - Deploying lunar rover <callback>
|
||||
21:47:08.616 [executor-3] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launched successfully
|
||||
21:47:08.617 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <10> launch complete
|
||||
21:47:08.617 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <test> launch complete
|
||||
21:47:08.618 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launch complete
|
||||
```
|
||||
|
||||
# Class diagram
|
||||
|
||||

|
||||
|
||||
## Applicability
|
||||
Use async method invocation pattern when
|
||||
|
||||
Use the async method invocation pattern when
|
||||
|
||||
* You have multiple independent tasks that can run in parallel
|
||||
* You need to improve the performance of a group of sequential tasks
|
||||
* You have limited amount of processing capacity or long running tasks and the
|
||||
caller should not wait the tasks to be ready
|
||||
* You have a limited amount of processing capacity or long-running tasks and the caller should not wait for the tasks to be ready
|
||||
|
||||
## Real world examples
|
||||
|
||||
|
@ -27,10 +27,12 @@ import java.util.concurrent.Callable;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
|
||||
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated
|
||||
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and
|
||||
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
|
||||
* In this example, we are launching space rockets and deploying lunar rovers.
|
||||
*
|
||||
* <p>The application demonstrates the async method invocation pattern. The key parts of the
|
||||
* pattern are <code>AsyncResult</code> which is an intermediate container for an asynchronously
|
||||
* evaluated value, <code>AsyncCallback</code> which can be provided to be executed on task
|
||||
* completion and <code>AsyncExecutor</code> that manages the execution of the async tasks.
|
||||
*
|
||||
* <p>The main method shows example flow of async invocations. The main thread starts multiple
|
||||
* tasks with variable durations and then continues its own work. When the main thread has done it's
|
||||
@ -68,13 +70,14 @@ public class App {
|
||||
final var asyncResult1 = executor.startProcess(lazyval(10, 500));
|
||||
final var asyncResult2 = executor.startProcess(lazyval("test", 300));
|
||||
final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
||||
final var asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
|
||||
final var asyncResult4 = executor.startProcess(lazyval(20, 400),
|
||||
callback("Deploying lunar rover"));
|
||||
final var asyncResult5 =
|
||||
executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
|
||||
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
|
||||
|
||||
// emulate processing in the current thread while async tasks are running in their own threads
|
||||
Thread.sleep(350); // Oh boy I'm working hard here
|
||||
log("Some hard work done");
|
||||
Thread.sleep(350); // Oh boy, we are working hard here
|
||||
log("Mission command is sipping coffee");
|
||||
|
||||
// wait for completion of the tasks
|
||||
final var result1 = executor.endProcess(asyncResult1);
|
||||
@ -84,9 +87,9 @@ public class App {
|
||||
asyncResult5.await();
|
||||
|
||||
// log the results of the tasks, callbacks log immediately when complete
|
||||
log("Result 1: " + result1);
|
||||
log("Result 2: " + result2);
|
||||
log("Result 3: " + result3);
|
||||
log("Space rocket <" + result1 + "> launch complete");
|
||||
log("Space rocket <" + result2 + "> launch complete");
|
||||
log("Space rocket <" + result3 + "> launch complete");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,7 +102,7 @@ public class App {
|
||||
private static <T> Callable<T> lazyval(T value, long delayMillis) {
|
||||
return () -> {
|
||||
Thread.sleep(delayMillis);
|
||||
log("Task completed with: " + value);
|
||||
log("Space rocket <" + value + "> launched successfully");
|
||||
return value;
|
||||
};
|
||||
}
|
||||
@ -115,7 +118,7 @@ public class App {
|
||||
if (ex.isPresent()) {
|
||||
log(name + " failed: " + ex.map(Exception::getMessage).orElse(""));
|
||||
} else {
|
||||
log(name + ": " + value);
|
||||
log(name + " <" + value + ">");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -9,19 +9,131 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Balking Pattern is used to prevent an object from executing certain code if it is an
|
||||
incomplete or inappropriate state
|
||||
|
||||
Balking Pattern is used to prevent an object from executing a certain code if it is in an incomplete
|
||||
or inappropriate state.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> There's a start-button in a washing machine to initiate the laundry washing. When the washing
|
||||
> machine is inactive the button works as expected, but if it's already washing the button does
|
||||
> nothing.
|
||||
|
||||
In plain words
|
||||
|
||||
> Using the balking pattern, a certain code executes only if the object is in particular state.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> The balking pattern is a software design pattern that only executes an action on an object when
|
||||
> the object is in a particular state. For example, if an object reads ZIP files and a calling
|
||||
> method invokes a get method on the object when the ZIP file is not open, the object would "balk"
|
||||
> at the request.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
In this example implementation, `WashingMachine` is an object that has two states in which it can
|
||||
be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING using a thread-safe
|
||||
method. On the other hand, if it already has been washing and any other thread executes `wash()`
|
||||
it won't do that and returns without doing anything.
|
||||
|
||||
Here are the relevant parts of the `WashingMachine` class.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class WashingMachine {
|
||||
|
||||
private final DelayProvider delayProvider;
|
||||
private WashingMachineState washingMachineState;
|
||||
|
||||
public WashingMachine(DelayProvider delayProvider) {
|
||||
this.delayProvider = delayProvider;
|
||||
this.washingMachineState = WashingMachineState.ENABLED;
|
||||
}
|
||||
|
||||
public WashingMachineState getWashingMachineState() {
|
||||
return washingMachineState;
|
||||
}
|
||||
|
||||
public void wash() {
|
||||
synchronized (this) {
|
||||
var machineState = getWashingMachineState();
|
||||
LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState);
|
||||
if (this.washingMachineState == WashingMachineState.WASHING) {
|
||||
LOGGER.error("Cannot wash if the machine has been already washing!");
|
||||
return;
|
||||
}
|
||||
this.washingMachineState = WashingMachineState.WASHING;
|
||||
}
|
||||
LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
|
||||
this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
|
||||
}
|
||||
|
||||
public synchronized void endOfWashing() {
|
||||
washingMachineState = WashingMachineState.ENABLED;
|
||||
LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the simple `DelayProvider` interface used by the `WashingMachine`.
|
||||
|
||||
```java
|
||||
public interface DelayProvider {
|
||||
void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
|
||||
}
|
||||
```
|
||||
|
||||
Now we introduce the application using the `WashingMachine`.
|
||||
|
||||
```java
|
||||
public static void main(String... args) {
|
||||
final var washingMachine = new WashingMachine();
|
||||
var executorService = Executors.newFixedThreadPool(3);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
executorService.execute(washingMachine::wash);
|
||||
}
|
||||
executorService.shutdown();
|
||||
try {
|
||||
executorService.awaitTermination(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ie) {
|
||||
LOGGER.error("ERROR: Waiting on executor service shutdown!");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here is the console output of the program.
|
||||
|
||||
```
|
||||
14:02:52.268 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Actual machine state: ENABLED
|
||||
14:02:52.272 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Doing the washing
|
||||
14:02:52.272 [pool-1-thread-3] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-3: Actual machine state: WASHING
|
||||
14:02:52.273 [pool-1-thread-3] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing!
|
||||
14:02:52.273 [pool-1-thread-1] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-1: Actual machine state: WASHING
|
||||
14:02:52.273 [pool-1-thread-1] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing!
|
||||
14:02:52.324 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed.
|
||||
```
|
||||
|
||||
## Class diagram
|
||||
|
||||

|
||||
|
||||
## Applicability
|
||||
|
||||
Use the Balking pattern when
|
||||
|
||||
* you want to invoke an action on an object only when it is in a particular state
|
||||
* objects are generally only in a state that is prone to balking temporarily
|
||||
but for an unknown amount of time
|
||||
* You want to invoke an action on an object only when it is in a particular state
|
||||
* Objects are generally only in a state that is prone to balking temporarily but for an unknown
|
||||
amount of time
|
||||
|
||||
## Related patterns
|
||||
* Guarded Suspension Pattern
|
||||
* Double Checked Locking Pattern
|
||||
|
||||
* [Guarded Suspension Pattern](https://java-design-patterns.com/patterns/guarded-suspension/)
|
||||
* [Double Checked Locking Pattern](https://java-design-patterns.com/patterns/double-checked-locking/)
|
||||
|
||||
## Credits
|
||||
|
||||
* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML, 2nd Edition, Volume 1](https://www.amazon.com/gp/product/0471227293/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0471227293&linkId=0e39a59ffaab93fb476036fecb637b99)
|
||||
|
@ -32,11 +32,11 @@ import lombok.extern.slf4j.Slf4j;
|
||||
* then the method will return without doing anything. Objects that use this pattern are generally
|
||||
* only in a state that is prone to balking temporarily but for an unknown amount of time
|
||||
*
|
||||
* <p>In this example implementation WashingMachine is an object that has two states in which it
|
||||
* can be: ENABLED and WASHING. If the machine is ENABLED the state is changed into WASHING that any
|
||||
* other thread can't invoke this action on this and then do the job. On the other hand if it have
|
||||
* been already washing and any other thread execute wash() it can't do that once again and returns
|
||||
* doing nothing.
|
||||
* <p>In this example implementation, {@link WashingMachine} is an object that has two states in
|
||||
* which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING
|
||||
* using a thread-safe method. On the other hand, if it already has been washing and any other
|
||||
* thread executes {@link WashingMachine#wash()} it won't do that and returns without doing
|
||||
* anything.
|
||||
*/
|
||||
@Slf4j
|
||||
public class App {
|
||||
@ -54,11 +54,12 @@ public class App {
|
||||
}
|
||||
executorService.shutdown();
|
||||
try {
|
||||
executorService.awaitTermination(10, TimeUnit.SECONDS);
|
||||
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
LOGGER.error("ERROR: Waiting on executor service shutdown!");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ Program output:
|
||||
|
||||
```java
|
||||
This is Ford.
|
||||
This Ferrari.
|
||||
This is Ferrari.
|
||||
```
|
||||
|
||||
## Class Diagram
|
||||
|
Reference in New Issue
Block a user