Compare commits
22 Commits
all-contri
...
all-contri
Author | SHA1 | Date | |
---|---|---|---|
bea39206ed | |||
c3228fd6d2 | |||
af0ccdc6e1 | |||
323dd63e66 | |||
be3250bd0d | |||
1222f12b99 | |||
965d38f139 | |||
eb8f9db575 | |||
6d7084f18d | |||
74f5cfa670 | |||
b525d871b4 | |||
c413e0902e | |||
7ac468db20 | |||
794795acf5 | |||
2b7d181ac4 | |||
b5ed8b2278 | |||
8fc64a2d38 | |||
7e91322a43 | |||
e9106ccfc5 | |||
77ffae5ecc | |||
74abc7a0d6 | |||
ccf350b611 |
@ -1132,7 +1132,8 @@
|
||||
"profile": "http://subho.xyz",
|
||||
"contributions": [
|
||||
"code",
|
||||
"review"
|
||||
"review",
|
||||
"maintenance"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -1431,6 +1432,34 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "qfxl",
|
||||
"name": "yonghong Xu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/14086462?v=4",
|
||||
"profile": "https://xuyonghong.cn/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jinishavora",
|
||||
"name": "jinishavora",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/40777762?v=4",
|
||||
"profile": "https://www.linkedin.com/in/jinisha-vora",
|
||||
"contributions": [
|
||||
"review",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "eas5",
|
||||
"name": "Elvys Soares",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/50836521?v=4",
|
||||
"profile": "https://github.com/eas5",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 4,
|
||||
|
1
.github/workflows/maven-ci.yml
vendored
1
.github/workflows/maven-ci.yml
vendored
@ -49,6 +49,7 @@ jobs:
|
||||
uses: actions/setup-java@master
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@master
|
||||
|
1
.github/workflows/maven-pr-builder.yml
vendored
1
.github/workflows/maven-pr-builder.yml
vendored
@ -44,6 +44,7 @@ jobs:
|
||||
uses: actions/setup-java@master
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Cache Maven Dependecies
|
||||
uses: actions/cache@master
|
||||
|
@ -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/>
|
||||
@ -259,7 +259,7 @@ This project is licensed under the terms of the MIT license.
|
||||
<td align="center"><a href="https://www.linkedin.com/in/ashish-trivedi-218379135/"><img src="https://avatars3.githubusercontent.com/u/23194128?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ashish Trivedi</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ashishtrivedi16" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://rayyounghong.com"><img src="https://avatars1.githubusercontent.com/u/41055099?v=4?s=100" width="100px;" alt=""/><br /><sub><b>洪月阳</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=RayYH" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://xdvrx1.github.io/"><img src="https://avatars0.githubusercontent.com/u/47092464?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xdvrx1</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Axdvrx1" title="Reviewed Pull Requests">👀</a> <a href="#ideas-xdvrx1" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="http://subho.xyz"><img src="https://avatars0.githubusercontent.com/u/13291222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Subhrodip Mohanta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ohbus" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aohbus" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="http://subho.xyz"><img src="https://avatars0.githubusercontent.com/u/13291222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Subhrodip Mohanta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ohbus" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aohbus" title="Reviewed Pull Requests">👀</a> <a href="#maintenance-ohbus" title="Maintenance">🚧</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/nahteb"><img src="https://avatars3.githubusercontent.com/u/13121570?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bethan Palmer</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nahteb" title="Code">💻</a></td>
|
||||
@ -311,6 +311,9 @@ This project is licensed under the terms of the MIT license.
|
||||
</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>
|
||||
<td align="center"><a href="https://www.linkedin.com/in/jinisha-vora"><img src="https://avatars.githubusercontent.com/u/40777762?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jinishavora</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Ajinishavora" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=jinishavora" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/eas5"><img src="https://avatars.githubusercontent.com/u/50836521?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Elvys Soares</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=eas5" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>abstract-document</artifactId>
|
||||
<dependencies>
|
||||
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>abstract-factory</artifactId>
|
||||
<dependencies>
|
||||
|
@ -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
|
||||
|
||||

|
||||

|
||||
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>active-object</artifactId>
|
||||
<dependencies>
|
||||
|
@ -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;
|
||||
|
@ -30,7 +30,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>acyclic-visitor</artifactId>
|
||||
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>adapter</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>aggregator-microservices</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>aggregator-service</artifactId>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>aggregator-microservices</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>aggregator-microservices</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>inventory-microservice</artifactId>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>aggregator-microservices</artifactId>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>ambassador</artifactId>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>api-gateway</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>api-gateway-service</artifactId>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>api-gateway</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>image-microservice</artifactId>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>api-gateway</artifactId>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>api-gateway</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>async-method-invocation</artifactId>
|
||||
<dependencies>
|
||||
|
@ -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)
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>bridge</artifactId>
|
||||
<dependencies>
|
||||
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>builder</artifactId>
|
||||
<dependencies>
|
||||
|
@ -9,21 +9,156 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
|
||||
The Business Delegate pattern adds an abstraction layer between
|
||||
presentation and business tiers. By using the pattern we gain loose coupling
|
||||
between the tiers and encapsulate knowledge about how to locate, connect to,
|
||||
and interact with the business objects that make up the application.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> A mobile phone application promises to stream any movie in existence to your phone. It captures
|
||||
> the user's search string and passes this on to the business delegate. The business delegate
|
||||
> selects the most suitable video streaming service and plays the video from there.
|
||||
|
||||
In Plain Words
|
||||
|
||||
> Business delegate adds an abstraction layer between the presentation and business tiers.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> Business delegate is a Java EE design pattern. This pattern is directing to reduce the coupling
|
||||
> in between business services and the connected presentation tier, and to hide the implementation
|
||||
> details of services (including lookup and accessibility of EJB architecture). Business delegates
|
||||
> acts as an adaptor to invoke business objects from the presentation tier.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
First, we have an abstraction for video streaming services and a couple of implementations.
|
||||
|
||||
```java
|
||||
public interface VideoStreamingService {
|
||||
void doProcessing();
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class NetflixService implements VideoStreamingService {
|
||||
@Override
|
||||
public void doProcessing() {
|
||||
LOGGER.info("NetflixService is now processing");
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class YouTubeService implements VideoStreamingService {
|
||||
@Override
|
||||
public void doProcessing() {
|
||||
LOGGER.info("YouTubeService is now processing");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then we have a lookup service that decides which video streaming service is used.
|
||||
|
||||
```java
|
||||
@Setter
|
||||
public class BusinessLookup {
|
||||
|
||||
private NetflixService netflixService;
|
||||
private YouTubeService youTubeService;
|
||||
|
||||
public VideoStreamingService getBusinessService(String movie) {
|
||||
if (movie.toLowerCase(Locale.ROOT).contains("die hard")) {
|
||||
return netflixService;
|
||||
} else {
|
||||
return youTubeService;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The business delegate uses a business lookup to route movie playback requests to a suitable
|
||||
video streaming service.
|
||||
|
||||
```java
|
||||
@Setter
|
||||
public class BusinessDelegate {
|
||||
|
||||
private BusinessLookup lookupService;
|
||||
|
||||
public void playbackMovie(String movie) {
|
||||
VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie);
|
||||
videoStreamingService.doProcessing();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The mobile client utilizes business delegate to call the business tier.
|
||||
|
||||
```java
|
||||
public class MobileClient {
|
||||
|
||||
private final BusinessDelegate businessDelegate;
|
||||
|
||||
public MobileClient(BusinessDelegate businessDelegate) {
|
||||
this.businessDelegate = businessDelegate;
|
||||
}
|
||||
|
||||
public void playbackMovie(String movie) {
|
||||
businessDelegate.playbackMovie(movie);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Finally, we can show the full example in action.
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
|
||||
// prepare the objects
|
||||
var businessDelegate = new BusinessDelegate();
|
||||
var businessLookup = new BusinessLookup();
|
||||
businessLookup.setNetflixService(new NetflixService());
|
||||
businessLookup.setYouTubeService(new YouTubeService());
|
||||
businessDelegate.setLookupService(businessLookup);
|
||||
|
||||
// create the client and use the business delegate
|
||||
var client = new MobileClient(businessDelegate);
|
||||
client.playbackMovie("Die Hard 2");
|
||||
client.playbackMovie("Maradona: The Greatest Ever");
|
||||
}
|
||||
```
|
||||
|
||||
Here is the console output.
|
||||
|
||||
```
|
||||
21:15:33.790 [main] INFO com.iluwatar.business.delegate.NetflixService - NetflixService is now processing
|
||||
21:15:33.794 [main] INFO com.iluwatar.business.delegate.YouTubeService - YouTubeService is now processing
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||

|
||||
|
||||
## Related patterns
|
||||
|
||||
* [Service locator pattern](https://java-design-patterns.com/patterns/service-locator/)
|
||||
|
||||
## Applicability
|
||||
|
||||
Use the Business Delegate pattern when
|
||||
|
||||
* you want loose coupling between presentation and business tiers
|
||||
* you want to orchestrate calls to multiple business services
|
||||
* you want to encapsulate service lookups and service calls
|
||||
* You want loose coupling between presentation and business tiers
|
||||
* You want to orchestrate calls to multiple business services
|
||||
* You want to encapsulate service lookups and service calls
|
||||
|
||||
## Tutorials
|
||||
|
||||
* [Business Delegate Pattern at TutorialsPoint](https://www.tutorialspoint.com/design_pattern/business_delegate_pattern.htm)
|
||||
|
||||
## Credits
|
||||
|
||||
* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
|
||||
* [Core J2EE Patterns: Best Practices and Design Strategies](https://www.amazon.com/gp/product/0130648841/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0130648841&linkId=a0100de2b28c71ede8db1757fb2b5947)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
@ -1,136 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
|
||||
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
|
||||
<class id="1" language="java" name="com.iluwatar.business.delegate.BusinessDelegate" project="business-delegate"
|
||||
file="/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="272" y="219"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="2" language="java" name="com.iluwatar.business.delegate.Client" project="business-delegate"
|
||||
file="/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="272" y="64"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<enumeration id="3" language="java" name="com.iluwatar.business.delegate.ServiceType" project="business-delegate"
|
||||
file="/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="89" y="383"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</enumeration>
|
||||
<interface id="4" language="java" name="com.iluwatar.business.delegate.BusinessService" project="business-delegate"
|
||||
file="/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="630" y="365"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</interface>
|
||||
<class id="5" language="java" name="com.iluwatar.business.delegate.JmsService" project="business-delegate"
|
||||
file="/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="489" y="210"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="6" language="java" name="com.iluwatar.business.delegate.BusinessLookup" project="business-delegate"
|
||||
file="/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="360" y="399"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="7" language="java" name="com.iluwatar.business.delegate.EjbService" project="business-delegate"
|
||||
file="/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="668" y="210"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<realization id="8">
|
||||
<end type="SOURCE" refId="7"/>
|
||||
<end type="TARGET" refId="4"/>
|
||||
</realization>
|
||||
<association id="9">
|
||||
<end type="SOURCE" refId="1" navigable="false">
|
||||
<attribute id="10" name="lookupService">
|
||||
<position height="0" width="0" x="0" y="0"/>
|
||||
</attribute>
|
||||
<multiplicity id="11" minimum="0" maximum="1">
|
||||
<position height="0" width="0" x="0" y="0"/>
|
||||
</multiplicity>
|
||||
</end>
|
||||
<end type="TARGET" refId="6" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<association id="12">
|
||||
<end type="SOURCE" refId="1" navigable="false">
|
||||
<attribute id="13" name="serviceType">
|
||||
<position height="0" width="0" x="0" y="0"/>
|
||||
</attribute>
|
||||
<multiplicity id="14" minimum="0" maximum="1">
|
||||
<position height="0" width="0" x="0" y="0"/>
|
||||
</multiplicity>
|
||||
</end>
|
||||
<end type="TARGET" refId="3" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<realization id="15">
|
||||
<end type="SOURCE" refId="5"/>
|
||||
<end type="TARGET" refId="4"/>
|
||||
</realization>
|
||||
<association id="16">
|
||||
<end type="SOURCE" refId="2" navigable="false">
|
||||
<attribute id="17" name="businessDelegate">
|
||||
<position height="0" width="0" x="0" y="0"/>
|
||||
</attribute>
|
||||
<multiplicity id="18" minimum="0" maximum="1">
|
||||
<position height="0" width="0" x="0" y="0"/>
|
||||
</multiplicity>
|
||||
</end>
|
||||
<end type="TARGET" refId="1" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<association id="19">
|
||||
<end type="SOURCE" refId="1" navigable="false">
|
||||
<attribute id="20" name="businessService">
|
||||
<position height="0" width="0" x="0" y="0"/>
|
||||
</attribute>
|
||||
<multiplicity id="21" minimum="0" maximum="1">
|
||||
<position height="0" width="0" x="0" y="0"/>
|
||||
</multiplicity>
|
||||
</end>
|
||||
<end type="TARGET" refId="4" navigable="true"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</classifier-display>
|
||||
<association-display labels="true" multiplicity="true"/>
|
||||
</class-diagram>
|
BIN
business-delegate/etc/business-delegate.urm.png
Normal file
BIN
business-delegate/etc/business-delegate.urm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
@ -5,53 +5,42 @@ package com.iluwatar.business.delegate {
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
class BusinessDelegate {
|
||||
- businessService : BusinessService
|
||||
- lookupService : BusinessLookup
|
||||
- serviceType : ServiceType
|
||||
+ BusinessDelegate()
|
||||
+ doTask()
|
||||
+ setLookupService(businessLookup : BusinessLookup)
|
||||
+ setServiceType(serviceType : ServiceType)
|
||||
+ playbackMovie(movie : String)
|
||||
+ setLookupService(lookupService : BusinessLookup)
|
||||
}
|
||||
class BusinessLookup {
|
||||
- ejbService : EjbService
|
||||
- jmsService : JmsService
|
||||
- netflixService : NetflixService
|
||||
- youTubeService : YouTubeService
|
||||
+ BusinessLookup()
|
||||
+ getBusinessService(serviceType : ServiceType) : BusinessService
|
||||
+ setEjbService(ejbService : EjbService)
|
||||
+ setJmsService(jmsService : JmsService)
|
||||
+ getBusinessService(movie : String) : VideoStreamingService
|
||||
+ setNetflixService(netflixService : NetflixService)
|
||||
+ setYouTubeService(youTubeService : YouTubeService)
|
||||
}
|
||||
interface BusinessService {
|
||||
class MobileClient {
|
||||
- businessDelegate : BusinessDelegate
|
||||
+ MobileClient(businessDelegate : BusinessDelegate)
|
||||
+ playbackMovie(movie : String)
|
||||
}
|
||||
class NetflixService {
|
||||
- LOGGER : Logger {static}
|
||||
+ NetflixService()
|
||||
+ doProcessing()
|
||||
}
|
||||
interface VideoStreamingService {
|
||||
+ doProcessing() {abstract}
|
||||
}
|
||||
class Client {
|
||||
- businessDelegate : BusinessDelegate
|
||||
+ Client(businessDelegate : BusinessDelegate)
|
||||
+ doTask()
|
||||
}
|
||||
class EjbService {
|
||||
class YouTubeService {
|
||||
- LOGGER : Logger {static}
|
||||
+ EjbService()
|
||||
+ YouTubeService()
|
||||
+ doProcessing()
|
||||
}
|
||||
class JmsService {
|
||||
- LOGGER : Logger {static}
|
||||
+ JmsService()
|
||||
+ doProcessing()
|
||||
}
|
||||
enum ServiceType {
|
||||
+ EJB {static}
|
||||
+ JMS {static}
|
||||
+ valueOf(name : String) : ServiceType {static}
|
||||
+ values() : ServiceType[] {static}
|
||||
}
|
||||
}
|
||||
BusinessLookup --> "-ejbService" EjbService
|
||||
BusinessDelegate --> "-serviceType" ServiceType
|
||||
Client --> "-businessDelegate" BusinessDelegate
|
||||
BusinessDelegate --> "-businessService" BusinessService
|
||||
BusinessLookup --> "-netflixService" NetflixService
|
||||
BusinessLookup --> "-youTubeService" YouTubeService
|
||||
MobileClient --> "-businessDelegate" BusinessDelegate
|
||||
BusinessDelegate --> "-lookupService" BusinessLookup
|
||||
BusinessLookup --> "-jmsService" JmsService
|
||||
EjbService ..|> BusinessService
|
||||
JmsService ..|> BusinessService
|
||||
NetflixService ..|> VideoStreamingService
|
||||
YouTubeService ..|> VideoStreamingService
|
||||
@enduml
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>business-delegate</artifactId>
|
||||
<dependencies>
|
||||
|
@ -33,9 +33,9 @@ package com.iluwatar.business.delegate;
|
||||
* retrieved through service lookups. The Business Delegate itself may contain business logic too
|
||||
* potentially tying together multiple service calls, exception handling, retrying etc.
|
||||
*
|
||||
* <p>In this example the client ({@link Client}) utilizes a business delegate (
|
||||
* {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
|
||||
* service and makes the service call.
|
||||
* <p>In this example the client ({@link MobileClient}) utilizes a business delegate (
|
||||
* {@link BusinessDelegate}) to search for movies in video streaming services. The Business Delegate
|
||||
* then selects the appropriate service and makes the service call.
|
||||
*/
|
||||
public class App {
|
||||
|
||||
@ -46,18 +46,16 @@ public class App {
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
// prepare the objects
|
||||
var businessDelegate = new BusinessDelegate();
|
||||
var businessLookup = new BusinessLookup();
|
||||
businessLookup.setEjbService(new EjbService());
|
||||
businessLookup.setJmsService(new JmsService());
|
||||
|
||||
businessLookup.setNetflixService(new NetflixService());
|
||||
businessLookup.setYouTubeService(new YouTubeService());
|
||||
businessDelegate.setLookupService(businessLookup);
|
||||
businessDelegate.setServiceType(ServiceType.EJB);
|
||||
|
||||
var client = new Client(businessDelegate);
|
||||
client.doTask();
|
||||
|
||||
businessDelegate.setServiceType(ServiceType.JMS);
|
||||
client.doTask();
|
||||
// create the client and use the business delegate
|
||||
var client = new MobileClient(businessDelegate);
|
||||
client.playbackMovie("Die Hard 2");
|
||||
client.playbackMovie("Maradona: The Greatest Ever");
|
||||
}
|
||||
}
|
||||
|
@ -23,24 +23,18 @@
|
||||
|
||||
package com.iluwatar.business.delegate;
|
||||
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* BusinessDelegate separates the presentation and business tiers.
|
||||
*/
|
||||
@Setter
|
||||
public class BusinessDelegate {
|
||||
|
||||
private BusinessLookup lookupService;
|
||||
private ServiceType serviceType;
|
||||
|
||||
public void setLookupService(BusinessLookup businessLookup) {
|
||||
this.lookupService = businessLookup;
|
||||
}
|
||||
|
||||
public void setServiceType(ServiceType serviceType) {
|
||||
this.serviceType = serviceType;
|
||||
}
|
||||
|
||||
public void doTask() {
|
||||
BusinessService businessService = lookupService.getBusinessService(serviceType);
|
||||
businessService.doProcessing();
|
||||
public void playbackMovie(String movie) {
|
||||
VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie);
|
||||
videoStreamingService.doProcessing();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
package com.iluwatar.business.delegate;
|
||||
|
||||
import java.util.Locale;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
@ -31,21 +32,21 @@ import lombok.Setter;
|
||||
@Setter
|
||||
public class BusinessLookup {
|
||||
|
||||
private EjbService ejbService;
|
||||
private NetflixService netflixService;
|
||||
|
||||
private JmsService jmsService;
|
||||
private YouTubeService youTubeService;
|
||||
|
||||
/**
|
||||
* Gets service instance based on service type.
|
||||
* Gets service instance based on given movie search string.
|
||||
*
|
||||
* @param serviceType Type of service instance to be returned.
|
||||
* @param movie Search string for the movie.
|
||||
* @return Service instance.
|
||||
*/
|
||||
public BusinessService getBusinessService(ServiceType serviceType) {
|
||||
if (serviceType.equals(ServiceType.EJB)) {
|
||||
return ejbService;
|
||||
public VideoStreamingService getBusinessService(String movie) {
|
||||
if (movie.toLowerCase(Locale.ROOT).contains("die hard")) {
|
||||
return netflixService;
|
||||
} else {
|
||||
return jmsService;
|
||||
return youTubeService;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,17 +24,17 @@
|
||||
package com.iluwatar.business.delegate;
|
||||
|
||||
/**
|
||||
* Client utilizes BusinessDelegate to call the business tier.
|
||||
* MobileClient utilizes BusinessDelegate to call the business tier.
|
||||
*/
|
||||
public class Client {
|
||||
public class MobileClient {
|
||||
|
||||
private final BusinessDelegate businessDelegate;
|
||||
|
||||
public Client(BusinessDelegate businessDelegate) {
|
||||
public MobileClient(BusinessDelegate businessDelegate) {
|
||||
this.businessDelegate = businessDelegate;
|
||||
}
|
||||
|
||||
public void doTask() {
|
||||
businessDelegate.doTask();
|
||||
public void playbackMovie(String movie) {
|
||||
businessDelegate.playbackMovie(movie);
|
||||
}
|
||||
}
|
@ -26,13 +26,13 @@ package com.iluwatar.business.delegate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Service EJB implementation.
|
||||
* NetflixService implementation.
|
||||
*/
|
||||
@Slf4j
|
||||
public class EjbService implements BusinessService {
|
||||
public class NetflixService implements VideoStreamingService {
|
||||
|
||||
@Override
|
||||
public void doProcessing() {
|
||||
LOGGER.info("EjbService is now processing");
|
||||
LOGGER.info("NetflixService is now processing");
|
||||
}
|
||||
}
|
@ -24,9 +24,9 @@
|
||||
package com.iluwatar.business.delegate;
|
||||
|
||||
/**
|
||||
* Interface for service implementations.
|
||||
* Interface for video streaming service implementations.
|
||||
*/
|
||||
public interface BusinessService {
|
||||
public interface VideoStreamingService {
|
||||
|
||||
void doProcessing();
|
||||
}
|
@ -26,13 +26,13 @@ package com.iluwatar.business.delegate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Service JMS implementation.
|
||||
* YouTubeService implementation.
|
||||
*/
|
||||
@Slf4j
|
||||
public class JmsService implements BusinessService {
|
||||
public class YouTubeService implements VideoStreamingService {
|
||||
|
||||
@Override
|
||||
public void doProcessing() {
|
||||
LOGGER.info("JmsService is now processing");
|
||||
LOGGER.info("YouTubeService is now processing");
|
||||
}
|
||||
}
|
@ -26,25 +26,20 @@ package com.iluwatar.business.delegate;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* The Business Delegate pattern adds an abstraction layer between the presentation and business
|
||||
* tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate
|
||||
* encapsulates knowledge about how to locate, connect to, and interact with the business objects
|
||||
* that make up the application.
|
||||
*
|
||||
* <p>Some of the services the Business Delegate uses are instantiated directly, and some can be
|
||||
* retrieved through service lookups. The Business Delegate itself may contain business logic too
|
||||
* potentially tying together multiple service calls, exception handling, retrying etc.
|
||||
* Tests for the {@link BusinessDelegate}
|
||||
*/
|
||||
class BusinessDelegateTest {
|
||||
|
||||
private EjbService ejbService;
|
||||
private NetflixService netflixService;
|
||||
|
||||
private JmsService jmsService;
|
||||
private YouTubeService youTubeService;
|
||||
|
||||
private BusinessDelegate businessDelegate;
|
||||
|
||||
@ -54,19 +49,19 @@ class BusinessDelegateTest {
|
||||
*/
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ejbService = spy(new EjbService());
|
||||
jmsService = spy(new JmsService());
|
||||
netflixService = spy(new NetflixService());
|
||||
youTubeService = spy(new YouTubeService());
|
||||
|
||||
BusinessLookup businessLookup = spy(new BusinessLookup());
|
||||
businessLookup.setEjbService(ejbService);
|
||||
businessLookup.setJmsService(jmsService);
|
||||
businessLookup.setNetflixService(netflixService);
|
||||
businessLookup.setYouTubeService(youTubeService);
|
||||
|
||||
businessDelegate = spy(new BusinessDelegate());
|
||||
businessDelegate.setLookupService(businessLookup);
|
||||
}
|
||||
|
||||
/**
|
||||
* In this example the client ({@link Client}) utilizes a business delegate (
|
||||
* In this example the client ({@link MobileClient}) utilizes a business delegate (
|
||||
* {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
|
||||
* service and makes the service call.
|
||||
*/
|
||||
@ -74,26 +69,20 @@ class BusinessDelegateTest {
|
||||
void testBusinessDelegate() {
|
||||
|
||||
// setup a client object
|
||||
var client = new Client(businessDelegate);
|
||||
|
||||
// set the service type
|
||||
businessDelegate.setServiceType(ServiceType.EJB);
|
||||
var client = new MobileClient(businessDelegate);
|
||||
|
||||
// action
|
||||
client.doTask();
|
||||
client.playbackMovie("Die hard");
|
||||
|
||||
// verifying that the businessDelegate was used by client during doTask() method.
|
||||
verify(businessDelegate).doTask();
|
||||
verify(ejbService).doProcessing();
|
||||
|
||||
// set the service type
|
||||
businessDelegate.setServiceType(ServiceType.JMS);
|
||||
// verifying that the businessDelegate was used by client during playbackMovie() method.
|
||||
verify(businessDelegate).playbackMovie(anyString());
|
||||
verify(netflixService).doProcessing();
|
||||
|
||||
// action
|
||||
client.doTask();
|
||||
client.playbackMovie("Maradona");
|
||||
|
||||
// verifying that the businessDelegate was used by client during doTask() method.
|
||||
verify(businessDelegate, times(2)).doTask();
|
||||
verify(jmsService).doProcessing();
|
||||
verify(businessDelegate, times(2)).playbackMovie(anyString());
|
||||
verify(youTubeService).doProcessing();
|
||||
}
|
||||
}
|
||||
|
@ -9,18 +9,234 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Allows to encode behaviour as instructions for virtual machine.
|
||||
|
||||
Allows encoding behavior as instructions for a virtual machine.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> A team is working on a new game where wizards battle against each other. The wizard behavior
|
||||
> needs to be carefully adjusted and iterated hundreds of times through playtesting. It's not
|
||||
> optimal to ask the programmer to make changes each time the game designer wants to vary the
|
||||
> behavior, so the wizard behavior is implemented as a data-driven virtual machine.
|
||||
|
||||
In plain words
|
||||
|
||||
> Bytecode pattern enables behavior driven by data instead of code.
|
||||
|
||||
[Gameprogrammingpatterns.com](https://gameprogrammingpatterns.com/bytecode.html) documentation
|
||||
states:
|
||||
|
||||
> An instruction set defines the low-level operations that can be performed. A series of
|
||||
> instructions is encoded as a sequence of bytes. A virtual machine executes these instructions one
|
||||
> at a time, using a stack for intermediate values. By combining instructions, complex high-level
|
||||
> behavior can be defined.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
One of the most important game objects is the `Wizard` class.
|
||||
|
||||
```java
|
||||
@AllArgsConstructor
|
||||
@Setter
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class Wizard {
|
||||
|
||||
private int health;
|
||||
private int agility;
|
||||
private int wisdom;
|
||||
private int numberOfPlayedSounds;
|
||||
private int numberOfSpawnedParticles;
|
||||
|
||||
public void playSound() {
|
||||
LOGGER.info("Playing sound");
|
||||
numberOfPlayedSounds++;
|
||||
}
|
||||
|
||||
public void spawnParticles() {
|
||||
LOGGER.info("Spawning particles");
|
||||
numberOfSpawnedParticles++;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next, we show the available instructions for our virtual machine. Each of the instructions has its
|
||||
own semantics on how it operates with the stack data. For example, the ADD instruction takes the top
|
||||
two items from the stack, adds them together and pushes the result to the stack.
|
||||
|
||||
```java
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum Instruction {
|
||||
|
||||
LITERAL(1), // e.g. "LITERAL 0", push 0 to stack
|
||||
SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health
|
||||
SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom
|
||||
SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility
|
||||
PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound
|
||||
SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles
|
||||
GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health
|
||||
GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility
|
||||
GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom
|
||||
ADD(10), // e.g. "ADD", pop 2 values, push their sum
|
||||
DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
At the heart of our example is the `VirtualMachine` class. It takes instructions as input and
|
||||
executes them to provide the game object behavior.
|
||||
|
||||
```java
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class VirtualMachine {
|
||||
|
||||
private final Stack<Integer> stack = new Stack<>();
|
||||
|
||||
private final Wizard[] wizards = new Wizard[2];
|
||||
|
||||
public VirtualMachine() {
|
||||
wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
|
||||
0, 0);
|
||||
wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
|
||||
0, 0);
|
||||
}
|
||||
|
||||
public VirtualMachine(Wizard wizard1, Wizard wizard2) {
|
||||
wizards[0] = wizard1;
|
||||
wizards[1] = wizard2;
|
||||
}
|
||||
|
||||
public void execute(int[] bytecode) {
|
||||
for (var i = 0; i < bytecode.length; i++) {
|
||||
Instruction instruction = Instruction.getInstruction(bytecode[i]);
|
||||
switch (instruction) {
|
||||
case LITERAL:
|
||||
// Read the next byte from the bytecode.
|
||||
int value = bytecode[++i];
|
||||
// Push the next value to stack
|
||||
stack.push(value);
|
||||
break;
|
||||
case SET_AGILITY:
|
||||
var amount = stack.pop();
|
||||
var wizard = stack.pop();
|
||||
setAgility(wizard, amount);
|
||||
break;
|
||||
case SET_WISDOM:
|
||||
amount = stack.pop();
|
||||
wizard = stack.pop();
|
||||
setWisdom(wizard, amount);
|
||||
break;
|
||||
case SET_HEALTH:
|
||||
amount = stack.pop();
|
||||
wizard = stack.pop();
|
||||
setHealth(wizard, amount);
|
||||
break;
|
||||
case GET_HEALTH:
|
||||
wizard = stack.pop();
|
||||
stack.push(getHealth(wizard));
|
||||
break;
|
||||
case GET_AGILITY:
|
||||
wizard = stack.pop();
|
||||
stack.push(getAgility(wizard));
|
||||
break;
|
||||
case GET_WISDOM:
|
||||
wizard = stack.pop();
|
||||
stack.push(getWisdom(wizard));
|
||||
break;
|
||||
case ADD:
|
||||
var a = stack.pop();
|
||||
var b = stack.pop();
|
||||
stack.push(a + b);
|
||||
break;
|
||||
case DIVIDE:
|
||||
a = stack.pop();
|
||||
b = stack.pop();
|
||||
stack.push(b / a);
|
||||
break;
|
||||
case PLAY_SOUND:
|
||||
wizard = stack.pop();
|
||||
getWizards()[wizard].playSound();
|
||||
break;
|
||||
case SPAWN_PARTICLES:
|
||||
wizard = stack.pop();
|
||||
getWizards()[wizard].spawnParticles();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid instruction value");
|
||||
}
|
||||
LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack());
|
||||
}
|
||||
}
|
||||
|
||||
public void setHealth(int wizard, int amount) {
|
||||
wizards[wizard].setHealth(amount);
|
||||
}
|
||||
// other setters ->
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Now we can show the full example utilizing the virtual machine.
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
|
||||
var vm = new VirtualMachine(
|
||||
new Wizard(45, 7, 11, 0, 0),
|
||||
new Wizard(36, 18, 8, 0, 0));
|
||||
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
|
||||
}
|
||||
```
|
||||
|
||||
Here is the console output.
|
||||
|
||||
```
|
||||
16:20:10.193 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0]
|
||||
16:20:10.196 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 0]
|
||||
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_HEALTH, Stack contains [0, 45]
|
||||
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 0]
|
||||
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_AGILITY, Stack contains [0, 45, 7]
|
||||
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 7, 0]
|
||||
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_WISDOM, Stack contains [0, 45, 7, 11]
|
||||
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 45, 18]
|
||||
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 18, 2]
|
||||
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed DIVIDE, Stack contains [0, 45, 9]
|
||||
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 54]
|
||||
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed SET_HEALTH, Stack contains []
|
||||
```
|
||||
|
||||
## Class diagram
|
||||
|
||||

|
||||
|
||||
## Applicability
|
||||
|
||||
Use the Bytecode pattern when you have a lot of behavior you need to define and your
|
||||
game’s implementation language isn’t a good fit because:
|
||||
|
||||
* it’s too low-level, making it tedious or error-prone to program in.
|
||||
* iterating on it takes too long due to slow compile times or other tooling issues.
|
||||
* it has too much trust. If you want to ensure the behavior being defined can’t break the game, you need to sandbox it from the rest of the codebase.
|
||||
* It’s too low-level, making it tedious or error-prone to program in.
|
||||
* Iterating on it takes too long due to slow compile times or other tooling issues.
|
||||
* It has too much trust. If you want to ensure the behavior being defined can’t break the game, you need to sandbox it from the rest of the codebase.
|
||||
|
||||
## Related patterns
|
||||
|
||||
* [Interpreter](https://java-design-patterns.com/patterns/interpreter/)
|
||||
|
||||
## Credits
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 19 KiB |
@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<class-diagram version="1.2.3" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
|
||||
associations="true" dependencies="false" nesting-relationships="true" router="FAN">
|
||||
<class id="1" language="java" name="com.iluwatar.bytecode.VirtualMachine" project="bytecode"
|
||||
file="/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="455" y="173"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="2" language="java" name="com.iluwatar.bytecode.App" project="bytecode"
|
||||
file="/bytecode/src/main/java/com/iluwatar/bytecode/App.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="148" y="110"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="3" language="java" name="com.iluwatar.bytecode.Wizard" project="bytecode"
|
||||
file="/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="148" y="416"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<association id="4">
|
||||
<end type="SOURCE" refId="1" navigable="false" variant="ASSOCIATION">
|
||||
<attribute id="5" name="wizards">
|
||||
<position height="18" width="48" x="296" y="291"/>
|
||||
</attribute>
|
||||
<multiplicity id="6" minimum="0" maximum="2147483647">
|
||||
<position height="0" width="0" x="-327" y="-27"/>
|
||||
</multiplicity>
|
||||
</end>
|
||||
<end type="TARGET" refId="3" navigable="true" variant="ASSOCIATION"/>
|
||||
<display labels="true" multiplicity="true"/>
|
||||
</association>
|
||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</classifier-display>
|
||||
<association-display labels="true" multiplicity="true"/>
|
||||
</class-diagram>
|
Binary file not shown.
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 84 KiB |
@ -3,7 +3,6 @@ package com.iluwatar.bytecode {
|
||||
class App {
|
||||
- LOGGER : Logger {static}
|
||||
+ App()
|
||||
- interpretInstruction(instruction : String, vm : VirtualMachine) {static}
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
enum Instruction {
|
||||
@ -18,22 +17,25 @@ package com.iluwatar.bytecode {
|
||||
+ SET_HEALTH {static}
|
||||
+ SET_WISDOM {static}
|
||||
+ SPAWN_PARTICLES {static}
|
||||
- value : int
|
||||
- intValue : int
|
||||
+ getInstruction(value : int) : Instruction {static}
|
||||
+ getIntValue() : int
|
||||
+ valueOf(name : String) : Instruction {static}
|
||||
+ values() : Instruction[] {static}
|
||||
}
|
||||
class VirtualMachine {
|
||||
- LOGGER : Logger {static}
|
||||
- stack : Stack<Integer>
|
||||
- wizards : Wizard[]
|
||||
+ VirtualMachine()
|
||||
+ VirtualMachine(wizard1 : Wizard, wizard2 : Wizard)
|
||||
+ execute(bytecode : int[])
|
||||
+ getAgility(wizard : int) : int
|
||||
+ getHealth(wizard : int) : int
|
||||
+ getStack() : Stack<Integer>
|
||||
+ getWisdom(wizard : int) : int
|
||||
+ getWizards() : Wizard[]
|
||||
- randomInt(min : int, max : int) : int
|
||||
+ setAgility(wizard : int, amount : int)
|
||||
+ setHealth(wizard : int, amount : int)
|
||||
+ setWisdom(wizard : int, amount : int)
|
||||
@ -45,7 +47,7 @@ package com.iluwatar.bytecode {
|
||||
- numberOfPlayedSounds : int
|
||||
- numberOfSpawnedParticles : int
|
||||
- wisdom : int
|
||||
+ Wizard()
|
||||
+ Wizard(health : int, agility : int, wisdom : int, numberOfPlayedSounds : int, numberOfSpawnedParticles : int)
|
||||
+ getAgility() : int
|
||||
+ getHealth() : int
|
||||
+ getNumberOfPlayedSounds() : int
|
||||
@ -54,6 +56,8 @@ package com.iluwatar.bytecode {
|
||||
+ playSound()
|
||||
+ setAgility(agility : int)
|
||||
+ setHealth(health : int)
|
||||
+ setNumberOfPlayedSounds(numberOfPlayedSounds : int)
|
||||
+ setNumberOfSpawnedParticles(numberOfSpawnedParticles : int)
|
||||
+ setWisdom(wisdom : int)
|
||||
+ spawnParticles()
|
||||
}
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -49,33 +49,21 @@ public class App {
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
var wizard = new Wizard();
|
||||
wizard.setHealth(45);
|
||||
wizard.setAgility(7);
|
||||
wizard.setWisdom(11);
|
||||
var vm = new VirtualMachine(
|
||||
new Wizard(45, 7, 11, 0, 0),
|
||||
new Wizard(36, 18, 8, 0, 0));
|
||||
|
||||
var vm = new VirtualMachine();
|
||||
vm.getWizards()[0] = wizard;
|
||||
|
||||
String literal = "LITERAL 0";
|
||||
|
||||
interpretInstruction(literal, vm);
|
||||
interpretInstruction(literal, vm);
|
||||
interpretInstruction("GET_HEALTH", vm);
|
||||
interpretInstruction(literal, vm);
|
||||
interpretInstruction("GET_AGILITY", vm);
|
||||
interpretInstruction(literal, vm);
|
||||
interpretInstruction("GET_WISDOM ", vm);
|
||||
interpretInstruction("ADD", vm);
|
||||
interpretInstruction("LITERAL 2", vm);
|
||||
interpretInstruction("DIVIDE", vm);
|
||||
interpretInstruction("ADD", vm);
|
||||
interpretInstruction("SET_HEALTH", vm);
|
||||
}
|
||||
|
||||
private static void interpretInstruction(String instruction, VirtualMachine vm) {
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode(instruction));
|
||||
var stack = vm.getStack();
|
||||
LOGGER.info(instruction + String.format("%" + (12 - instruction.length()) + "s", "") + stack);
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
||||
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
|
||||
}
|
||||
}
|
||||
|
@ -33,17 +33,17 @@ import lombok.Getter;
|
||||
@Getter
|
||||
public enum Instruction {
|
||||
|
||||
LITERAL(1),
|
||||
SET_HEALTH(2),
|
||||
SET_WISDOM(3),
|
||||
SET_AGILITY(4),
|
||||
PLAY_SOUND(5),
|
||||
SPAWN_PARTICLES(6),
|
||||
GET_HEALTH(7),
|
||||
GET_AGILITY(8),
|
||||
GET_WISDOM(9),
|
||||
ADD(10),
|
||||
DIVIDE(11);
|
||||
LITERAL(1), // e.g. "LITERAL 0", push 0 to stack
|
||||
SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health
|
||||
SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom
|
||||
SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility
|
||||
PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound
|
||||
SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles
|
||||
GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health
|
||||
GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility
|
||||
GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom
|
||||
ADD(10), // e.g. "ADD", pop 2 values, push their sum
|
||||
DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division
|
||||
|
||||
private final int intValue;
|
||||
|
||||
|
@ -24,12 +24,15 @@
|
||||
package com.iluwatar.bytecode;
|
||||
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Implementation of virtual machine.
|
||||
*/
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class VirtualMachine {
|
||||
|
||||
private final Stack<Integer> stack = new Stack<>();
|
||||
@ -37,12 +40,21 @@ public class VirtualMachine {
|
||||
private final Wizard[] wizards = new Wizard[2];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* No-args constructor.
|
||||
*/
|
||||
public VirtualMachine() {
|
||||
for (var i = 0; i < wizards.length; i++) {
|
||||
wizards[i] = new Wizard();
|
||||
}
|
||||
wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
|
||||
0, 0);
|
||||
wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
|
||||
0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor taking the wizards as arguments.
|
||||
*/
|
||||
public VirtualMachine(Wizard wizard1, Wizard wizard2) {
|
||||
wizards[0] = wizard1;
|
||||
wizards[1] = wizard2;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,6 +69,7 @@ public class VirtualMachine {
|
||||
case LITERAL:
|
||||
// Read the next byte from the bytecode.
|
||||
int value = bytecode[++i];
|
||||
// Push the next value to stack
|
||||
stack.push(value);
|
||||
break;
|
||||
case SET_AGILITY:
|
||||
@ -107,6 +120,7 @@ public class VirtualMachine {
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid instruction value");
|
||||
}
|
||||
LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack());
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,4 +147,8 @@ public class VirtualMachine {
|
||||
public int getAgility(int wizard) {
|
||||
return wizards[wizard].getAgility();
|
||||
}
|
||||
|
||||
private int randomInt(int min, int max) {
|
||||
return ThreadLocalRandom.current().nextInt(min, max + 1);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
package com.iluwatar.bytecode;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -31,16 +32,15 @@ import lombok.extern.slf4j.Slf4j;
|
||||
* This class represent game objects which properties can be changed by instructions interpreted by
|
||||
* virtual machine.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Setter
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class Wizard {
|
||||
|
||||
private int health;
|
||||
|
||||
private int agility;
|
||||
private int wisdom;
|
||||
|
||||
private int numberOfPlayedSounds;
|
||||
private int numberOfSpawnedParticles;
|
||||
|
||||
@ -53,5 +53,4 @@ public class Wizard {
|
||||
LOGGER.info("Spawning particles");
|
||||
numberOfSpawnedParticles++;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -73,6 +73,4 @@ public class InstructionConverterUtil {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -10,20 +10,326 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
To avoid expensive re-acquisition of resources by not releasing
|
||||
the resources immediately after their use. The resources retain their identity, are kept in some
|
||||
fast-access storage, and are re-used to avoid having to acquire them again.
|
||||
|
||||
The caching pattern avoids expensive re-acquisition of resources by not releasing them immediately
|
||||
after use. The resources retain their identity, are kept in some fast-access storage, and are
|
||||
re-used to avoid having to acquire them again.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> A team is working on a website that provides new homes for abandoned cats. People can post their
|
||||
> cats on the website after registering, but all the new posts require approval from one of the
|
||||
> site moderators. The user accounts of the site moderators contain a specific flag and the data
|
||||
> is stored in a MongoDB database. Checking for the moderator flag each time a post is viewed
|
||||
> becomes expensive and it's a good idea to utilize caching here.
|
||||
|
||||
In plain words
|
||||
|
||||
> Caching pattern keeps frequently needed data in fast-access storage to improve performance.
|
||||
|
||||
Wikipedia says:
|
||||
|
||||
> In computing, a cache is a hardware or software component that stores data so that future
|
||||
> requests for that data can be served faster; the data stored in a cache might be the result of
|
||||
> an earlier computation or a copy of data stored elsewhere. A cache hit occurs when the requested
|
||||
> data can be found in a cache, while a cache miss occurs when it cannot. Cache hits are served by
|
||||
> reading data from the cache, which is faster than recomputing a result or reading from a slower
|
||||
> data store; thus, the more requests that can be served from the cache, the faster the system
|
||||
> performs.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's first look at the data layer of our application. The interesting classes are `UserAccount`
|
||||
which is a simple Java object containing the user account details, and `DbManager` which handles
|
||||
reading and writing of these objects to/from MongoDB database.
|
||||
|
||||
```java
|
||||
@Setter
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public class UserAccount {
|
||||
private String userId;
|
||||
private String userName;
|
||||
private String additionalInfo;
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public final class DbManager {
|
||||
|
||||
private static MongoClient mongoClient;
|
||||
private static MongoDatabase db;
|
||||
|
||||
private DbManager() { /*...*/ }
|
||||
|
||||
public static void createVirtualDb() { /*...*/ }
|
||||
|
||||
public static void connect() throws ParseException { /*...*/ }
|
||||
|
||||
public static UserAccount readFromDb(String userId) { /*...*/ }
|
||||
|
||||
public static void writeToDb(UserAccount userAccount) { /*...*/ }
|
||||
|
||||
public static void updateDb(UserAccount userAccount) { /*...*/ }
|
||||
|
||||
public static void upsertDb(UserAccount userAccount) { /*...*/ }
|
||||
}
|
||||
```
|
||||
|
||||
In the example, we are demonstrating various different caching policies
|
||||
|
||||
* Write-through writes data to the cache and DB in a single transaction
|
||||
* Write-around writes data immediately into the DB instead of the cache
|
||||
* Write-behind writes data into the cache initially whilst the data is only written into the DB
|
||||
when the cache is full
|
||||
* Cache-aside pushes the responsibility of keeping the data synchronized in both data sources to
|
||||
the application itself
|
||||
* Read-through strategy is also included in the aforementioned strategies and it returns data from
|
||||
the cache to the caller if it exists, otherwise queries from DB and stores it into the cache for
|
||||
future use.
|
||||
|
||||
The cache implementation in `LruCache` is a hash table accompanied by a doubly
|
||||
linked-list. The linked-list helps in capturing and maintaining the LRU data in the cache. When
|
||||
data is queried (from the cache), added (to the cache), or updated, the data is moved to the front
|
||||
of the list to depict itself as the most-recently-used data. The LRU data is always at the end of
|
||||
the list.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class LruCache {
|
||||
|
||||
static class Node {
|
||||
String userId;
|
||||
UserAccount userAccount;
|
||||
Node previous;
|
||||
Node next;
|
||||
|
||||
public Node(String userId, UserAccount userAccount) {
|
||||
this.userId = userId;
|
||||
this.userAccount = userAccount;
|
||||
}
|
||||
}
|
||||
|
||||
/* ... omitted details ... */
|
||||
|
||||
public LruCache(int capacity) {
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
public UserAccount get(String userId) {
|
||||
if (cache.containsKey(userId)) {
|
||||
var node = cache.get(userId);
|
||||
remove(node);
|
||||
setHead(node);
|
||||
return node.userAccount;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void set(String userId, UserAccount userAccount) {
|
||||
if (cache.containsKey(userId)) {
|
||||
var old = cache.get(userId);
|
||||
old.userAccount = userAccount;
|
||||
remove(old);
|
||||
setHead(old);
|
||||
} else {
|
||||
var newNode = new Node(userId, userAccount);
|
||||
if (cache.size() >= capacity) {
|
||||
LOGGER.info("# Cache is FULL! Removing {} from cache...", end.userId);
|
||||
cache.remove(end.userId); // remove LRU data from cache.
|
||||
remove(end);
|
||||
setHead(newNode);
|
||||
} else {
|
||||
setHead(newNode);
|
||||
}
|
||||
cache.put(userId, newNode);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contains(String userId) {
|
||||
return cache.containsKey(userId);
|
||||
}
|
||||
|
||||
public void remove(Node node) { /* ... */ }
|
||||
public void setHead(Node node) { /* ... */ }
|
||||
public void invalidate(String userId) { /* ... */ }
|
||||
public boolean isFull() { /* ... */ }
|
||||
public UserAccount getLruData() { /* ... */ }
|
||||
public void clear() { /* ... */ }
|
||||
public List<UserAccount> getCacheDataInListForm() { /* ... */ }
|
||||
public void setCapacity(int newCapacity) { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
The next layer we are going to look at is `CacheStore` which implements the different caching
|
||||
strategies.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class CacheStore {
|
||||
|
||||
private static LruCache cache;
|
||||
|
||||
/* ... details omitted ... */
|
||||
|
||||
public static UserAccount readThrough(String userId) {
|
||||
if (cache.contains(userId)) {
|
||||
LOGGER.info("# Cache Hit!");
|
||||
return cache.get(userId);
|
||||
}
|
||||
LOGGER.info("# Cache Miss!");
|
||||
UserAccount userAccount = DbManager.readFromDb(userId);
|
||||
cache.set(userId, userAccount);
|
||||
return userAccount;
|
||||
}
|
||||
|
||||
public static void writeThrough(UserAccount userAccount) {
|
||||
if (cache.contains(userAccount.getUserId())) {
|
||||
DbManager.updateDb(userAccount);
|
||||
} else {
|
||||
DbManager.writeToDb(userAccount);
|
||||
}
|
||||
cache.set(userAccount.getUserId(), userAccount);
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
if (cache != null) {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void flushCache() {
|
||||
LOGGER.info("# flushCache...");
|
||||
Optional.ofNullable(cache)
|
||||
.map(LruCache::getCacheDataInListForm)
|
||||
.orElse(List.of())
|
||||
.forEach(DbManager::updateDb);
|
||||
}
|
||||
|
||||
/* ... omitted the implementation of other caching strategies ... */
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
`AppManager` helps to bridge the gap in communication between the main class and the application's
|
||||
back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
|
||||
also initialized here. Before the cache can be used, the size of the cache has to be set. Depending
|
||||
on the chosen caching policy, `AppManager` will call the appropriate function in the `CacheStore`
|
||||
class.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public final class AppManager {
|
||||
|
||||
private static CachingPolicy cachingPolicy;
|
||||
|
||||
private AppManager() {
|
||||
}
|
||||
|
||||
public static void initDb(boolean useMongoDb) { /* ... */ }
|
||||
|
||||
public static void initCachingPolicy(CachingPolicy policy) { /* ... */ }
|
||||
|
||||
public static void initCacheCapacity(int capacity) { /* ... */ }
|
||||
|
||||
public static UserAccount find(String userId) {
|
||||
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
|
||||
return CacheStore.readThrough(userId);
|
||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
||||
return CacheStore.readThroughWithWriteBackPolicy(userId);
|
||||
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
||||
return findAside(userId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void save(UserAccount userAccount) {
|
||||
if (cachingPolicy == CachingPolicy.THROUGH) {
|
||||
CacheStore.writeThrough(userAccount);
|
||||
} else if (cachingPolicy == CachingPolicy.AROUND) {
|
||||
CacheStore.writeAround(userAccount);
|
||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
||||
CacheStore.writeBehind(userAccount);
|
||||
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
||||
saveAside(userAccount);
|
||||
}
|
||||
}
|
||||
|
||||
public static String printCacheContent() {
|
||||
return CacheStore.print();
|
||||
}
|
||||
|
||||
/* ... details omitted ... */
|
||||
}
|
||||
```
|
||||
|
||||
Here is what we do in the main class of the application.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class App {
|
||||
|
||||
public static void main(String[] args) {
|
||||
AppManager.initDb(false);
|
||||
AppManager.initCacheCapacity(3);
|
||||
var app = new App();
|
||||
app.useReadAndWriteThroughStrategy();
|
||||
app.useReadThroughAndWriteAroundStrategy();
|
||||
app.useReadThroughAndWriteBehindStrategy();
|
||||
app.useCacheAsideStategy();
|
||||
}
|
||||
|
||||
public void useReadAndWriteThroughStrategy() {
|
||||
LOGGER.info("# CachingPolicy.THROUGH");
|
||||
AppManager.initCachingPolicy(CachingPolicy.THROUGH);
|
||||
var userAccount1 = new UserAccount("001", "John", "He is a boy.");
|
||||
AppManager.save(userAccount1);
|
||||
LOGGER.info(AppManager.printCacheContent());
|
||||
AppManager.find("001");
|
||||
AppManager.find("001");
|
||||
}
|
||||
|
||||
public void useReadThroughAndWriteAroundStrategy() { /* ... */ }
|
||||
|
||||
public void useReadThroughAndWriteBehindStrategy() { /* ... */ }
|
||||
|
||||
public void useCacheAsideStategy() { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
Finally, here is some of the console output from the program.
|
||||
|
||||
```
|
||||
12:32:53.845 [main] INFO com.iluwatar.caching.App - # CachingPolicy.THROUGH
|
||||
12:32:53.900 [main] INFO com.iluwatar.caching.App -
|
||||
--CACHE CONTENT--
|
||||
UserAccount(userId=001, userName=John, additionalInfo=He is a boy.)
|
||||
----
|
||||
```
|
||||
|
||||
## Class diagram
|
||||
|
||||

|
||||
|
||||
## Applicability
|
||||
|
||||
Use the Caching pattern(s) when
|
||||
|
||||
* Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead.
|
||||
* Repetitious acquisition, initialization, and release of the same resource cause unnecessary
|
||||
performance overhead.
|
||||
|
||||
## Related patterns
|
||||
|
||||
* [Proxy](https://java-design-patterns.com/patterns/proxy/)
|
||||
|
||||
## Credits
|
||||
|
||||
* [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained)
|
||||
* [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177)
|
||||
* [Cache-Aside pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside)
|
||||
* [Java EE 8 High Performance: Master techniques such as memory optimization, caching, concurrency, and multithreading to achieve maximum performance from your enterprise applications](https://www.amazon.com/gp/product/178847306X/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=178847306X&linkId=e948720055599f248cdac47da9125ff4)
|
||||
* [Java Performance: In-Depth Advice for Tuning and Programming Java 8, 11, and Beyond](https://www.amazon.com/gp/product/1492056111/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1492056111&linkId=7e553581559b9ec04221259e52004b08)
|
||||
* [Effective Java](https://www.amazon.com/gp/product/B078H61SCH/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=B078H61SCH&linkId=f06607a0b48c76541ef19c5b8b9e7882)
|
||||
* [Java Performance: The Definitive Guide: Getting the Most Out of Your Code](https://www.amazon.com/gp/product/1449358454/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1449358454&linkId=475c18363e350630cc0b39ab681b2687)
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>caching</artifactId>
|
||||
<dependencies>
|
||||
|
@ -25,6 +25,7 @@ package com.iluwatar.caching;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Optional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* AppManager helps to bridge the gap in communication between the main class and the application's
|
||||
@ -33,6 +34,7 @@ import java.util.Optional;
|
||||
* Depending on the chosen caching policy, AppManager will call the appropriate function in the
|
||||
* CacheStore class.
|
||||
*/
|
||||
@Slf4j
|
||||
public final class AppManager {
|
||||
|
||||
private static CachingPolicy cachingPolicy;
|
||||
@ -50,7 +52,7 @@ public final class AppManager {
|
||||
try {
|
||||
DbManager.connect();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
LOGGER.error("Error connecting to MongoDB", e);
|
||||
}
|
||||
} else {
|
||||
DbManager.createVirtualDb();
|
||||
|
@ -30,6 +30,7 @@ import com.mongodb.client.model.UpdateOptions;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bson.Document;
|
||||
|
||||
/**
|
||||
@ -41,6 +42,7 @@ import org.bson.Document;
|
||||
* underlying data storage (connect()) or a simple Java data structure to (temporarily) store the
|
||||
* data/objects during runtime (createVirtualDB()).</p>
|
||||
*/
|
||||
@Slf4j
|
||||
public final class DbManager {
|
||||
|
||||
private static MongoClient mongoClient;
|
||||
@ -83,7 +85,7 @@ public final class DbManager {
|
||||
try {
|
||||
connect();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
LOGGER.error("Error connecting to MongoDB", e);
|
||||
}
|
||||
}
|
||||
var iterable = db
|
||||
@ -110,7 +112,7 @@ public final class DbManager {
|
||||
try {
|
||||
connect();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
LOGGER.error("Error connecting to MongoDB", e);
|
||||
}
|
||||
}
|
||||
db.getCollection(CachingConstants.USER_ACCOUNT).insertOne(
|
||||
@ -132,7 +134,7 @@ public final class DbManager {
|
||||
try {
|
||||
connect();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
LOGGER.error("Error connecting to MongoDB", e);
|
||||
}
|
||||
}
|
||||
db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
|
||||
@ -153,7 +155,7 @@ public final class DbManager {
|
||||
try {
|
||||
connect();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
LOGGER.error("Error connecting to MongoDB", e);
|
||||
}
|
||||
}
|
||||
db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>callback</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>chain</artifactId>
|
||||
<dependencies>
|
||||
|
@ -27,7 +27,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>circuit-breaker</artifactId>
|
||||
<dependencies>
|
||||
|
@ -27,7 +27,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>collection-pipeline</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>combinator</artifactId>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>command</artifactId>
|
||||
<dependencies>
|
||||
|
@ -27,7 +27,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>commander</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>composite</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>converter</artifactId>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -30,7 +30,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>cqrs</artifactId>
|
||||
<dependencies>
|
||||
|
@ -30,7 +30,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>dao</artifactId>
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>data-bus</artifactId>
|
||||
<dependencies>
|
||||
|
@ -30,7 +30,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>data-locality</artifactId>
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>data-mapper</artifactId>
|
||||
<dependencies>
|
||||
|
@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>data-transfer-object</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>decorator</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>dependency-injection</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,10 +29,10 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>dirty-flag</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
<name>dirty-flag</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<properties>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>double-checked-locking</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>double-dispatch</artifactId>
|
||||
<dependencies>
|
||||
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@ -30,7 +30,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>eip-message-channel</artifactId>
|
||||
<dependencies>
|
||||
|
@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>eip-publish-subscribe</artifactId>
|
||||
<dependencies>
|
||||
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>event-aggregator</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>event-asynchronous</artifactId>
|
||||
<dependencies>
|
||||
|
@ -31,7 +31,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>event-driven-architecture</artifactId>
|
||||
|
@ -30,7 +30,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>event-queue</artifactId>
|
||||
<dependencies>
|
||||
|
@ -30,7 +30,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>event-sourcing</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>execute-around</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>facade</artifactId>
|
||||
<dependencies>
|
||||
|
@ -30,7 +30,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>factory-kit</artifactId>
|
||||
<dependencies>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>factory-method</artifactId>
|
||||
<dependencies>
|
||||
|
@ -105,7 +105,7 @@ Program output:
|
||||
|
||||
```java
|
||||
This is Ford.
|
||||
This Ferrari.
|
||||
This is Ferrari.
|
||||
```
|
||||
|
||||
## Class Diagram
|
||||
|
@ -29,7 +29,7 @@
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.24.0-SNAPSHOT</version>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>factory</artifactId>
|
||||
<dependencies>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user