Compare commits
7 Commits
fix-domain
...
all-contri
Author | SHA1 | Date | |
---|---|---|---|
1e7c51588c | |||
3e439be481 | |||
3126ad3106 | |||
e2ebb59fe7 | |||
d87e8cf10d | |||
c5a4068e84 | |||
d36efdbc7c |
@ -1568,6 +1568,24 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "karthikbhat13",
|
||||
"name": "karthikbhat13",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22431014?v=4",
|
||||
"profile": "https://github.com/karthikbhat13",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mortezaadi",
|
||||
"name": "Morteza Adigozalpour",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1329687?v=4",
|
||||
"profile": "https://github.com/mortezaadi",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 4,
|
||||
|
@ -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/>
|
||||
@ -333,6 +333,10 @@ This project is licensed under the terms of the MIT license.
|
||||
<td align="center"><a href="https://github.com/marlo2222"><img src="https://avatars.githubusercontent.com/u/40809563?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marlo Henrique</b></sub></a><br /><a href="#translation-marlo2222" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/AndriyPyzh"><img src="https://avatars.githubusercontent.com/u/57706635?v=4?s=100" width="100px;" alt=""/><br /><sub><b>AndriyPyzh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=AndriyPyzh" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/karthikbhat13"><img src="https://avatars.githubusercontent.com/u/22431014?v=4?s=100" width="100px;" alt=""/><br /><sub><b>karthikbhat13</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=karthikbhat13" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/mortezaadi"><img src="https://avatars.githubusercontent.com/u/1329687?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Morteza Adigozalpour</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mortezaadi" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
|
120
fanout-fanin/README.md
Normal file
120
fanout-fanin/README.md
Normal file
@ -0,0 +1,120 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Fan-Out/Fan-In
|
||||
folder: fanout-fanin
|
||||
permalink: /patterns/fanout-fanin/
|
||||
categories: Integration
|
||||
language: en
|
||||
tags:
|
||||
- Microservices
|
||||
---
|
||||
|
||||
## Intent
|
||||
The pattern is used when a source system needs to run one or more long-running processes that will fetch some data.
|
||||
The source will not block itself waiting for the reply. <br> The pattern will run the same function in multiple
|
||||
services or machines to fetch the data. This is equivalent to invoking the function multiple times on different chunks of data.
|
||||
|
||||
## Explanation
|
||||
The FanOut/FanIn service will take in a list of requests and a consumer. Each request might complete at a different time.
|
||||
FanOut/FanIn service will accept the input params and returns the initial system an ID to acknowledge that the pattern
|
||||
service has received the requests. Now the caller will not wait or expect the result in the same connection.
|
||||
|
||||
Meanwhile, the pattern service will invoke the requests that have come. The requests might complete at different time.
|
||||
These requests will be processed in different instances of the same function in different machines or services. As the
|
||||
requests get completed, a callback service everytime is called that transforms the result into a common single object format
|
||||
that gets pushed to a consumer. The caller will be at the other end of the consumer receiving the result.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
The implementation provided has a list of numbers and end goal is to square the numbers and add them to a single result.
|
||||
`FanOutFanIn` class receives the list of numbers in the form of list of `SquareNumberRequest` and a `Consumer` instance
|
||||
that collects the results as the requests get over. `SquareNumberRequest` will square the number with a random delay
|
||||
to give the impression of a long-running process that can complete at any time. `Consumer` instance will add the results from
|
||||
different `SquareNumberRequest` that will come random time instances.
|
||||
|
||||
Let's look at `FanOutFanIn` class that fans out the requests in async processes.
|
||||
|
||||
```java
|
||||
public class FanOutFanIn {
|
||||
public static Long fanOutFanIn(
|
||||
final List<SquareNumberRequest> requests, final Consumer consumer) {
|
||||
|
||||
ExecutorService service = Executors.newFixedThreadPool(requests.size());
|
||||
|
||||
// fanning out
|
||||
List<CompletableFuture<Void>> futures =
|
||||
requests.stream()
|
||||
.map(
|
||||
request ->
|
||||
CompletableFuture.runAsync(() -> request.delayedSquaring(consumer), service))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
|
||||
return consumer.getSumOfSquaredNumbers().get();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Consumer` is used a callback class that will be called when a request is completed. This will aggregate
|
||||
the result from all requests.
|
||||
|
||||
```java
|
||||
public class Consumer {
|
||||
|
||||
private final AtomicLong sumOfSquaredNumbers;
|
||||
|
||||
Consumer(Long init) {
|
||||
sumOfSquaredNumbers = new AtomicLong(init);
|
||||
}
|
||||
|
||||
public Long add(final Long num) {
|
||||
return sumOfSquaredNumbers.addAndGet(num);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Request is represented as a `SquareNumberRequest` that squares the number with random delay and calls the
|
||||
`Consumer` once it is squared.
|
||||
|
||||
```java
|
||||
public class SquareNumberRequest {
|
||||
|
||||
private final Long number;
|
||||
public void delayedSquaring(final Consumer consumer) {
|
||||
|
||||
var minTimeOut = 5000L;
|
||||
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
var randomTimeOut = secureRandom.nextInt(2000);
|
||||
|
||||
try {
|
||||
// this will make the thread sleep from 5-7s.
|
||||
Thread.sleep(minTimeOut + randomTimeOut);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.error("Exception while sleep ", e);
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
consumer.add(number * number);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
## Applicability
|
||||
|
||||
Use this pattern when you can divide the workload into multiple chunks that can be dealt with separately.
|
||||
|
||||
## Related patterns
|
||||
|
||||
* [Aggregator Microservices](https://java-design-patterns.com/patterns/aggregator-microservices/)
|
||||
* [API Gateway](https://java-design-patterns.com/patterns/api-gateway/)
|
||||
|
||||
## Credits
|
||||
|
||||
* [Understanding Azure Durable Functions - Part 8: The Fan Out/Fan In Pattern](http://dontcodetired.com/blog/post/Understanding-Azure-Durable-Functions-Part-8-The-Fan-OutFan-In-Pattern)
|
||||
* [Fan-out/fan-in scenario in Durable Functions - Cloud backup example](https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-cloud-backup)
|
||||
* [Understanding the Fan-Out/Fan-In API Integration Pattern](https://dzone.com/articles/understanding-the-fan-out-fan-in-api-integration-p)
|
BIN
fanout-fanin/etc/fanout-fanin.png
Normal file
BIN
fanout-fanin/etc/fanout-fanin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
39
fanout-fanin/etc/fanout-fanin.urm.puml
Normal file
39
fanout-fanin/etc/fanout-fanin.urm.puml
Normal file
@ -0,0 +1,39 @@
|
||||
@startuml
|
||||
package com.iluwatar.fanout.fanin {
|
||||
class App {
|
||||
- LOGGER : Logger {static}
|
||||
+ App()
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
class Consumer {
|
||||
- sumOfSquaredNumbers : AtomicLong
|
||||
~ Consumer(init : Long)
|
||||
+ add(num : Long) : Long
|
||||
+ getSumOfSquaredNumbers() : AtomicLong
|
||||
}
|
||||
class FanOutFanIn {
|
||||
+ FanOutFanIn()
|
||||
+ fanOutFanIn(requests : List<SquareNumberRequest>, consumer : Consumer) : Long {static}
|
||||
}
|
||||
class SquareNumberRequest {
|
||||
- LOGGER : Logger {static}
|
||||
- number : Long
|
||||
+ SquareNumberRequest(number : Long)
|
||||
+ delayedSquaring(consumer : Consumer)
|
||||
}
|
||||
|
||||
object SquareNumberRequest1
|
||||
object SquareNumberRequest2
|
||||
object SquareNumberRequest3
|
||||
diamond dia
|
||||
}
|
||||
|
||||
App --> FanOutFanIn
|
||||
FanOutFanIn --> "fan out - running in parallel" SquareNumberRequest1
|
||||
FanOutFanIn --> "fan out" SquareNumberRequest2
|
||||
FanOutFanIn --> "fan out" SquareNumberRequest3
|
||||
SquareNumberRequest1 --> "fan in - aggregate using callback" dia
|
||||
SquareNumberRequest2 --> "fan in" dia
|
||||
SquareNumberRequest3 --> "fan in" dia
|
||||
dia --> Consumer
|
||||
@enduml
|
69
fanout-fanin/pom.xml
Normal file
69
fanout-fanin/pom.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
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.
|
||||
|
||||
Module Model-view-viewmodel is using ZK framework
|
||||
ZK framework is licensed under LGPL and the license can be found at lgpl-3.0.txt
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.25.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>fanout-fanin</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.iluwatar.fanout.fanin.App</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.fanout.fanin;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* FanOut/FanIn pattern is a concurrency pattern that refers to executing multiple instances of the
|
||||
* activity function concurrently. The "fan out" part is essentially splitting the data into
|
||||
* multiple chunks and then calling the activity function multiple times, passing the chunks.
|
||||
*
|
||||
* <p>When each chunk has been processed, the "fan in" takes place that aggregates results from each
|
||||
* instance of function and forms a single final result.
|
||||
*
|
||||
* <p>This pattern is only really useful if you can “chunk” the workload in a meaningful way for
|
||||
* splitting up to be processed in parallel.
|
||||
*/
|
||||
@Slf4j
|
||||
public class App {
|
||||
|
||||
/**
|
||||
* Entry point.
|
||||
*
|
||||
* <p>Implementation provided has a list of numbers that has to be squared and added. The list can
|
||||
* be chunked in any way and the "activity function" {@link
|
||||
* SquareNumberRequest#delayedSquaring(Consumer)} i.e. squaring the number ca be done
|
||||
* concurrently. The "fan in" part is handled by the {@link Consumer} that takes in the result
|
||||
* from each instance of activity and aggregates it whenever that particular activity function
|
||||
* gets over.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
final List<Long> numbers = Arrays.asList(1L, 3L, 4L, 7L, 8L);
|
||||
|
||||
LOGGER.info("Numbers to be squared and get sum --> {}", numbers);
|
||||
|
||||
final List<SquareNumberRequest> requests =
|
||||
numbers.stream().map(SquareNumberRequest::new).collect(Collectors.toList());
|
||||
|
||||
var consumer = new Consumer(0L);
|
||||
|
||||
// Pass the request and the consumer to fanOutFanIn or sometimes referred as Orchestrator
|
||||
// function
|
||||
final Long sumOfSquaredNumbers = FanOutFanIn.fanOutFanIn(requests, consumer);
|
||||
|
||||
LOGGER.info("Sum of all squared numbers --> {}", sumOfSquaredNumbers);
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.fanout.fanin;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Consumer or callback class that will be called everytime a request is complete This will
|
||||
* aggregate individual result to form a final result.
|
||||
*/
|
||||
@Getter
|
||||
public class Consumer {
|
||||
|
||||
private final AtomicLong sumOfSquaredNumbers;
|
||||
|
||||
Consumer(Long init) {
|
||||
sumOfSquaredNumbers = new AtomicLong(init);
|
||||
}
|
||||
|
||||
public Long add(final Long num) {
|
||||
return sumOfSquaredNumbers.addAndGet(num);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.fanout.fanin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* FanOutFanIn class processes long running requests, when any of the processes gets over, result is
|
||||
* passed over to the consumer or the callback function. Consumer will aggregate the results as they
|
||||
* keep on completing.
|
||||
*/
|
||||
public class FanOutFanIn {
|
||||
|
||||
/**
|
||||
* the main fanOutFanIn function or orchestrator function.
|
||||
* @param requests List of numbers that need to be squared and summed up
|
||||
* @param consumer Takes in the squared number from {@link SquareNumberRequest} and sums it up
|
||||
* @return Aggregated sum of all squared numbers.
|
||||
*/
|
||||
public static Long fanOutFanIn(
|
||||
final List<SquareNumberRequest> requests, final Consumer consumer) {
|
||||
|
||||
ExecutorService service = Executors.newFixedThreadPool(requests.size());
|
||||
|
||||
// fanning out
|
||||
List<CompletableFuture<Void>> futures =
|
||||
requests.stream()
|
||||
.map(
|
||||
request ->
|
||||
CompletableFuture.runAsync(() -> request.delayedSquaring(consumer), service))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
|
||||
return consumer.getSumOfSquaredNumbers().get();
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.fanout.fanin;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Squares the number with a little timeout to give impression of long running process that return
|
||||
* at different times.
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class SquareNumberRequest {
|
||||
|
||||
private final Long number;
|
||||
|
||||
/**
|
||||
* Squares the number with a little timeout to give impression of long running process that return
|
||||
* at different times.
|
||||
* @param consumer callback class that takes the result after the delay.
|
||||
* */
|
||||
public void delayedSquaring(final Consumer consumer) {
|
||||
|
||||
var minTimeOut = 5000L;
|
||||
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
var randomTimeOut = secureRandom.nextInt(2000);
|
||||
|
||||
try {
|
||||
// this will make the thread sleep from 5-7s.
|
||||
Thread.sleep(minTimeOut + randomTimeOut);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.error("Exception while sleep ", e);
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
consumer.add(number * number);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.fanout.fanin;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
class AppTest {
|
||||
|
||||
@Test
|
||||
void shouldLaunchApp() {
|
||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.fanout.fanin;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class FanOutFanInTest {
|
||||
|
||||
@Test
|
||||
void fanOutFanInTest() {
|
||||
final List<Long> numbers = Arrays.asList(1L, 3L, 4L, 7L, 8L);
|
||||
|
||||
final List<SquareNumberRequest> requests =
|
||||
numbers.stream().map(SquareNumberRequest::new).collect(Collectors.toList());
|
||||
|
||||
final Consumer consumer = new Consumer(0L);
|
||||
|
||||
final Long sumOfSquaredNumbers = FanOutFanIn.fanOutFanIn(requests, consumer);
|
||||
|
||||
Assertions.assertEquals(139, sumOfSquaredNumbers);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.fanout.fanin;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class SquareNumberRequestTest {
|
||||
|
||||
@Test
|
||||
void delayedSquaringTest() {
|
||||
Consumer consumer = new Consumer(10L);
|
||||
|
||||
SquareNumberRequest squareNumberRequest = new SquareNumberRequest(5L);
|
||||
|
||||
squareNumberRequest.delayedSquaring(consumer);
|
||||
|
||||
Assertions.assertEquals(35, consumer.getSumOfSquaredNumbers().get());
|
||||
}
|
||||
}
|
23
gpl-3.0.txt
23
gpl-3.0.txt
@ -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.
|
||||
====
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
|
23
lgpl-3.0.txt
23
lgpl-3.0.txt
@ -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.
|
||||
====
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Abstract Document
|
||||
folder: abstract-document
|
||||
permalink: /patterns/abstract-document/zh
|
||||
permalink: /patterns/abstract-document/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
@ -166,7 +166,7 @@ public class Car extends AbstractDocument implements HasModel, HasPrice, HasPart
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Abstract Factory
|
||||
folder: abstract-factory
|
||||
permalink: /patterns/abstract-factory/zh
|
||||
permalink: /patterns/abstract-factory/
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
@ -168,7 +168,7 @@ public static void main(String[] args) {
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## 适用性
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Active Object
|
||||
folder: active-object
|
||||
permalink: /patterns/active-object/zh
|
||||
permalink: /patterns/active-object/
|
||||
categories: Concurrency
|
||||
language: zh
|
||||
tags:
|
||||
@ -122,4 +122,4 @@ public class Orc extends ActiveCreature {
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Acyclic Visitor
|
||||
folder: acyclic-visitor
|
||||
permalink: /patterns/acyclic-visitor/zh
|
||||
permalink: /patterns/acyclic-visitor/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -123,7 +123,7 @@ public class ConfigureForUnixVisitor implements ZoomVisitor {
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Adapter
|
||||
folder: adapter
|
||||
permalink: /patterns/adapter/zh
|
||||
permalink: /patterns/adapter/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
@ -94,7 +94,7 @@ captain.row();
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
|
||||
## 应用
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Aggregator Microservices
|
||||
folder: aggregator-microservices
|
||||
permalink: /patterns/aggregator-microservices/zh
|
||||
permalink: /patterns/aggregator-microservices/
|
||||
categories: Architectural
|
||||
language: zh
|
||||
tags:
|
||||
@ -95,7 +95,7 @@ curl http://localhost:50004/product
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Ambassador
|
||||
folder: ambassador
|
||||
permalink: /patterns/ambassador/zh
|
||||
permalink: /patterns/ambassador/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
@ -167,7 +167,7 @@ Service result: -1
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: API Gateway
|
||||
folder: api-gateway
|
||||
permalink: /patterns/api-gateway/zh
|
||||
permalink: /patterns/api-gateway/
|
||||
categories: Architectural
|
||||
language: zh
|
||||
tags:
|
||||
@ -123,7 +123,7 @@ public class ApiGateway {
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Arrange/Act/Assert
|
||||
folder: arrange-act-assert
|
||||
permalink: /patterns/arrange-act-assert/zh
|
||||
permalink: /patterns/arrange-act-assert/
|
||||
categories: Idiom
|
||||
language: zh
|
||||
tags:
|
||||
@ -73,10 +73,7 @@ public class Cash {
|
||||
}
|
||||
```
|
||||
|
||||
Then we write our unit tests according to Arrange/Act/Assert pattern. Notice the clearly
|
||||
separated steps for each unit test.
|
||||
|
||||
然后我们根据Arrange / Act / Assert模式编写单元测试。 注意每个单元测试的步骤是分开的清晰的。
|
||||
然后我们根据安排/ 执行 / 断言模式编写单元测试。 注意每个单元测试的步骤是分开的清晰的。
|
||||
|
||||
```java
|
||||
class CashAAATest {
|
||||
@ -129,9 +126,8 @@ class CashAAATest {
|
||||
|
||||
## 适用性
|
||||
|
||||
使用 Arrange/Act/Assert 模式当
|
||||
使用 安排/执行/断言 模式当
|
||||
|
||||
* You need to structure your unit tests so that they're easier to read, maintain, and enhance.
|
||||
* 你需要结构化你的单元测试代码这样它们可以更好的阅读,维护和增强。
|
||||
|
||||
## 鸣谢
|
||||
|
@ -4,33 +4,32 @@ title: Async Method Invocation
|
||||
folder: async-method-invocation
|
||||
permalink: /patterns/async-method-invocation/
|
||||
categories: Concurrency
|
||||
language: zh
|
||||
tags:
|
||||
- Reactive
|
||||
---
|
||||
|
||||
## 含义
|
||||
## 意图
|
||||
|
||||
异步方法是一种调用线程在等待任务结果时候不会被阻塞的模式。该模式提供了对多个任务的并行处理,并通过回调或等待,在所有任务完成后在提供结果读取。
|
||||
异步方法调用是一个调用线程在等待任务结果时不会阻塞的模式。模式为多个独立的任务提供并行的处理方式并且通过回调或等到它们全部完成来接收任务结果。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界案例
|
||||
真实世界例子
|
||||
|
||||
> 发射太空火箭是一项令人兴奋的事业。在任务指挥部下达发射命令后, 经过一些未确定的时间,火箭要么成功发射,要么重演挑战者悲剧。
|
||||
> 发射火箭是一项令人激动的事务。任务指挥官发出了发射命令,经过一段不确定的时间后,火箭要么成功发射,要么惨遭失败。
|
||||
|
||||
简而言之
|
||||
通俗地说
|
||||
|
||||
> 异步方法调用开始任务处理并,在任务结果准备好之前立即返回。任务处理的结果会在稍后再返回给调用者。
|
||||
> 异步方法调用开始任务处理,并在任务完成之前立即返回。 任务处理的结果稍后返回给调用方。
|
||||
|
||||
维基百科的解释
|
||||
维基百科说
|
||||
|
||||
> 在多线程计算机编程中,异步方法调用(AMI),也被称为异步方法调用或异步模式。这是一种设计模式,在这种模式下,调用点在等待被调用代码完成时不会被阻塞。相反,当返回点到达时,调用线程会得到通知。轮询结果是一种不受欢迎的选择。
|
||||
> 在多线程计算机编程中,异步方法调用(AMI),也称为异步方法调用或异步模式,是一种设计模式,其中在等待被调用的代码完成时不会阻塞调用站点。 而是在执行结果到达时通知调用线程。轮询调用结果是不希望的选项。
|
||||
|
||||
**编程示例**
|
||||
**程序示例**
|
||||
|
||||
在这个例子中,我们正在发射太空火箭和部署月球车。
|
||||
|
||||
该应用演示了异步方法调用模式。该模式的关键部分是 `AsyncResult`,它是一个异步计算值的中间容器,`AsyncCallback` 可以在任务完成时提供执行行动作,`AsyncExecutor` 负责管理异步任务的执行。
|
||||
在此示例中,我们正在发射太空火箭并部署月球漫游车。该应用演示了异步方法调用模式。 模式的关键部分是`AsyncResult`(用于异步评估值的中间容器),`AsyncCallback`(可以在任务完成时被执行)和`AsyncExecutor`(用于管理异步任务的执行)。
|
||||
|
||||
```java
|
||||
public interface AsyncResult<T> {
|
||||
@ -54,7 +53,7 @@ public interface AsyncExecutor {
|
||||
}
|
||||
```
|
||||
|
||||
`ThreadAsyncExecutor` 是 `AsyncExecutor` 的一个实现。接下来将着重说明它的一些关键部分。
|
||||
`ThreadAsyncExecutor`是`AsyncExecutor`的实现。 接下来将突出显示其一些关键部分。
|
||||
|
||||
```java
|
||||
public class ThreadAsyncExecutor implements AsyncExecutor {
|
||||
@ -91,14 +90,14 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
||||
}
|
||||
```
|
||||
|
||||
然后我们准备发射一些火箭,看看所有东西是如何一起运作的。
|
||||
然后,我们准备发射一些火箭,看看一切是如何协同工作的。
|
||||
|
||||
```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));
|
||||
@ -106,11 +105,11 @@ public static void main(String[] args) throws Exception {
|
||||
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
|
||||
// 在当前线程中模拟异步任务正在它们自己的线程中执行
|
||||
Thread.sleep(350); // 哦,兄弟,我们在这很辛苦
|
||||
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);
|
||||
@ -118,13 +117,14 @@ public static void main(String[] args) throws Exception {
|
||||
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");
|
||||
}
|
||||
```
|
||||
|
||||
以下是控制台输出。
|
||||
这是程序控制台的输出。
|
||||
|
||||
```java
|
||||
21:47:08.227 [executor-2] INFO com.iluwatar.async.method.invocation.App - Space rocket <test> launched successfully
|
||||
@ -140,22 +140,21 @@ public static void main(String[] args) throws Exception {
|
||||
21:47:08.618 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launch complete
|
||||
```
|
||||
|
||||
## 类图
|
||||
# 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
## 适用性
|
||||
|
||||
在以下场景可以使用异步调用模式
|
||||
在以下情况下使用异步方法调用模式
|
||||
|
||||
* 你有多有可以并行执行的独立任务
|
||||
* 你需要提高一组串行任务的性能
|
||||
* 你的处理能力有限、或者有长期运行的任务,调用者不应该等待任务所有任务运行结束
|
||||
* 您有多个可以并行运行的独立任务
|
||||
* 您需要提高一组顺序任务的性能
|
||||
* 您的处理能力或长时间运行的任务数量有限,并且调用方不应等待任务执行完毕
|
||||
|
||||
## 现实示例
|
||||
## 真实世界例子
|
||||
|
||||
* [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html)
|
||||
* [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html)
|
||||
* [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html)
|
||||
* [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx)
|
||||
|
||||
|
@ -4,33 +4,34 @@ title: Balking
|
||||
folder: balking
|
||||
permalink: /patterns/balking/
|
||||
categories: Concurrency
|
||||
language: zh
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## 含义
|
||||
## 意图
|
||||
|
||||
阻止模式用于防止一个对象在不完整或不适当的状态下执行某段代码。
|
||||
止步模式用于防止对象在不完整或不合适的状态下执行某些代码。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界的案例
|
||||
真实世界例子
|
||||
|
||||
> 洗衣机里有一个用于启动衣物洗涤的启动按钮。当洗衣机没有启动时,该按钮可以正常按下生效,但如果洗衣机已经在洗衣服了,再按下按钮就不生效了。
|
||||
> 洗衣机中有一个开始按钮,用于启动衣物洗涤。当洗衣机处于非活动状态时,按钮将按预期工作,但是如果已经在洗涤,则按钮将不起任何作用。
|
||||
|
||||
简而言之
|
||||
通俗地说
|
||||
|
||||
> 使用阻止模式,只有当对象处于特定状态时,才会执行某段代码。
|
||||
> 使用止步模式,仅当对象处于特定状态时才执行特定代码。
|
||||
|
||||
维基百科的解释
|
||||
维基百科说
|
||||
|
||||
> 阻止模式是一种软件设计模式,它只在对象处于特定状态时对其执行动作。例如,如果一个对象读取 ZIP 文件,当 ZIP 文件没有打开时,如果一个方法在该对象上调用一个获取方法,该对象就会对阻止这个请求。
|
||||
> 禁止模式是一种软件设计模式,仅当对象处于特定状态时才对对象执行操作。例如,一个对象读取zip压缩文件并在压缩文件没打开的时候调用get方法,对象将在请求的时候”止步“。
|
||||
|
||||
**编程示例**
|
||||
**程序示例**
|
||||
|
||||
在这个例子的实现中,`WashingMachine` 对象存在 2 种状态: `ENABLED` 和 `WASHING`。如果该对象处于 `ENABLED` 状态,则使用一个线程安全的方法可以其状态改变为 `WASHING`。在另一方面,如果它已经处于 `WASHING` 状态,而任何其他线程执行了 `wash()`,它不会执行该指令,而是什么都不做就返回。
|
||||
在此示例中,` WashingMachine`是一个具有两个状态的对象,可以处于两种状态:ENABLED和WASHING。 如果机器已启用,则使用线程安全方法将状态更改为WASHING。 另一方面,如果已经进行了清洗并且任何其他线程执行`wash()`,则它将不执行该操作,而是不执行任何操作而返回。
|
||||
|
||||
以下是 `WashingMachine` 类的相关代码。
|
||||
这里是`WashingMachine` 类相关的部分。
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
@ -69,7 +70,7 @@ public class WashingMachine {
|
||||
}
|
||||
```
|
||||
|
||||
以下是 `WashingMachine` 使用的简单 `DelayProvider` 接口。
|
||||
这里是一个`WashingMachine`所使用的`DelayProvider`简单接口。
|
||||
|
||||
```java
|
||||
public interface DelayProvider {
|
||||
@ -77,7 +78,7 @@ public interface DelayProvider {
|
||||
}
|
||||
```
|
||||
|
||||
现在我们介绍一下使用 `WashingMachine` 的应用。
|
||||
现在,我们使用`WashingMachine`介绍该应用程序。
|
||||
|
||||
```java
|
||||
public static void main(String... args) {
|
||||
@ -96,7 +97,7 @@ public interface DelayProvider {
|
||||
}
|
||||
```
|
||||
|
||||
以下是程序的控制台输出。
|
||||
下面是程序的输出。
|
||||
|
||||
```
|
||||
14:02:52.268 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Actual machine state: ENABLED
|
||||
@ -110,20 +111,21 @@ public interface DelayProvider {
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
## 适用性
|
||||
|
||||
在以下情况下可以使用阻止模式:
|
||||
|
||||
* 你想要在某个对象上调用一个动作,只有当该对象处于特定状态时才允许该调用。
|
||||
* 对象一般只处于容易暂时阻止的状态,只不过该时间是未知的。
|
||||
使用止步模式当
|
||||
|
||||
## 教学
|
||||
* 您只想在对象处于特定状态时才对其调用操作
|
||||
* 对象通常仅处于容易暂时停止但状态未知的状态
|
||||
|
||||
* [Guarded Suspension Pattern](https://java-design-patterns.com/patterns/guarded-suspension/)
|
||||
* [Double Checked Locking Pattern](https://java-design-patterns.com/patterns/double-checked-locking/)
|
||||
## 相关模式
|
||||
|
||||
## 引用
|
||||
* [保护性暂挂模式](https://java-design-patterns.com/patterns/guarded-suspension/)
|
||||
* [双重检查锁模式](https://java-design-patterns.com/patterns/double-checked-locking/)
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [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)
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Bridge
|
||||
folder: bridge
|
||||
permalink: /patterns/bridge/zh
|
||||
permalink: /patterns/bridge/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
@ -184,7 +184,7 @@ hammer.unwield();
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Builder
|
||||
folder: builder
|
||||
permalink: /patterns/builder/zh
|
||||
permalink: /patterns/builder/
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
@ -114,7 +114,7 @@ var mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -4,36 +4,32 @@ title: Business Delegate
|
||||
folder: business-delegate
|
||||
permalink: /patterns/business-delegate/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## 含义
|
||||
## 意图
|
||||
|
||||
业务委托模式(译者:国内也有翻译成业务代表模式)在表现层和业务层之间增加了一个抽象层。通过使用该模式,我们获得了各层之间的松散耦合,并封装了关于如何定位、连接和与构成应用程序的业务对象进行交互的知识。
|
||||
业务委托模式在表示层和业务层之间添加了一个抽象层。 通过使用该模式,我们获得了各层之间的松散耦合,并封装了有关如何定位,连接到组成应用程序的业务对象以及与之交互的逻辑。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界的案例
|
||||
真实世界例子
|
||||
|
||||
> 一个手机应用程序承诺将现有的任何电影传输到你的手机上。它捕获了用户的搜索关键字内容,并将其传递给业务委托层。业务委托层选择最合适的视频流服务,并从该服务进行视频播放。
|
||||
> 手机应用程序承诺将现有的任何电影流式传输到您的手机。它捕获用户的搜索字符串,并将其传递给业务委托层。业务委托层选择最合适的视频流服务,然后从那里播放视频。
|
||||
|
||||
简而言之
|
||||
通俗的说
|
||||
|
||||
> 业务委托模式在表现层和业务层之间增加了一个抽象层。
|
||||
> 业务委托模式在表示层和业务层之间添加了一个抽象层。
|
||||
|
||||
维基百科的解释
|
||||
维基百科说
|
||||
|
||||
> 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.
|
||||
>
|
||||
> 业务委托模式是一种 Java EE 设计模式。这种模式旨在减少业务服务和所连接的表现层之间的耦合度,并隐藏服务的实现细节(包括 EJB 架构的查询和可访问性)。业务代表作为一个适配器,从表现层调用业务对象。
|
||||
> 业务委托是一种Java EE设计模式。 该模式旨在减少业务服务与连接的表示层之间的耦合,并隐藏服务的实现细节(包括EJB体系结构的查找和可访问性)。 业务代表充当适配器,以从表示层调用业务对象。
|
||||
|
||||
**编程示例**
|
||||
**程序示例**
|
||||
|
||||
首先,我们实现了一个视频流服务的抽象,和几个具体实现。
|
||||
首先,我们有视频流服务的抽象类和一些它的实现。
|
||||
|
||||
```java
|
||||
public interface VideoStreamingService {
|
||||
@ -57,7 +53,7 @@ public class YouTubeService implements VideoStreamingService {
|
||||
}
|
||||
```
|
||||
|
||||
接下来,我们实现一个查询服务,用于决定使用哪个视频流服务。
|
||||
然后我们有一个查找服务来决定我们使用哪个视频流服务。
|
||||
|
||||
```java
|
||||
@Setter
|
||||
@ -76,7 +72,7 @@ public class BusinessLookup {
|
||||
}
|
||||
```
|
||||
|
||||
业务委托层使用业务查询,将电影播放请求路由到合适的视频流服务。
|
||||
业务委托类使用业务查找服务将电影播放请求路由到合适的视频流服务。
|
||||
|
||||
```java
|
||||
@Setter
|
||||
@ -108,7 +104,7 @@ public class MobileClient {
|
||||
}
|
||||
```
|
||||
|
||||
最后,我们展示一下这个示例完整的操作。
|
||||
最后我们展示完整示例。
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
@ -127,7 +123,7 @@ public class MobileClient {
|
||||
}
|
||||
```
|
||||
|
||||
以下是终端输出的内容。
|
||||
这是控制台的输出。
|
||||
|
||||
```
|
||||
21:15:33.790 [main] INFO com.iluwatar.business.delegate.NetflixService - NetflixService is now processing
|
||||
@ -136,25 +132,25 @@ public class MobileClient {
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 相关模式
|
||||
|
||||
* [Service locator pattern](https://java-design-patterns.com/patterns/service-locator/)
|
||||
* [服务定位器模式](https://java-design-patterns.com/patterns/service-locator/)
|
||||
|
||||
## 适用场景
|
||||
## 适用性
|
||||
|
||||
业务委托模式的适用场景:
|
||||
使用业务委托模式当
|
||||
|
||||
* 你希望表现层和业务层之间是松耦合的。
|
||||
* 你想要协调对多个业务服务的调用。
|
||||
* 你想要对服务查询、服务调用进行封装。
|
||||
* 你希望表示层和业务层之间的松散耦合
|
||||
* 你想编排对多个业务服务的调用
|
||||
* 你希望封装查找服务和服务调用
|
||||
|
||||
## 教程
|
||||
|
||||
* [Business Delegate Pattern at TutorialsPoint](https://www.tutorialspoint.com/design_pattern/business_delegate_pattern.htm)
|
||||
|
||||
## 引用
|
||||
## 鸣谢
|
||||
|
||||
* [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)
|
||||
|
235
localization/zh/bytecode/README.md
Normal file
235
localization/zh/bytecode/README.md
Normal file
@ -0,0 +1,235 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Bytecode
|
||||
folder: bytecode
|
||||
permalink: /patterns/bytecode/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- Game programming
|
||||
---
|
||||
|
||||
## 意图
|
||||
|
||||
允许编码行为作为虚拟机的指令。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界例子
|
||||
|
||||
> 一个团队正在开发一款新的巫师对战游戏。巫师的行为需要经过精心的调整和上百次的游玩测试。每次当游戏设计师想改变巫师行为时都让程序员去修改代码这是不妥的,所以巫师行为以数据驱动的虚拟机方式实现。
|
||||
|
||||
通俗地说
|
||||
|
||||
> 字节码模式支持由数据而不是代码驱动的行为。
|
||||
|
||||
[Gameprogrammingpatterns.com](https://gameprogrammingpatterns.com/bytecode.html) 中做了如下阐述:
|
||||
|
||||
> 指令集定义了可以执行的低级操作。一系列指令被编码为字节序列。虚拟机一次一条地执行这些指令,中间的值用栈处理。通过组合指令,可以定义复杂的高级行为。
|
||||
|
||||
**程序示例**
|
||||
|
||||
其中最重要的游戏对象是`巫师`类。
|
||||
|
||||
```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++;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
下面我们展示虚拟机可用的指令。每个指令对于如何操作栈中的数据都有自己的语义。例如,增加指令,其取得栈顶的两个元素并把结果压入栈中。
|
||||
|
||||
```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
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
我们示例的核心是`虚拟机`类。 它将指令作为输入并执行它们以提供游戏对象行为。
|
||||
|
||||
```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 ->
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
现在我们可以展示使用虚拟机的完整示例。
|
||||
|
||||
```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"));
|
||||
}
|
||||
```
|
||||
|
||||
下面是控制台输出。
|
||||
|
||||
```
|
||||
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 []
|
||||
```
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
||||
|
||||
当您需要定义很多行为并且游戏的实现语言不合适时,请使用字节码模式,因为:
|
||||
|
||||
* 它的等级太低,使得编程变得乏味或容易出错。
|
||||
* 由于编译时间慢或其他工具问题,迭代它需要很长时间。
|
||||
* 它有太多的信任。 如果您想确保定义的行为不会破坏游戏,您需要将其与代码库的其余部分进行沙箱化。
|
||||
|
||||
## 相关模式
|
||||
|
||||
* [Interpreter](https://java-design-patterns.com/patterns/interpreter/)
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [Game programming patterns](http://gameprogrammingpatterns.com/bytecode.html)
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Caching
|
||||
folder: caching
|
||||
permalink: /patterns/caching/zh
|
||||
permalink: /patterns/caching/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -14,7 +14,7 @@ tags:
|
||||
为了避免昂贵的资源重新获取,方法是在资源使用后不立即释放资源。资源保留其身份,保留在某些快速访问的存储中,并被重新使用,以避免再次获取它们。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
在以下情况下使用缓存模式
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Callback
|
||||
folder: callback
|
||||
permalink: /patterns/callback/zh
|
||||
permalink: /patterns/callback/
|
||||
categories: Idiom
|
||||
language: zh
|
||||
tags:
|
||||
@ -69,7 +69,7 @@ public final class SimpleTask extends Task {
|
||||
task.executeWith(() -> LOGGER.info("I'm done now."));
|
||||
```
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
使用回调模式当
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Chain of responsibility
|
||||
folder: chain
|
||||
permalink: /patterns/chain/zh
|
||||
permalink: /patterns/chain/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -139,7 +139,7 @@ king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); // Orc so
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
使用责任链模式当
|
||||
|
@ -4,39 +4,45 @@ title: Circuit Breaker
|
||||
folder: circuit-breaker
|
||||
permalink: /patterns/circuit-breaker/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- Performance
|
||||
- Decoupling
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## 含义
|
||||
## 意图
|
||||
|
||||
以这样的方式(译者:指断路器方式)处理昂贵的远程服务调用,可以防止单个服务/组件的故障导致整个应用程序崩溃,同时我们可以尽快地进行服务重连。
|
||||
以这样一种方式处理昂贵的远程服务调用,即单个服务/组件的故障不会导致整个应用程序宕机,我们可以尽快重新连接到服务。
|
||||
|
||||
## 解释
|
||||
|
||||
现实世界案例
|
||||
真实世界例子
|
||||
|
||||
> 设想一下,一个网络应用程序既有本地文件/图像,又有用于获取数据的远程服务。这些远程服务可能在某些时候是健康的、有反应的,也可能在某些时候由于各种原因而变得缓慢和无反应。因此,如果其中一个远程服务速度慢或不能成功响应,我们的应用程序将尝试使用多个线程/进程从远程服务中获取响应,很快所有的线程/进程都会挂起(也称为线程饥饿 thread starvation),从而导致我们整个 Web 应用程序崩溃。我们应该能够检测到这种情况,并向用户显示一个适当的信息,以便用户可以探索应用程序的其他部分,而不受远程服务故障的影响。同时,其他正常工作的服务应该保持运作,不受这次故障的影响。
|
||||
> 想象一个 Web 应用程序,它同时具有用于获取数据的本地文件/图像和远程服务。 这些远程服务有时可能健康且响应迅速,或者由于各种原因可能在某 个时间点变得缓慢和无响应。因此,如果其中一个远程服务缓慢或未成功响应,我们的应用程序将尝试使用多个线程/进程从远程服务获取响应,很快它们都会挂起(也称为 [线程饥饿][thread starvation](https://en.wikipedia.org/wiki/Starvation_(computer_science)))导致我们的整个 Web 应用程序崩溃。我们应该能够检测到这种情况并向用户显示适当的消息,以便他/她可以探索不受远程服务故障影响的应用程序的其他部分。 同时,其他正常工作的服务应保持正常运行,不受此故障的影响。
|
||||
>
|
||||
|
||||
简而言之
|
||||
通俗地说
|
||||
|
||||
> 断路器允许优雅地处理失败的远程服务。当我们的应用程序的所有部分都高度解耦时,这种方式的效果会很好,一个组件的失败并不会导致其他部分停止工作。
|
||||
> 断路器允许优雅地处理失败的远程服务。当我们应用程序的所有部分彼此高度解耦时,它特别有用,一个组件的故障并不意味着其他部分将停止工作。
|
||||
|
||||
维基百科的解释
|
||||
维基百科说
|
||||
|
||||
> 断路器是现代软件开发中使用的一种设计模式。它用于检测故障,并封装了防止故障不断复发的逻辑,在维护期间,临时地处理外部系统故障或意外的系统问题。
|
||||
> 断路器是现代软件开发中使用的一种设计模式。 它用于检测故障并封装防止故障不断重复发生、维护期间、临时外部系统故障或意外系统困难的逻辑。
|
||||
|
||||
## Programmatic Example
|
||||
## 程序示例
|
||||
|
||||
那么,这一切是如何实现的呢?考虑到上面的例子,我们将在一个简单的例子中模拟这个功能。一个监控服务(译者:下图的 Monitoring Service)模拟了网络应用,进行本地和远程调用。
|
||||
So, how does this all come together? With the above example in mind we will imitate the
|
||||
functionality in a simple example. A monitoring service mimics the web app and makes both local and
|
||||
remote calls.
|
||||
|
||||
该服务架构如下:
|
||||
那么,这一切是如何结合在一起的呢? 记住上面的例子,我们将在一个简单的例子中模仿这个功能。 监控服务模仿 Web 应用程序并进行本地和远程调用。
|
||||
|
||||

|
||||
服务架构如下:
|
||||
|
||||
终端用户(译者:上图的 End User)应用的代码如下:
|
||||

|
||||
|
||||
在代码方面,最终用户应用程序是:
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
@ -61,44 +67,44 @@ public class App {
|
||||
var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2,
|
||||
2000 * 1000 * 1000);
|
||||
|
||||
//Create an object of monitoring service which makes both local and remote calls
|
||||
// 创建一个可以进行本地和远程调用的监控服务对象
|
||||
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
|
||||
quickServiceCircuitBreaker);
|
||||
|
||||
//Fetch response from local resource
|
||||
// 获取本地资源
|
||||
LOGGER.info(monitoringService.localResourceResponse());
|
||||
|
||||
//Fetch response from delayed service 2 times, to meet the failure threshold
|
||||
// 从延迟服务中获取响应 2 次,以满足失败阈值
|
||||
LOGGER.info(monitoringService.delayedServiceResponse());
|
||||
LOGGER.info(monitoringService.delayedServiceResponse());
|
||||
|
||||
//Fetch current state of delayed service circuit breaker after crossing failure threshold limit
|
||||
//which is OPEN now
|
||||
// 在超过故障阈值限制后获取延迟服务断路器的当前状态
|
||||
// 现在是打开状态
|
||||
LOGGER.info(delayedServiceCircuitBreaker.getState());
|
||||
|
||||
//Meanwhile, the delayed service is down, fetch response from the healthy quick service
|
||||
// 同时,延迟服务宕机,从健康快速服务获取响应
|
||||
LOGGER.info(monitoringService.quickServiceResponse());
|
||||
LOGGER.info(quickServiceCircuitBreaker.getState());
|
||||
|
||||
//Wait for the delayed service to become responsive
|
||||
// 等待延迟的服务响应
|
||||
try {
|
||||
LOGGER.info("Waiting for delayed service to become responsive");
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//Check the state of delayed circuit breaker, should be HALF_OPEN
|
||||
// 检查延时断路器的状态,应该是HALF_OPEN
|
||||
LOGGER.info(delayedServiceCircuitBreaker.getState());
|
||||
|
||||
//Fetch response from delayed service, which should be healthy by now
|
||||
// 从延迟服务中获取响应,现在应该是健康的
|
||||
LOGGER.info(monitoringService.delayedServiceResponse());
|
||||
//As successful response is fetched, it should be CLOSED again.
|
||||
// 获取成功响应后,它的状态应该是关闭。
|
||||
LOGGER.info(delayedServiceCircuitBreaker.getState());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
监控服务代码(译者:上图的 monitoring service):
|
||||
监控服务类:
|
||||
|
||||
```java
|
||||
public class MonitoringService {
|
||||
@ -112,7 +118,7 @@ public class MonitoringService {
|
||||
this.quickService = quickService;
|
||||
}
|
||||
|
||||
//Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic
|
||||
// 假设:本地服务不会失败,无需将其包装在断路器逻辑中
|
||||
public String localResourceResponse() {
|
||||
return "Local Service is working";
|
||||
}
|
||||
@ -144,7 +150,7 @@ public class MonitoringService {
|
||||
}
|
||||
}
|
||||
```
|
||||
可以看出,它直接进行了获取本地资源的调用,但它把对远程(昂贵的)服务的调用包装在一个断路器对象中,这样可以防止出现如下故障:
|
||||
可以看出,它直接调用获取本地资源,但它将对远程(昂贵)服务的调用包装在断路器对象中,防止故障如下:
|
||||
|
||||
```java
|
||||
public class DefaultCircuitBreaker implements CircuitBreaker {
|
||||
@ -171,11 +177,11 @@ public class DefaultCircuitBreaker implements CircuitBreaker {
|
||||
DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold,
|
||||
long retryTimePeriod) {
|
||||
this.service = serviceToCall;
|
||||
// We start in a closed state hoping that everything is fine
|
||||
// 我们从关闭状态开始希望一切都是正常的
|
||||
this.state = State.CLOSED;
|
||||
this.failureThreshold = failureThreshold;
|
||||
// Timeout for the API request.
|
||||
// Used to break the calls made to remote resource if it exceeds the limit
|
||||
// API的超时时间.
|
||||
// 用于在超过限制时中断对远程资源的调用
|
||||
this.timeout = timeout;
|
||||
this.retryTimePeriod = retryTimePeriod;
|
||||
//An absurd amount of time in future which basically indicates the last failure never happened
|
||||
@ -183,7 +189,7 @@ public class DefaultCircuitBreaker implements CircuitBreaker {
|
||||
this.failureCount = 0;
|
||||
}
|
||||
|
||||
// Reset everything to defaults
|
||||
// 重置所有
|
||||
@Override
|
||||
public void recordSuccess() {
|
||||
this.failureCount = 0;
|
||||
@ -199,18 +205,18 @@ public class DefaultCircuitBreaker implements CircuitBreaker {
|
||||
this.lastFailureResponse = response;
|
||||
}
|
||||
|
||||
// Evaluate the current state based on failureThreshold, failureCount and lastFailureTime.
|
||||
// 根据 failureThreshold、failureCount 和 lastFailureTime 评估当前状态。
|
||||
protected void evaluateState() {
|
||||
if (failureCount >= failureThreshold) { //Then something is wrong with remote service
|
||||
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
|
||||
//We have waited long enough and should try checking if service is up
|
||||
// 我们已经等得够久了,应该尝试检查服务是否已启动
|
||||
state = State.HALF_OPEN;
|
||||
} else {
|
||||
//Service would still probably be down
|
||||
// 服务可能仍会出现故障
|
||||
state = State.OPEN;
|
||||
}
|
||||
} else {
|
||||
//Everything is working fine
|
||||
// 一切正常
|
||||
state = State.CLOSED;
|
||||
}
|
||||
}
|
||||
@ -253,16 +259,15 @@ public class DefaultCircuitBreaker implements CircuitBreaker {
|
||||
public String attemptRequest() throws RemoteServiceException {
|
||||
evaluateState();
|
||||
if (state == State.OPEN) {
|
||||
// return cached response if the circuit is in OPEN state
|
||||
// 如果电路处于打开状态,则返回缓存的响应
|
||||
return this.lastFailureResponse;
|
||||
} else {
|
||||
// Make the API request if the circuit is not OPEN
|
||||
// 如果电路未打开,则发出 API 请求
|
||||
try {
|
||||
//In a real application, this would be run in a thread and the timeout
|
||||
//parameter of the circuit breaker would be utilized to know if service
|
||||
//is working. Here, we simulate that based on server response itself
|
||||
//在实际应用程序中,这将在线程中运行,并且将利用断路器的超时参数来了解服务
|
||||
// 是否正在工作。 在这里,我们根据服务器响应本身模拟
|
||||
var response = service.call();
|
||||
// Yay!! the API responded fine. Let's reset everything.
|
||||
// api 响应正常,重置所有。
|
||||
recordSuccess();
|
||||
return response;
|
||||
} catch (RemoteServiceException ex) {
|
||||
@ -274,39 +279,39 @@ public class DefaultCircuitBreaker implements CircuitBreaker {
|
||||
}
|
||||
```
|
||||
|
||||
上述模式是如何防止失败的呢?让我们通过它所实现的这个有限状态机来了解。
|
||||
上述模式如何防止失败? 让我们通过它实现的这个有限状态机来理解。
|
||||
|
||||

|
||||

|
||||
|
||||
- 我们用 `timeout`(超时)、 `failureThreshold` (失败阈值)、`retryTimePeriod`(重试时间周期) 参数初始化断路器对象 ,用于确定 API 的适应性。
|
||||
- 最初,断路器处于 `closed` 关闭状态,没有发生对 API 的远程调用。
|
||||
- 每次调用成功,我们就把状态重置为开始时的样子。
|
||||
- 如果失败的次数超过了一定的阈值(`failureThreshold`),断路器就会进入 `open` 开启状态,它的作用就像一个开启的电路,阻止远程服务的调用,从而节省资源。
|
||||
- 一旦我们超过重试时间周期(`retryTimePeriod`),断路器就会转到 `half-open` 半启用状态,并再次调用远程服务,检查服务是否正常,以便我们可以提供最新的响应内容。如果远程服务调用失败会使断路器回到 `open` 状态,并在重试超时后进行另一次尝试;如果远程服务调用成功则使断路器进入 `closed` 状态,这样一切又开始正常工作。
|
||||
- 我们使用某些参数初始化断路器对象:`timeout`、`failureThreshold` 和 `retryTimePeriod`,这有助于确定 API 的弹性。
|
||||
- 最初,我们处于“关闭”状态,没有发生对 API 的远程调用。
|
||||
- 每次调用成功时,我们都会将状态重置为开始时的状态。
|
||||
- 如果失败次数超过某个阈值,我们将进入“open”状态,这就像开路一样,阻止远程服务调用,从而节省资源。 (这里,我们从 API 返回名为 ```stale response``` 的响应)
|
||||
- 一旦超过重试超时时间,我们就会进入“半开”状态并再次调用远程服务以检查服务是否正常工作,以便我们可以提供新鲜内容。 失败将其设置回“打开”状态,并在重试超时时间后进行另一次尝试,而成功将其设置为“关闭”状态,以便一切重新开始正常工作。
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
## 适用性
|
||||
|
||||
在以下场景下,可以使用断路器模式:
|
||||
在以下情况下使用断路器模式
|
||||
|
||||
- 构建一个高可用的应用程序,某些些服务的失败不会导致整个应用程序的崩溃。
|
||||
- 构建一个持续运行(长期在线)的应用程序,以便其组件可以在不完全关闭的情况下进行升级。
|
||||
- 构建一个容错应用程序,其中某些服务的故障不应导致整个应用程序宕机。
|
||||
- 构建一个持续运行(永远在线)的应用程序,这样它的组件就可以在不完全关闭的情况下升级。
|
||||
|
||||
## 相关模式
|
||||
|
||||
- [Retry Pattern](https://github.com/iluwatar/java-design-patterns/tree/master/retry)
|
||||
|
||||
## 现实案例
|
||||
## 真实世界例子
|
||||
|
||||
* [Spring Circuit Breaker module](https://spring.io/guides/gs/circuit-breaker)
|
||||
* [Netflix Hystrix API](https://github.com/Netflix/Hystrix)
|
||||
|
||||
## 引用
|
||||
## 鸣谢
|
||||
|
||||
* [Understanding Circuit Breaker Pattern](https://itnext.io/understand-circuitbreaker-design-pattern-with-simple-practical-example-92a752615b42)
|
||||
* [Martin Fowler on Circuit Breaker](https://martinfowler.com/bliki/CircuitBreaker.html)
|
||||
* [Fault tolerance in a high volume, distributed system](https://medium.com/netflix-techblog/fault-tolerance-in-a-high-volume-distributed-system-91ab4faae74a)
|
||||
* [Circuit Breaker pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker)
|
||||
* [Circuit Breaker pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker)
|
||||
|
134
localization/zh/cloud-static-content-hosting/README.md
Normal file
134
localization/zh/cloud-static-content-hosting/README.md
Normal file
@ -0,0 +1,134 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Static Content Hosting
|
||||
folder: cloud-static-content-hosting
|
||||
permalink: /patterns/cloud-static-content-hosting/
|
||||
categories: Cloud
|
||||
language: zh
|
||||
tags:
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## 意图
|
||||
|
||||
将静态内容部署到基于云的存储服务,该服务可以将它们直接交付给客户端。 这可以减少对昂贵计算实例的需求。
|
||||
|
||||
## 解释
|
||||
|
||||
真实世界例子
|
||||
|
||||
> 全球性的营销网站(静态内容)需要快速的部署以开始吸引潜在的客户。为了将托管费用和维护成本降至最低,使用云托管存储服务和内容交付网络。
|
||||
|
||||
通俗地说
|
||||
|
||||
> 静态内容托管模式利用云原生存储服务来存储内容和全球内容交付网络,将其缓存在世界各地的多个数据中心。 在静态网站上,单个网页包含静态内容。 它们还可能包含客户端脚本,例如 Javascript。相比之下,动态网站依赖于服务器端处理,包括服务器端脚本,如 PHP、JSP 或 ASP.NET。
|
||||
|
||||
维基百科说
|
||||
|
||||
> 与由 Web 应用程序生成的动态网页相反,静态网页(有时称为平面网页或固定网页)是完全按照存储的方式传送到用户的网页浏览器的网页。静态网页适用于从不或很少需要更新的内容,尽管现代
|
||||
> Web 模板系统正在改变这一点。可以将大量静态页面作为文件进行维护,没有自动化工具(例如静态站点生成器)是不切实际的。
|
||||
|
||||
**示例**
|
||||
|
||||

|
||||
|
||||
在这个例子中我们使用AWS S3创建一个静态网站,并利用 AWS Cloudfront 在全球范围内分发内容。
|
||||
|
||||
1. 首先你需要一个AWS账户,你可以在这个创建一个免费的:[AWS Free Tier](https://aws.amazon.com/free/free-tier/)
|
||||
|
||||
2. 登陆 [AWS控制台](https://console.aws.amazon.com/console/home?nc2=h_ct&src=header-signin)
|
||||
|
||||
3. 进入身份和接入管理服务 (IAM) .
|
||||
|
||||
4. 创建一个仅具有此应用程序必要权限的IAM用户。
|
||||
|
||||
* 点击 `用户`
|
||||
* 点击 `添加用户`. 选择你想要的 `用户名`, `接入类型`应该是 `编程式接入`. 点击 `下一步: 权限`.
|
||||
* 选择 `直接附加已存在的策略`. 选择 `AmazonS3FullAccess` 和 `CloudFrontFullAccess`. Click `下一步: 标签`.
|
||||
* 没有需要的标签, 所以直接点击 `下一步: 回顾`.
|
||||
* 检查呈现的信息,没问题的话点击`创建用户`
|
||||
* 完成这个示例所需要的`访问秘钥Id`和`访问秘钥密码`将会呈现在你面前,请妥善保管。
|
||||
* 点击 `关闭`.
|
||||
|
||||
5. [安装AWS 命令行工具 (AWS CLI)](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv1.html) 来获得编程式访问AWS云。
|
||||
|
||||
6. 使用`aws configure`命令来配置AWS CLI [说明书](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-config)
|
||||
|
||||
7. 为web站点创建AWS S3 bucket。 注意S3 bucket名字必须要在全球范围内唯一。
|
||||
|
||||
|
||||
* 语法是 `aws s3 mb <bucket name>` [说明书](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-buckets-creating)
|
||||
* 比如 `aws s3 mb s3://my-static-website-jh34jsjmg`
|
||||
* 使用列出现有存储桶的命令`aws s3 ls`验证存储桶是否已成功创建
|
||||
|
||||
8. 使用命令`aws s3 website`来配置bucket作为web站点。 [说明书](https://docs.aws.amazon.com/cli/latest/reference/s3/website.html).
|
||||
|
||||
* 比如`aws s3 website s3://my-static-website-jh34jsjmg --index-document index.html --error-document error.html`
|
||||
|
||||
9. 上传内容到bucket中。
|
||||
* 首先创建内容,至少包含`index.html`和`error.html`文档。
|
||||
* 上传内容到你的bucket中。 [说明书](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-objects-copy)
|
||||
* 比如`aws s3 cp index.html s3://my-static-website-jh34jsjmg` and `aws s3 cp error.html s3://my-static-website-jh34jsjmg`
|
||||
|
||||
10. 然后我们需要设置bucket的策略以允许读取访问。
|
||||
|
||||
* 使用以下内容创建`policy.json`(注意需要将bucket名称替换为自己的)。
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "PublicReadGetObject",
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::my-static-website-jh34jsjmg/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
* 根据这些设置桶策略[说明书](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-policy.html)
|
||||
* 比如 `aws s3api put-bucket-policy --bucket my-static-website-jh34jsjmg --policy file://policy.json`
|
||||
|
||||
11. 使用浏览器测试web站点。
|
||||
|
||||
* web站点的URL格式是 `http://<bucket-name>.s3-website-<region-name>.amazonaws.com`
|
||||
* 比如 这个站点创建在 `eu-west-1` 区域 ,名字是 `my-static-website-jh34jsjmg` 所以它可以通过 `http://my-static-website-jh34jsjmg.s3-website-eu-west-1.amazonaws.com`来访问。
|
||||
|
||||
12. 为web站点创建CloudFormation 分发。
|
||||
|
||||
* 语法文档在这里 [this reference](https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-distribution.html)
|
||||
* 比如,最简单的方式是使用命令l `aws cloudfront create-distribution --origin-domain-name my-static-website-jh34jsjmg.s3.amazonaws.com --default-root-object index.html`
|
||||
* 也支持JSON格式的配置 比如使用 `--distribution-config file://dist-config.json` 来传递分发的配置文件参数
|
||||
* 命令的舒勇将显示准确的分配配置项,包括包括可用于测试的生成的 CloudFront 域名,例如 `d2k3xwnaqa8nqx.cloudfront.net`
|
||||
* CloudFormation 分发部署需要一些时间,但一旦完成,您的网站就会从全球各地的数据中心提供服务!
|
||||
|
||||
13. 就是这样! 您已经实现了一个静态网站,其内容分发网络以闪电般的速度在世界各地提供服务。
|
||||
|
||||
* 要更新网站,您需要更新 S3 存储桶中的对象并使 CloudFront 分配中的对象无效
|
||||
* 要从 AWS CLI 执行此操作,请参阅 [this reference](https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-invalidation.html)
|
||||
* 您可能想要做的进一步开发是通过 https 提供内容并为您的站点添加域名
|
||||
|
||||
## 适用性
|
||||
|
||||
当您想要执行以下操作时,请使用静态内容托管模式:
|
||||
|
||||
* 最小化包含一些静态资源的网站和应用程序的托管成本。
|
||||
* 使用静态内容构建全球可用的网站
|
||||
* 监控网站流量、带宽使用、成本等。
|
||||
|
||||
## 典型用例
|
||||
|
||||
* 具有全球影响力的网站
|
||||
* 静态网站生成器生成的内容
|
||||
* 没有动态内容要求的网站
|
||||
|
||||
## 真实世界例子
|
||||
|
||||
* [Java Design Patterns web site](https://java-design-patterns.com)
|
||||
|
||||
## 鸣谢
|
||||
|
||||
* [Static Content Hosting pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/static-content-hosting)
|
@ -4,6 +4,7 @@ title: Collection Pipeline
|
||||
folder: collection-pipeline
|
||||
permalink: /patterns/collection-pipeline/
|
||||
categories: Functional
|
||||
language: zh
|
||||
tags:
|
||||
- Reactive
|
||||
---
|
||||
@ -13,7 +14,7 @@ tags:
|
||||
在函数式编程中,可以通过一系列较小的模块化函数或操作来编排复杂的操作。这一系列函数被称为函数组合。当一个数据集合流经一个函数组合时,它就成为一个集合管道。函数组合和集合管道是函数式编程中经常使用的两种设计模式。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
在以下场景适用集合管道模式:
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Command
|
||||
folder: command
|
||||
permalink: /patterns/command/zh
|
||||
permalink: /patterns/command/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -222,7 +222,7 @@ goblin.printStatus();
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
使用命令模式当你想
|
||||
|
@ -4,6 +4,7 @@ title: Composite Entity
|
||||
folder: composite-entity
|
||||
permalink: /patterns/composite-entity/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
- Enterprise Integration Pattern
|
||||
---
|
||||
@ -109,7 +110,7 @@ Arrays.stream(console.getData()).forEach(LOGGER::info);
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Composite
|
||||
folder: composite
|
||||
permalink: /patterns/composite/zh
|
||||
permalink: /patterns/composite/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
@ -154,7 +154,7 @@ elfMessage.print(); // Much wind pours from your mouth.
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Converter
|
||||
folder: converter
|
||||
permalink: /patterns/converter/zh
|
||||
permalink: /patterns/converter/
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
@ -86,7 +86,7 @@ var user = userConverter.convertFromDto(dtoUser);
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Data Access Object
|
||||
folder: dao
|
||||
permalink: /patterns/dao/zh
|
||||
permalink: /patterns/dao/
|
||||
categories: Architectural
|
||||
language: zh
|
||||
tags:
|
||||
@ -149,7 +149,7 @@ customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@6ec8211c
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -3,8 +3,8 @@ layout: pattern
|
||||
title: Data Bus
|
||||
folder: data-bus
|
||||
permalink: /patterns/data-bus/
|
||||
|
||||
categories: Architectural
|
||||
language: zh
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
@ -14,7 +14,7 @@ tags:
|
||||
数据总线模式(译者:实际上,就是 Event-Bus 消息总线模式)允许在一个应用程序的组件之间收发消息/事件,而不需要这些组件相互感知,它们只需要知道所发送/接收的消息/事件的类型即可。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
可以在以下场景使用数据总线模式:
|
||||
|
@ -4,6 +4,7 @@ title: Data Mapper
|
||||
folder: data-mapper
|
||||
permalink: /patterns/data-mapper/
|
||||
categories: Architectural
|
||||
language: zh
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
@ -12,7 +13,7 @@ tags:
|
||||
一个用于在持久化对象和数据库之间传输数据的映射器,同时保持它们之间和映射器本身的独立性。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
数据映射器适用于以下场景:
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Data Transfer Object
|
||||
folder: data-transfer-object
|
||||
permalink: /patterns/data-transfer-object/zh
|
||||
permalink: /patterns/data-transfer-object/
|
||||
categories: Architectural
|
||||
language: zh
|
||||
tags:
|
||||
@ -94,7 +94,7 @@ public class CustomerResource {
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Decorator
|
||||
folder: decorator
|
||||
permalink: /patterns/decorator/zh
|
||||
permalink: /patterns/decorator/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
@ -106,7 +106,7 @@ clubbedTroll.fleeBattle(); // The troll shrieks in horror and runs away!
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
使用装饰者
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Delegation
|
||||
folder: delegation
|
||||
permalink: /patterns/delegation/zh
|
||||
permalink: /patterns/delegation/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
@ -16,7 +16,7 @@ tags:
|
||||
它是一种让对象将某种行为向外部表达,但实际上将实现该行为的责任委托给关联对象的技术。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
使用委托模式以实现以下目的
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Dependency Injection
|
||||
folder: dependency-injection
|
||||
permalink: /patterns/dependency-injection/zh
|
||||
permalink: /patterns/dependency-injection/
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
@ -84,7 +84,7 @@ public class AdvancedWizard implements Wizard {
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Dirty Flag
|
||||
folder: dirty-flag
|
||||
permalink: /patterns/dirty-flag/zh
|
||||
permalink: /patterns/dirty-flag/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -17,7 +17,7 @@ tags:
|
||||
避免昂贵资源的重新获取。资源保留其身份,保留在某些快速访问的存储中,并被重新使用以避免再次获取它们。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
在以下情况下使用脏标志模式
|
||||
|
@ -4,6 +4,7 @@ title: Double Checked Locking
|
||||
folder: double-checked-locking
|
||||
permalink: /patterns/double-checked-locking/
|
||||
categories: Idiom
|
||||
language: zh
|
||||
tags:
|
||||
- Performance
|
||||
---
|
||||
@ -12,7 +13,7 @@ tags:
|
||||
通过先测试锁定标准("锁提示")而不实际获取锁的方式来减少获取锁的开销。只有当锁定标准检查表明需要锁定时,才进行实际的锁定逻辑。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
在以下场景适合使用双重锁检查模式:
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Facade
|
||||
folder: facade
|
||||
permalink: /patterns/facade/zh
|
||||
permalink: /patterns/facade/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
@ -189,7 +189,7 @@ facade.endDay();
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
使用外观模式当
|
||||
|
@ -4,6 +4,7 @@ title: Factory Kit
|
||||
folder: factory-kit
|
||||
permalink: /patterns/factory-kit/
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
- Extensibility
|
||||
---
|
||||
@ -12,7 +13,7 @@ tags:
|
||||
使用分离的构建器和工厂接口来定义一个不可变内容的工厂。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
工厂套件模式适用于与以下场景:
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Factory Method
|
||||
folder: factory-method
|
||||
permalink: /patterns/factory-method/zh
|
||||
permalink: /patterns/factory-method/
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
@ -63,7 +63,7 @@ blacksmith.manufactureWeapon(WeaponType.AXE);
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
使用工厂方法模式当
|
||||
|
@ -4,6 +4,7 @@ title: Factory
|
||||
folder: factory
|
||||
permalink: /patterns/factory/
|
||||
categories: Creational
|
||||
language: zh
|
||||
tags:
|
||||
- Gang of Four
|
||||
---
|
||||
@ -108,7 +109,7 @@ This is Ferrari.
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Interpreter
|
||||
folder: interpreter
|
||||
permalink: /patterns/interpreter/zh
|
||||
permalink: /patterns/interpreter/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -13,7 +13,7 @@ tags:
|
||||
给定一种语言,请定义其语法的表示形式,以及使用该表示形式来解释该语言中的句子的解释器。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
有一种要解释的语言时,请使用解释器模式,并且可以将语言中的语句表示为抽象语法树。解释器模式在以下情况下效果最佳
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Iterator
|
||||
folder: iterator
|
||||
permalink: /patterns/iterator/zh
|
||||
permalink: /patterns/iterator/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -115,7 +115,7 @@ while (itemIterator.hasNext()) {
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
以下情况使用迭代器模式
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Observer
|
||||
folder: observer
|
||||
permalink: /patterns/observer/zh
|
||||
permalink: /patterns/observer/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -129,7 +129,7 @@ public class Weather {
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||

|
||||
|
||||
## 应用
|
||||
在下面任何一种情况下都可以使用观察者模式
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Private Class Data
|
||||
folder: private-class-data
|
||||
permalink: /patterns/private-class-data/zh
|
||||
permalink: /patterns/private-class-data/
|
||||
categories: Idiom
|
||||
language: zh
|
||||
tags:
|
||||
@ -120,7 +120,7 @@ immutableStew.mix(); // Mixing the immutable stew we find: 2 potatoes, 4 carrot
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Producer Consumer
|
||||
folder: producer-consumer
|
||||
permalink: /patterns/producer-consumer/zh
|
||||
permalink: /patterns/producer-consumer/
|
||||
categories: Concurrency
|
||||
language: zh
|
||||
tags:
|
||||
@ -13,7 +13,7 @@ tags:
|
||||
生产者消费者设计模式是一种经典的并发模式,通过将工作与执行工作任务分开来减少生产者与消费者之间的耦合。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
在以下情况下使用生产者消费者
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Proxy
|
||||
folder: proxy
|
||||
permalink: /patterns/proxy/zh
|
||||
permalink: /patterns/proxy/
|
||||
categories: Structural
|
||||
language: zh
|
||||
tags:
|
||||
@ -123,7 +123,7 @@ Brown wizard is not allowed to enter!
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -4,6 +4,7 @@ title: Sharding
|
||||
folder: sharding
|
||||
permalink: /patterns/sharding/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
- Performance
|
||||
- Cloud distributed
|
||||
@ -15,7 +16,7 @@ tags:
|
||||
一个分片本身就是一个数据存储(它可以包含许多不同类型的实体的数据),运行在作为存储节点的服务器上。
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用场景
|
||||
这种设计模式提供了一下的好处:
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: State
|
||||
folder: state
|
||||
permalink: /patterns/state/zh
|
||||
permalink: /patterns/state/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -135,7 +135,7 @@ public class Mammoth {
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Strategy
|
||||
folder: strategy
|
||||
permalink: /patterns/strategy/zh
|
||||
permalink: /patterns/strategy/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -112,7 +112,7 @@ public class DragonSlayer {
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 应用
|
||||
使用策略模式当
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Template method
|
||||
folder: template-method
|
||||
permalink: /patterns/template-method/zh
|
||||
permalink: /patterns/template-method/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -119,7 +119,7 @@ public class HalflingThief {
|
||||
```
|
||||
|
||||
## 类图
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,13 +2,13 @@
|
||||
layout: pattern
|
||||
title: Version Number
|
||||
folder: versionnumber
|
||||
permalink: /patterns/versionnumber/zh
|
||||
permalink: /patterns/versionnumber/
|
||||
description: Entity versioning with version number
|
||||
language: zh
|
||||
|
||||
categories:
|
||||
- Concurrency
|
||||
|
||||
language: zh
|
||||
tags:
|
||||
- Data access
|
||||
- Microservices
|
||||
@ -135,7 +135,7 @@ Exception: Tried to update stale version 0 while actual version is 1
|
||||
|
||||
## 类图
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
layout: pattern
|
||||
title: Visitor
|
||||
folder: visitor
|
||||
permalink: /patterns/visitor/zh
|
||||
permalink: /patterns/visitor/
|
||||
categories: Behavioral
|
||||
language: zh
|
||||
tags:
|
||||
@ -201,7 +201,7 @@ Good to see you commander
|
||||
|
||||
## Class diagram
|
||||
|
||||

|
||||

|
||||
|
||||
## 适用性
|
||||
|
||||
|
1
pom.xml
1
pom.xml
@ -232,6 +232,7 @@
|
||||
<module>table-module</module>
|
||||
<module>presentation</module>
|
||||
<module>lockable-object</module>
|
||||
<module>fanout-fanin</module>
|
||||
<module>domain-model</module>
|
||||
</modules>
|
||||
|
||||
|
@ -36,16 +36,12 @@ public final class ThreadSafeDoubleCheckLocking {
|
||||
|
||||
private static volatile ThreadSafeDoubleCheckLocking instance;
|
||||
|
||||
private static boolean flag = true;
|
||||
|
||||
/**
|
||||
* private constructor to prevent client from instantiating.
|
||||
*/
|
||||
private ThreadSafeDoubleCheckLocking() {
|
||||
// to prevent instantiating by Reflection call
|
||||
if (flag) {
|
||||
flag = false;
|
||||
} else {
|
||||
if (instance != null) {
|
||||
throw new IllegalStateException("Already initialized.");
|
||||
}
|
||||
}
|
||||
|
@ -34,9 +34,7 @@ public final class ThreadSafeLazyLoadedIvoryTower {
|
||||
|
||||
private ThreadSafeLazyLoadedIvoryTower() {
|
||||
// Protect against instantiation via reflection
|
||||
if (instance == null) {
|
||||
instance = this;
|
||||
} else {
|
||||
if (instance != null) {
|
||||
throw new IllegalStateException("Already initialized.");
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user