Compare commits
110 Commits
new-branch
...
all-contri
Author | SHA1 | Date | |
---|---|---|---|
d6109548ae | |||
7968615ad4 | |||
c0d7c8922e | |||
5a23fab795 | |||
8c21809dad | |||
37bffb4a99 | |||
8182405796 | |||
39e5436ed5 | |||
8d6791490b | |||
eb8ddde98f | |||
0358fcec4c | |||
023865ad4c | |||
60ab9fa3ce | |||
f85e4db0be | |||
2b095bec28 | |||
cfba28f9a4 | |||
c0d2c7fdb0 | |||
cb9e2b1b60 | |||
574c410593 | |||
96344142e9 | |||
0bc3756250 | |||
d94199f5ff | |||
b805a7526e | |||
0cff538c27 | |||
452981669b | |||
2706c8fc37 | |||
03ebd5f353 | |||
5b269d5af1 | |||
0563ac7645 | |||
a5ff32c13e | |||
97adc13a1b | |||
d8f12529f2 | |||
4c766b9e71 | |||
c85d764e39 | |||
f360b64877 | |||
a77e9620b5 | |||
cf8e366e25 | |||
80605283f5 | |||
8ba111fe60 | |||
1cb9c2bcde | |||
d6edeee326 | |||
781a7c8b52 | |||
960adfc37a | |||
b3eb6ccea4 | |||
d609f3eec6 | |||
09dd0bee30 | |||
64eff5eb93 | |||
05dfd31fb7 | |||
2c8535e839 | |||
aea90ab115 | |||
211d7903ae | |||
02b6aba6ae | |||
76f634ff7a | |||
ae7a0b8a4a | |||
b7d122f614 | |||
93c11fdf23 | |||
ef4de30310 | |||
b62bed7e43 | |||
54c0b1725c | |||
4b88214bae | |||
eee409f284 | |||
2fdd7a11e9 | |||
f37d697a60 | |||
3e1a83e29d | |||
0a35cdfbe4 | |||
1eafb46b61 | |||
2ee5789c77 | |||
d9ed8a52b5 | |||
205b87cd93 | |||
689486267d | |||
9deb587c52 | |||
645fb20730 | |||
1886a6f969 | |||
9db997d0ae | |||
8982392fea | |||
082d63a1b3 | |||
6c21143303 | |||
172964e75c | |||
ab4e53a468 | |||
b907a2a9bc | |||
467f647ca2 | |||
a18c0f76ea | |||
e34de39ae7 | |||
4d95d38b8d | |||
d2724e8091 | |||
9a81ddb7d8 | |||
61dfa50822 | |||
09cee8ffa7 | |||
5aacdecc6c | |||
eeea3c7b1f | |||
76fb9aff8b | |||
68fa6f451b | |||
e98ad671e9 | |||
4264f52d49 | |||
ee654cf7b2 | |||
51f6282e9b | |||
2212690468 | |||
4beb53b8b8 | |||
0b1a98137a | |||
bd1dbec19f | |||
a3564a8847 | |||
9e30383eb6 | |||
6fe219d644 | |||
5192beb5dd | |||
f4f9c1a441 | |||
a396d972f6 | |||
30678792fd | |||
cfa2b35bff | |||
5db8037b8b | |||
71affacee2 |
445
.all-contributorsrc
Normal file
445
.all-contributorsrc
Normal file
@ -0,0 +1,445 @@
|
||||
{
|
||||
"files": [
|
||||
"README.md"
|
||||
],
|
||||
"imageSize": 100,
|
||||
"commit": false,
|
||||
"contributors": [
|
||||
{
|
||||
"login": "iluwatar",
|
||||
"name": "Ilkka Seppälä",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/582346?v=4",
|
||||
"profile": "https://github.com/iluwatar",
|
||||
"contributions": [
|
||||
"projectManagement",
|
||||
"maintenance",
|
||||
"content"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "amit1307",
|
||||
"name": "amit1307",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/23420222?v=4",
|
||||
"profile": "https://github.com/amit1307",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "npathai",
|
||||
"name": "Narendra Pathai",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/1792515?v=4",
|
||||
"profile": "https://github.com/npathai",
|
||||
"contributions": [
|
||||
"code",
|
||||
"ideas",
|
||||
"review"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fluxw42",
|
||||
"name": "Jeroen Meulemeester",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1545460?v=4",
|
||||
"profile": "https://github.com/fluxw42",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mikulucky",
|
||||
"name": "Joseph McCarthy",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/4526195?v=4",
|
||||
"profile": "http://www.joemccarthy.co.uk",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "thomasoss",
|
||||
"name": "Thomas",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/22516154?v=4",
|
||||
"profile": "https://github.com/thomasoss",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "anuragagarwal561994",
|
||||
"name": "Anurag Agarwal",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/6075379?v=4",
|
||||
"profile": "https://github.com/anuragagarwal561994",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "markusmo3",
|
||||
"name": "Markus Moser",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/3317416?v=4",
|
||||
"profile": "https://markusmo3.github.io",
|
||||
"contributions": [
|
||||
"design",
|
||||
"code",
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "isabiq",
|
||||
"name": "Sabiq Ihab",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/19510920?v=4",
|
||||
"profile": "https://twitter.com/i_sabiq",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "inbravo",
|
||||
"name": "Amit Dixit",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/5253764?v=4",
|
||||
"profile": "http://inbravo.github.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "piyushchaudhari04",
|
||||
"name": "Piyush Kailash Chaudhari",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/10268029?v=4",
|
||||
"profile": "https://github.com/piyushchaudhari04",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "joshzambales",
|
||||
"name": "joshzambales",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/8704552?v=4",
|
||||
"profile": "https://github.com/joshzambales",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Crossy147",
|
||||
"name": "Kamil Pietruszka",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/7272996?v=4",
|
||||
"profile": "https://github.com/Crossy147",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "zafarella",
|
||||
"name": "Zafar Khaydarov",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/660742?v=4",
|
||||
"profile": "http://cs.joensuu.fi/~zkhayda",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kemitix",
|
||||
"name": "Paul Campbell",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1147749?v=4",
|
||||
"profile": "https://kemitix.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Argyro-Sioziou",
|
||||
"name": "Argyro Sioziou",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/22822639?v=4",
|
||||
"profile": "https://github.com/Argyro-Sioziou",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "TylerMcConville",
|
||||
"name": "TylerMcConville",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/4946449?v=4",
|
||||
"profile": "https://github.com/TylerMcConville",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "saksham93",
|
||||
"name": "saksham93",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/37399540?v=4",
|
||||
"profile": "https://github.com/saksham93",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nikhilbarar",
|
||||
"name": "nikhilbarar",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/37332144?v=4",
|
||||
"profile": "https://github.com/nikhilbarar",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "colinbut",
|
||||
"name": "Colin But",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/10725674?v=4",
|
||||
"profile": "http://colinbut.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ruslanpa",
|
||||
"name": "Ruslan",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/1503411?v=4",
|
||||
"profile": "https://github.com/ruslanpa",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JuhoKang",
|
||||
"name": "Juho Kang",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/4745294?v=4",
|
||||
"profile": "https://github.com/JuhoKang",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dheeraj-mummareddy",
|
||||
"name": "Dheeraj Mummareddy",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/7002230?v=4",
|
||||
"profile": "https://github.com/dheeraj-mummareddy",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bernardosulzbach",
|
||||
"name": "Bernardo Sulzbach",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/8271090?v=4",
|
||||
"profile": "https://www.bernardosulzbach.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "4lexis",
|
||||
"name": "Aleksandar Dudukovic",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/19871727?v=4",
|
||||
"profile": "https://github.com/4lexis",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "yusufaytas",
|
||||
"name": "Yusuf Aytaş",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/1049483?v=4",
|
||||
"profile": "https://www.yusufaytas.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "qpi",
|
||||
"name": "Mihály Kuprivecz",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/1001491?v=4",
|
||||
"profile": "http://futurehomes.hu",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kapinuss",
|
||||
"name": "Stanislav Kapinus",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/17639945?v=4",
|
||||
"profile": "https://github.com/kapinuss",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gvsharma",
|
||||
"name": "GVSharma",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/6648152?v=4",
|
||||
"profile": "https://github.com/gvsharma",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SrdjanPaunovic",
|
||||
"name": "Srđan Paunović",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/22815104?v=4",
|
||||
"profile": "https://github.com/SrdjanPaunovic",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sideris",
|
||||
"name": "Petros G. Sideris",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/5484694?v=4",
|
||||
"profile": "https://sideris.xyz/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "llorllale",
|
||||
"name": "George Aristy",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2019896?v=4",
|
||||
"profile": "https://llorllale.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mookkiah",
|
||||
"name": "Mahendran Mookkiah",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/8975264?v=4",
|
||||
"profile": "https://github.com/mookkiah",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Azureyjt",
|
||||
"name": "Azureyjt",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/18476317?v=4",
|
||||
"profile": "https://github.com/Azureyjt",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vehpsr",
|
||||
"name": "gans",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/3133265?v=4",
|
||||
"profile": "https://github.com/vehpsr",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ThatGuyWithTheHat",
|
||||
"name": "Matt",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/24470582?v=4",
|
||||
"profile": "https://github.com/ThatGuyWithTheHat",
|
||||
"contributions": [
|
||||
"content"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gopinath-langote",
|
||||
"name": "Gopinath Langote",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/10210778?v=4",
|
||||
"profile": "https://www.linkedin.com/in/gopinathlangote/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hoswey",
|
||||
"name": "Hoswey",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/3689445?v=4",
|
||||
"profile": "https://github.com/hoswey",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "amit2103",
|
||||
"name": "Amit Pandey",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/7566692?v=4",
|
||||
"profile": "https://github.com/amit2103",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gwildor28",
|
||||
"name": "gwildor28",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/16000365?v=4",
|
||||
"profile": "https://github.com/gwildor28",
|
||||
"contributions": [
|
||||
"content"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "llitfkitfk",
|
||||
"name": "田浩",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2404785?v=4",
|
||||
"profile": "https://t.me/paul_docker",
|
||||
"contributions": [
|
||||
"content"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pitsios-s",
|
||||
"name": "Stamatis Pitsios",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/6773603?v=4",
|
||||
"profile": "https://twitter.com/StPitsios",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "qza",
|
||||
"name": "qza",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/233149?v=4",
|
||||
"profile": "https://github.com/qza",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Tschis",
|
||||
"name": "Rodolfo Forte",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/20662669?v=4",
|
||||
"profile": "http://tschis.github.io",
|
||||
"contributions": [
|
||||
"content"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ankurkaushal",
|
||||
"name": "Ankur Kaushal",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/2236616?v=4",
|
||||
"profile": "https://github.com/ankurkaushal",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "okinskas",
|
||||
"name": "Ovidijus Okinskas",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/20372387?v=4",
|
||||
"profile": "https://www.linkedin.com/in/ovidijus-okinskas/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "robertt240",
|
||||
"name": "Robert Kasperczyk",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/9137432?v=4",
|
||||
"profile": "https://github.com/robertt240",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 4,
|
||||
"projectName": "java-design-patterns",
|
||||
"projectOwner": "iluwatar",
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com",
|
||||
"skipCi": true
|
||||
}
|
34
.github/workflows/maven.yml
vendored
34
.github/workflows/maven.yml
vendored
@ -1,3 +1,26 @@
|
||||
#
|
||||
# The MIT License
|
||||
# Copyright © 2014-2019 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.
|
||||
#
|
||||
|
||||
# This workflow will build a Java project with Maven
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
@ -20,7 +43,18 @@ jobs:
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
# Some tests need screen access
|
||||
- name: Install xvfb
|
||||
run: sudo apt-get install xvfb
|
||||
# SonarQube scan does not work for forked repositories
|
||||
# See https://jira.sonarsource.com/browse/MMF-1371
|
||||
- name: Build with Maven
|
||||
if: github.ref != 'refs/heads/master'
|
||||
run: xvfb-run mvn clean verify
|
||||
- name: Build with Maven and run SonarQube analysis
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: xvfb-run mvn clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
||||
env:
|
||||
# These two env variables are needed for sonar analysis
|
||||
GITHUB_TOKEN: ${{ secrets.REPOSITORY_ACCESS_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
|
97
README.md
97
README.md
@ -8,6 +8,9 @@
|
||||
[](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md)
|
||||
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
# Introduction
|
||||
|
||||
@ -31,19 +34,20 @@ programming tutorials how to implement a specific pattern. We use the most
|
||||
popular battle-proven open source Java technologies.
|
||||
|
||||
Before you dive into the material, you should be familiar with various
|
||||
software design principles.
|
||||
[Software Design Principles](https://java-design-patterns.com/principles/).
|
||||
|
||||
All designs should be as simple as possible. You should start with KISS, YAGNI,
|
||||
and Do The Simplest Thing That Could Possibly Work principles. Complexity and
|
||||
patterns should only be introduced when they are needed for practical
|
||||
extensibility.
|
||||
|
||||
Once you are familiar with these concepts you can start drilling down into
|
||||
patterns by any of the following approaches
|
||||
Once you are familiar with these concepts you can start drilling down into the
|
||||
[available design patterns](https://java-design-patterns.com/patterns/) by any
|
||||
of the following approaches
|
||||
|
||||
- Using difficulty tags, `Difficulty-Beginner`, `Difficulty-Intermediate` & `Difficulty-Expert`.
|
||||
- Search for a specific pattern by name. Can't find one? Please report a new pattern [here](https://github.com/iluwatar/java-design-patterns/issues).
|
||||
- Using tags such as `Performance`, `Gang of Four` or `Data access`.
|
||||
- Using pattern categories, `Creational`, `Behavioral`, and others.
|
||||
- Search for a specific pattern. Can't find one? Please report a new pattern [here](https://github.com/iluwatar/java-design-patterns/issues).
|
||||
|
||||
Hopefully you find the object oriented solutions presented on this site useful
|
||||
in your architectures and have as much fun learning them as we had developing them.
|
||||
@ -57,3 +61,86 @@ you and answer your questions in the [Gitter chatroom](https://gitter.im/iluwata
|
||||
# License
|
||||
|
||||
This project is licensed under the terms of the MIT license.
|
||||
|
||||
# Contributors
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/iluwatar"><img src="https://avatars1.githubusercontent.com/u/582346?v=4" width="100px;" alt=""/><br /><sub><b>Ilkka Seppälä</b></sub></a><br /><a href="#projectManagement-iluwatar" title="Project Management">📆</a> <a href="#maintenance-iluwatar" title="Maintenance">🚧</a> <a href="#content-iluwatar" title="Content">🖋</a></td>
|
||||
<td align="center"><a href="https://github.com/amit1307"><img src="https://avatars0.githubusercontent.com/u/23420222?v=4" width="100px;" alt=""/><br /><sub><b>amit1307</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amit1307" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/npathai"><img src="https://avatars2.githubusercontent.com/u/1792515?v=4" width="100px;" alt=""/><br /><sub><b>Narendra Pathai</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=npathai" title="Code">💻</a> <a href="#ideas-npathai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Anpathai" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://github.com/fluxw42"><img src="https://avatars1.githubusercontent.com/u/1545460?v=4" width="100px;" alt=""/><br /><sub><b>Jeroen Meulemeester</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fluxw42" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://www.joemccarthy.co.uk"><img src="https://avatars0.githubusercontent.com/u/4526195?v=4" width="100px;" alt=""/><br /><sub><b>Joseph McCarthy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mikulucky" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/thomasoss"><img src="https://avatars1.githubusercontent.com/u/22516154?v=4" width="100px;" alt=""/><br /><sub><b>Thomas</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=thomasoss" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/anuragagarwal561994"><img src="https://avatars1.githubusercontent.com/u/6075379?v=4" width="100px;" alt=""/><br /><sub><b>Anurag Agarwal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=anuragagarwal561994" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://markusmo3.github.io"><img src="https://avatars1.githubusercontent.com/u/3317416?v=4" width="100px;" alt=""/><br /><sub><b>Markus Moser</b></sub></a><br /><a href="#design-markusmo3" title="Design">🎨</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=markusmo3" title="Code">💻</a> <a href="#ideas-markusmo3" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://twitter.com/i_sabiq"><img src="https://avatars1.githubusercontent.com/u/19510920?v=4" width="100px;" alt=""/><br /><sub><b>Sabiq Ihab</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=isabiq" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://inbravo.github.io"><img src="https://avatars3.githubusercontent.com/u/5253764?v=4" width="100px;" alt=""/><br /><sub><b>Amit Dixit</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=inbravo" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/piyushchaudhari04"><img src="https://avatars3.githubusercontent.com/u/10268029?v=4" width="100px;" alt=""/><br /><sub><b>Piyush Kailash Chaudhari</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=piyushchaudhari04" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/joshzambales"><img src="https://avatars1.githubusercontent.com/u/8704552?v=4" width="100px;" alt=""/><br /><sub><b>joshzambales</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=joshzambales" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/Crossy147"><img src="https://avatars2.githubusercontent.com/u/7272996?v=4" width="100px;" alt=""/><br /><sub><b>Kamil Pietruszka</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Crossy147" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://cs.joensuu.fi/~zkhayda"><img src="https://avatars2.githubusercontent.com/u/660742?v=4" width="100px;" alt=""/><br /><sub><b>Zafar Khaydarov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=zafarella" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=zafarella" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://kemitix.github.io/"><img src="https://avatars1.githubusercontent.com/u/1147749?v=4" width="100px;" alt=""/><br /><sub><b>Paul Campbell</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kemitix" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/Argyro-Sioziou"><img src="https://avatars0.githubusercontent.com/u/22822639?v=4" width="100px;" alt=""/><br /><sub><b>Argyro Sioziou</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Argyro-Sioziou" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/TylerMcConville"><img src="https://avatars0.githubusercontent.com/u/4946449?v=4" width="100px;" alt=""/><br /><sub><b>TylerMcConville</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=TylerMcConville" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/saksham93"><img src="https://avatars1.githubusercontent.com/u/37399540?v=4" width="100px;" alt=""/><br /><sub><b>saksham93</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=saksham93" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/nikhilbarar"><img src="https://avatars2.githubusercontent.com/u/37332144?v=4" width="100px;" alt=""/><br /><sub><b>nikhilbarar</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nikhilbarar" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://colinbut.com"><img src="https://avatars2.githubusercontent.com/u/10725674?v=4" width="100px;" alt=""/><br /><sub><b>Colin But</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=colinbut" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/ruslanpa"><img src="https://avatars2.githubusercontent.com/u/1503411?v=4" width="100px;" alt=""/><br /><sub><b>Ruslan</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ruslanpa" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/JuhoKang"><img src="https://avatars1.githubusercontent.com/u/4745294?v=4" width="100px;" alt=""/><br /><sub><b>Juho Kang</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=JuhoKang" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/dheeraj-mummareddy"><img src="https://avatars2.githubusercontent.com/u/7002230?v=4" width="100px;" alt=""/><br /><sub><b>Dheeraj Mummareddy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dheeraj-mummareddy" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.bernardosulzbach.com"><img src="https://avatars0.githubusercontent.com/u/8271090?v=4" width="100px;" alt=""/><br /><sub><b>Bernardo Sulzbach</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=bernardosulzbach" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/4lexis"><img src="https://avatars0.githubusercontent.com/u/19871727?v=4" width="100px;" alt=""/><br /><sub><b>Aleksandar Dudukovic</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=4lexis" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.yusufaytas.com"><img src="https://avatars2.githubusercontent.com/u/1049483?v=4" width="100px;" alt=""/><br /><sub><b>Yusuf Aytaş</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yusufaytas" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://futurehomes.hu"><img src="https://avatars2.githubusercontent.com/u/1001491?v=4" width="100px;" alt=""/><br /><sub><b>Mihály Kuprivecz</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qpi" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/kapinuss"><img src="https://avatars0.githubusercontent.com/u/17639945?v=4" width="100px;" alt=""/><br /><sub><b>Stanislav Kapinus</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kapinuss" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/gvsharma"><img src="https://avatars1.githubusercontent.com/u/6648152?v=4" width="100px;" alt=""/><br /><sub><b>GVSharma</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gvsharma" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/SrdjanPaunovic"><img src="https://avatars1.githubusercontent.com/u/22815104?v=4" width="100px;" alt=""/><br /><sub><b>Srđan Paunović</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=SrdjanPaunovic" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://sideris.xyz/"><img src="https://avatars3.githubusercontent.com/u/5484694?v=4" width="100px;" alt=""/><br /><sub><b>Petros G. Sideris</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=sideris" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://llorllale.github.io/"><img src="https://avatars1.githubusercontent.com/u/2019896?v=4" width="100px;" alt=""/><br /><sub><b>George Aristy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=llorllale" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/qza"><img src="https://avatars3.githubusercontent.com/u/233149?v=4" width="100px;" alt=""/><br /><sub><b>qza</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qza" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://tschis.github.io"><img src="https://avatars1.githubusercontent.com/u/20662669?v=4" width="100px;" alt=""/><br /><sub><b>Rodolfo Forte</b></sub></a><br /><a href="#content-Tschis" title="Content">🖋</a></td>
|
||||
<td align="center"><a href="https://github.com/ankurkaushal"><img src="https://avatars2.githubusercontent.com/u/2236616?v=4" width="100px;" alt=""/><br /><sub><b>Ankur Kaushal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ankurkaushal" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.linkedin.com/in/ovidijus-okinskas/"><img src="https://avatars0.githubusercontent.com/u/20372387?v=4" width="100px;" alt=""/><br /><sub><b>Ovidijus Okinskas</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=okinskas" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://twitter.com/StPitsios"><img src="https://avatars1.githubusercontent.com/u/6773603?v=4" width="100px;" alt=""/><br /><sub><b>Stamatis Pitsios</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=pitsios-s" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/robertt240"><img src="https://avatars1.githubusercontent.com/u/9137432?v=4" width="100px;" alt=""/><br /><sub><b>Robert Kasperczyk</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=robertt240" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://t.me/paul_docker"><img src="https://avatars1.githubusercontent.com/u/2404785?v=4" width="100px;" alt=""/><br /><sub><b>田浩</b></sub></a><br /><a href="#content-llitfkitfk" title="Content">🖋</a></td>
|
||||
<td align="center"><a href="https://github.com/gwildor28"><img src="https://avatars0.githubusercontent.com/u/16000365?v=4" width="100px;" alt=""/><br /><sub><b>gwildor28</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gwildor28" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/amit2103"><img src="https://avatars3.githubusercontent.com/u/7566692?v=4" width="100px;" alt=""/><br /><sub><b>Amit Pandey</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amit2103" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/hoswey"><img src="https://avatars3.githubusercontent.com/u/3689445?v=4" width="100px;" alt=""/><br /><sub><b>Hoswey</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=hoswey" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.linkedin.com/in/gopinathlangote/"><img src="https://avatars2.githubusercontent.com/u/10210778?v=4" width="100px;" alt=""/><br /><sub><b>Gopinath Langote</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gopinath-langote" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/ThatGuyWithTheHat"><img src="https://avatars0.githubusercontent.com/u/24470582?v=4" width="100px;" alt=""/><br /><sub><b>Matt</b></sub></a><br /><a href="#content-ThatGuyWithTheHat" title="Content">🖋</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/vehpsr"><img src="https://avatars2.githubusercontent.com/u/3133265?v=4" width="100px;" alt=""/><br /><sub><b>gans</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vehpsr" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/Azureyjt"><img src="https://avatars2.githubusercontent.com/u/18476317?v=4" width="100px;" alt=""/><br /><sub><b>Azureyjt</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Azureyjt" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/mookkiah"><img src="https://avatars1.githubusercontent.com/u/8975264?v=4" width="100px;" alt=""/><br /><sub><b>Mahendran Mookkiah</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mookkiah" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
@ -198,4 +198,5 @@ Use the Abstract Factory pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
@ -129,5 +129,7 @@ An object adapter
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [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)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
@ -5,7 +5,8 @@ folder: ambassador
|
||||
permalink: /patterns/ambassador/
|
||||
categories: Structural
|
||||
tags:
|
||||
- Decoupling
|
||||
- Decoupling
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Intent
|
||||
@ -22,8 +23,7 @@ In plain words
|
||||
|
||||
Microsoft documentation states
|
||||
|
||||
> An ambassador service can be thought of as an out-of-process proxy that is co-located with the client.
|
||||
This pattern can be useful for offloading common client connectivity tasks such as monitoring, logging, routing, security (such as TLS), and resiliency patterns in a language agnostic way. It is often used with legacy applications, or other applications that are difficult to modify, in order to extend their networking capabilities. It can also enable a specialized team to implement those features.
|
||||
> An ambassador service can be thought of as an out-of-process proxy that is co-located with the client. This pattern can be useful for offloading common client connectivity tasks such as monitoring, logging, routing, security (such as TLS), and resiliency patterns in a language agnostic way. It is often used with legacy applications, or other applications that are difficult to modify, in order to extend their networking capabilities. It can also enable a specialized team to implement those features.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
|
@ -5,14 +5,142 @@ folder: api-gateway
|
||||
permalink: /patterns/api-gateway/
|
||||
categories: Architectural
|
||||
tags:
|
||||
- Cloud distributed
|
||||
- Decoupling
|
||||
- Cloud distributed
|
||||
- Decoupling
|
||||
- Microservices
|
||||
---
|
||||
|
||||
## Intent
|
||||
|
||||
Aggregate calls to microservices in a single location: the API Gateway. The user makes a single
|
||||
call to the API Gateway, and the API Gateway then calls each relevant microservice.
|
||||
Aggregate calls to microservices in a single location: the API Gateway. The user makes a single call to the API Gateway,
|
||||
and the API Gateway then calls each relevant microservice.
|
||||
|
||||
## Explanation
|
||||
|
||||
With the Microservices pattern, a client may need data from multiple different microservices. If the client called each
|
||||
microservice directly, that could contribute to longer load times, since the client would have to make a network request
|
||||
for each microservice called. Moreover, having the client call each microservice directly ties the client to that
|
||||
microservice - if the internal implementations of the microservices change (for example, if two microservices are
|
||||
combined sometime in the future) or if the location (host and port) of a microservice changes, then every client that
|
||||
makes use of those microservices must be updated.
|
||||
|
||||
The intent of the API Gateway pattern is to alleviate some of these issues. In the API Gateway pattern, an additional
|
||||
entity (the API Gateway) is placed between the client and the microservices. The job of the API Gateway is to aggregate
|
||||
the calls to the microservices. Rather than the client calling each microservice individually, the client calls the
|
||||
API Gateway a single time. The API Gateway then calls each of the microservices that the client needs.
|
||||
|
||||
Real world example
|
||||
|
||||
> We are implementing microservices and API Gateway pattern for an e-commerce site. In this system the API Gateway makes
|
||||
calls to the Image and Price microservices.
|
||||
|
||||
In plain words
|
||||
|
||||
> For a system implemented using microservices architecture, API Gateway is the single entry point that aggregates the
|
||||
calls to the individual microservices.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> API Gateway is a server that acts as an API front-end, receives API requests, enforces throttling and security
|
||||
policies, passes requests to the back-end service and then passes the response back to the requester. A gateway often
|
||||
includes a transformation engine to orchestrate and modify the requests and responses on the fly. A gateway can also
|
||||
provide functionality such as collecting analytics data and providing caching. The gateway can provide functionality to
|
||||
support authentication, authorization, security, audit and regulatory compliance.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
This implementation shows what the API Gateway pattern could look like for an e-commerce site. The `ApiGateway` makes
|
||||
calls to the Image and Price microservices using the `ImageClientImpl` and `PriceClientImpl` respectively. Customers
|
||||
viewing the site on a desktop device can see both price information and an image of a product, so the `ApiGateway` calls
|
||||
both of the microservices and aggregates the data in the `DesktopProduct` model. However, mobile users only see price
|
||||
information; they do not see a product image. For mobile users, the `ApiGateway` only retrieves price information, which
|
||||
it uses to populate the `MobileProduct`.
|
||||
|
||||
Here's the Image microservice implementation.
|
||||
|
||||
```java
|
||||
public interface ImageClient {
|
||||
String getImagePath();
|
||||
}
|
||||
|
||||
public class ImageClientImpl implements ImageClient {
|
||||
|
||||
@Override
|
||||
public String getImagePath() {
|
||||
var httpClient = HttpClient.newHttpClient();
|
||||
var httpGet = HttpRequest.newBuilder()
|
||||
.GET()
|
||||
.uri(URI.create("http://localhost:50005/image-path"))
|
||||
.build();
|
||||
|
||||
try {
|
||||
var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
|
||||
return httpResponse.body();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the Price microservice implementation.
|
||||
|
||||
```java
|
||||
public interface PriceClient {
|
||||
String getPrice();
|
||||
}
|
||||
|
||||
public class PriceClientImpl implements PriceClient {
|
||||
|
||||
@Override
|
||||
public String getPrice() {
|
||||
var httpClient = HttpClient.newHttpClient();
|
||||
var httpGet = HttpRequest.newBuilder()
|
||||
.GET()
|
||||
.uri(URI.create("http://localhost:50006/price"))
|
||||
.build();
|
||||
|
||||
try {
|
||||
var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
|
||||
return httpResponse.body();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And here we can see how API Gateway maps the requests to the microservices.
|
||||
|
||||
```java
|
||||
public class ApiGateway {
|
||||
|
||||
@Resource
|
||||
private ImageClient imageClient;
|
||||
|
||||
@Resource
|
||||
private PriceClient priceClient;
|
||||
|
||||
@RequestMapping(path = "/desktop", method = RequestMethod.GET)
|
||||
public DesktopProduct getProductDesktop() {
|
||||
var desktopProduct = new DesktopProduct();
|
||||
desktopProduct.setImagePath(imageClient.getImagePath());
|
||||
desktopProduct.setPrice(priceClient.getPrice());
|
||||
return desktopProduct;
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/mobile", method = RequestMethod.GET)
|
||||
public MobileProduct getProductMobile() {
|
||||
var mobileProduct = new MobileProduct();
|
||||
mobileProduct.setPrice(priceClient.getPrice());
|
||||
return mobileProduct;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -21,10 +149,11 @@ call to the API Gateway, and the API Gateway then calls each relevant microservi
|
||||
|
||||
Use the API Gateway pattern when
|
||||
|
||||
* you're also using the Microservices pattern and need a single point of aggregation for your
|
||||
microservice calls
|
||||
* You're using microservices architecture and need a single point of aggregation for your microservice calls.
|
||||
|
||||
## Credits
|
||||
|
||||
* [microservices.io - API Gateway](http://microservices.io/patterns/apigateway.html)
|
||||
* [NGINX - Building Microservices: Using an API Gateway](https://www.nginx.com/blog/building-microservices-using-an-api-gateway/)
|
||||
* [Microservices Patterns: With examples in Java](https://www.amazon.com/gp/product/1617294543/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617294543&linkId=ac7b6a57f866ac006a309d9086e8cfbd)
|
||||
* [Building Microservices: Designing Fine-Grained Systems](https://www.amazon.com/gp/product/1491950358/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1491950358&linkId=4c95ca9831e05e3f0dadb08841d77bf1)
|
||||
|
@ -194,4 +194,5 @@ Use the Bridge pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
@ -131,5 +131,7 @@ Use the Builder pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
@ -26,4 +26,4 @@ Use the Business Delegate pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||
* [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)
|
||||
|
@ -5,7 +5,8 @@ folder: caching
|
||||
permalink: /patterns/caching/
|
||||
categories: Behavioral
|
||||
tags:
|
||||
- Performance
|
||||
- Performance
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Intent
|
||||
@ -25,4 +26,4 @@ Use the Caching pattern(s) when
|
||||
|
||||
* [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](https://msdn.microsoft.com/en-us/library/dn589799.aspx)
|
||||
* [Cache-Aside pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside)
|
||||
|
@ -9,9 +9,64 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Callback is a piece of executable code that is passed as an
|
||||
argument to other code, which is expected to call back (execute) the argument
|
||||
at some convenient time.
|
||||
Callback is a piece of executable code that is passed as an argument to other code, which is expected to call back
|
||||
(execute) the argument at some convenient time.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> We need to be notified after executing task has finished. We pass a callback method for the executor and wait for it to call back on us.
|
||||
|
||||
In plain words
|
||||
|
||||
> Callback is a method passed to the executor which will be called at defined moment.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In computer programming, a callback, also known as a "call-after" function, is any executable code that is passed as an argument to other code; that other code is expected to call back (execute) the argument at a given time.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Callback is a simple interface with single method.
|
||||
|
||||
```java
|
||||
public interface Callback {
|
||||
|
||||
void call();
|
||||
}
|
||||
```
|
||||
|
||||
Next we define a task that will execute the callback after the task execution has finished.
|
||||
|
||||
```java
|
||||
public abstract class Task {
|
||||
|
||||
final void executeWith(Callback callback) {
|
||||
execute();
|
||||
Optional.ofNullable(callback).ifPresent(Callback::call);
|
||||
}
|
||||
|
||||
public abstract void execute();
|
||||
}
|
||||
|
||||
public final class SimpleTask extends Task {
|
||||
|
||||
private static final Logger LOGGER = getLogger(SimpleTask.class);
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
LOGGER.info("Perform some important activity and after call the callback method.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Finally here's how we execute a task and receive a callback when it's finished.
|
||||
|
||||
```java
|
||||
var task = new SimpleTask();
|
||||
task.executeWith(() -> LOGGER.info("I'm done now."));
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
@ -33,7 +33,7 @@ public abstract class Task {
|
||||
/**
|
||||
* Execute with callback.
|
||||
*/
|
||||
final void executeWith(final Callback callback) {
|
||||
final void executeWith(Callback callback) {
|
||||
execute();
|
||||
Optional.ofNullable(callback).ifPresent(Callback::call);
|
||||
}
|
||||
|
@ -157,4 +157,5 @@ Use Chain of Responsibility when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
@ -5,8 +5,9 @@ folder: circuit-breaker
|
||||
permalink: /patterns/circuit-breaker/
|
||||
categories: Behavioral
|
||||
tags:
|
||||
- Performance
|
||||
- Decoupling
|
||||
- Performance
|
||||
- Decoupling
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Intent
|
||||
@ -187,4 +188,4 @@ Use the Circuit Breaker pattern when
|
||||
* [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)
|
||||
* [Microsoft docs](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)
|
||||
|
@ -12,9 +12,214 @@ tags:
|
||||
Action, Transaction
|
||||
|
||||
## Intent
|
||||
Encapsulate a request as an object, thereby letting you
|
||||
parameterize clients with different requests, queue or log requests, and
|
||||
support undoable operations.
|
||||
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
|
||||
|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> There is a wizard casting spells on a goblin. The spells are executed on the goblin one by one. The first spell shrinks the goblin and the second makes him invisible. Then the wizard reverses the spells one by one. Each spell here is a command object that can be undone.
|
||||
|
||||
In plain words
|
||||
|
||||
> Storing requests as command objects allows performing an action or undoing it at a later time.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Here's the sample code with wizard and goblin. Let's start from the wizard class.
|
||||
|
||||
```java
|
||||
public class Wizard {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
|
||||
|
||||
private Deque<Command> undoStack = new LinkedList<>();
|
||||
private Deque<Command> redoStack = new LinkedList<>();
|
||||
|
||||
public Wizard() {}
|
||||
|
||||
public void castSpell(Command command, Target target) {
|
||||
LOGGER.info("{} casts {} at {}", this, command, target);
|
||||
command.execute(target);
|
||||
undoStack.offerLast(command);
|
||||
}
|
||||
|
||||
public void undoLastSpell() {
|
||||
if (!undoStack.isEmpty()) {
|
||||
var previousSpell = undoStack.pollLast();
|
||||
redoStack.offerLast(previousSpell);
|
||||
LOGGER.info("{} undoes {}", this, previousSpell);
|
||||
previousSpell.undo();
|
||||
}
|
||||
}
|
||||
|
||||
public void redoLastSpell() {
|
||||
if (!redoStack.isEmpty()) {
|
||||
var previousSpell = redoStack.pollLast();
|
||||
undoStack.offerLast(previousSpell);
|
||||
LOGGER.info("{} redoes {}", this, previousSpell);
|
||||
previousSpell.redo();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Wizard";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next we present the spell hierarchy.
|
||||
|
||||
```java
|
||||
public abstract class Command {
|
||||
|
||||
public abstract void execute(Target target);
|
||||
|
||||
public abstract void undo();
|
||||
|
||||
public abstract void redo();
|
||||
|
||||
@Override
|
||||
public abstract String toString();
|
||||
}
|
||||
|
||||
public class InvisibilitySpell extends Command {
|
||||
|
||||
private Target target;
|
||||
|
||||
@Override
|
||||
public void execute(Target target) {
|
||||
target.setVisibility(Visibility.INVISIBLE);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
if (target != null) {
|
||||
target.setVisibility(Visibility.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
if (target != null) {
|
||||
target.setVisibility(Visibility.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Invisibility spell";
|
||||
}
|
||||
}
|
||||
|
||||
public class ShrinkSpell extends Command {
|
||||
|
||||
private Size oldSize;
|
||||
private Target target;
|
||||
|
||||
@Override
|
||||
public void execute(Target target) {
|
||||
oldSize = target.getSize();
|
||||
target.setSize(Size.SMALL);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
if (oldSize != null && target != null) {
|
||||
var temp = target.getSize();
|
||||
target.setSize(oldSize);
|
||||
oldSize = temp;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
undo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Shrink spell";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And last we have the goblin who's the target of the spells.
|
||||
|
||||
```java
|
||||
public abstract class Target {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Target.class);
|
||||
|
||||
private Size size;
|
||||
|
||||
private Visibility visibility;
|
||||
|
||||
public Size getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Size size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public Visibility getVisibility() {
|
||||
return visibility;
|
||||
}
|
||||
|
||||
public void setVisibility(Visibility visibility) {
|
||||
this.visibility = visibility;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract String toString();
|
||||
|
||||
public void printStatus() {
|
||||
LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
|
||||
}
|
||||
}
|
||||
|
||||
public class Goblin extends Target {
|
||||
|
||||
public Goblin() {
|
||||
setSize(Size.NORMAL);
|
||||
setVisibility(Visibility.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Goblin";
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Finally here's the whole example in action.
|
||||
|
||||
```java
|
||||
var wizard = new Wizard();
|
||||
var goblin = new Goblin();
|
||||
goblin.printStatus();
|
||||
// Goblin, [size=normal] [visibility=visible]
|
||||
wizard.castSpell(new ShrinkSpell(), goblin);
|
||||
// Wizard casts Shrink spell at Goblin
|
||||
goblin.printStatus();
|
||||
// Goblin, [size=small] [visibility=visible]
|
||||
wizard.castSpell(new InvisibilitySpell(), goblin);
|
||||
// Wizard casts Invisibility spell at Goblin
|
||||
goblin.printStatus();
|
||||
// Goblin, [size=small] [visibility=invisible]
|
||||
wizard.undoLastSpell();
|
||||
// Wizard undoes Invisibility spell
|
||||
goblin.printStatus();
|
||||
// Goblin, [size=small] [visibility=visible]
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -43,4 +248,7 @@ Use the Command pattern when you want to
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
* [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=f27d2644fbe5026ea448791a8ad09c94)
|
||||
|
@ -168,4 +168,6 @@ Use the Composite pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
@ -5,8 +5,8 @@ folder: cqrs
|
||||
permalink: /patterns/cqrs/
|
||||
categories: Architectural
|
||||
tags:
|
||||
- Performance
|
||||
- Cloud distributed
|
||||
- Performance
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Intent
|
||||
@ -18,12 +18,13 @@ CQRS Command Query Responsibility Segregation - Separate the query side from the
|
||||
## Applicability
|
||||
Use the CQRS pattern when
|
||||
|
||||
* you want to scale the queries and commands independently.
|
||||
* you want to use different data models for queries and commands. Useful when dealing with complex domains.
|
||||
* you want to use architectures like event sourcing or task based UI.
|
||||
* You want to scale the queries and commands independently.
|
||||
* You want to use different data models for queries and commands. Useful when dealing with complex domains.
|
||||
* You want to use architectures like event sourcing or task based UI.
|
||||
|
||||
## Credits
|
||||
|
||||
* [Greg Young - CQRS, Task Based UIs, Event Sourcing agh!](http://codebetter.com/gregyoung/2010/02/16/cqrs-task-based-uis-event-sourcing-agh/)
|
||||
* [Martin Fowler - CQRS](https://martinfowler.com/bliki/CQRS.html)
|
||||
* [Oliver Wolf - CQRS for Great Good](https://www.youtube.com/watch?v=Ge53swja9Dw)
|
||||
* [Command and Query Responsibility Segregation (CQRS) pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs)
|
||||
|
296
dao/README.md
296
dao/README.md
@ -9,8 +9,298 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Object provides an abstract interface to some type of database or
|
||||
other persistence mechanism.
|
||||
Object provides an abstract interface to some type of database or other persistence mechanism.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> There's a set of customers that need to be persisted to database. Additionally we need the whole set of CRUD (create/read/update/delete) operations so we can operate on customers easily.
|
||||
|
||||
In plain words
|
||||
|
||||
> DAO is an interface we provide over the base persistence mechanism.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In computer software, a data access object (DAO) is a pattern that provides an abstract interface to some type of database or other persistence mechanism.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Walking through our customers example, here's the basic Customer entity.
|
||||
|
||||
```java
|
||||
public class Customer {
|
||||
|
||||
private int id;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
|
||||
public Customer(int id, String firstName, String lastName) {
|
||||
this.id = id;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(final String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(final String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Customer{" + "id=" + getId() + ", firstName='" + getFirstName() + '\'' + ", lastName='"
|
||||
+ getLastName() + '\'' + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object that) {
|
||||
var isEqual = false;
|
||||
if (this == that) {
|
||||
isEqual = true;
|
||||
} else if (that != null && getClass() == that.getClass()) {
|
||||
final var customer = (Customer) that;
|
||||
if (getId() == customer.getId()) {
|
||||
isEqual = true;
|
||||
}
|
||||
}
|
||||
return isEqual;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the DAO interface and two different implementations for it. InMemoryCustomerDao keeps a simple map of customers
|
||||
in memory while DBCustomerDao is the real RDBMS implementation.
|
||||
|
||||
```java
|
||||
public interface CustomerDao {
|
||||
|
||||
Stream<Customer> getAll() throws Exception;
|
||||
|
||||
Optional<Customer> getById(int id) throws Exception;
|
||||
|
||||
boolean add(Customer customer) throws Exception;
|
||||
|
||||
boolean update(Customer customer) throws Exception;
|
||||
|
||||
boolean delete(Customer customer) throws Exception;
|
||||
}
|
||||
|
||||
public class InMemoryCustomerDao implements CustomerDao {
|
||||
|
||||
private Map<Integer, Customer> idToCustomer = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Stream<Customer> getAll() {
|
||||
return idToCustomer.values().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Customer> getById(final int id) {
|
||||
return Optional.ofNullable(idToCustomer.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(final Customer customer) {
|
||||
if (getById(customer.getId()).isPresent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
idToCustomer.put(customer.getId(), customer);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(final Customer customer) {
|
||||
return idToCustomer.replace(customer.getId(), customer) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(final Customer customer) {
|
||||
return idToCustomer.remove(customer.getId()) != null;
|
||||
}
|
||||
}
|
||||
|
||||
public class DbCustomerDao implements CustomerDao {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DbCustomerDao.class);
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
public DbCustomerDao(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Customer> getAll() throws Exception {
|
||||
try {
|
||||
var connection = getConnection();
|
||||
var statement = connection.prepareStatement("SELECT * FROM CUSTOMERS");
|
||||
var resultSet = statement.executeQuery();
|
||||
return StreamSupport.stream(new Spliterators.AbstractSpliterator<Customer>(Long.MAX_VALUE,
|
||||
Spliterator.ORDERED) {
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super Customer> action) {
|
||||
try {
|
||||
if (!resultSet.next()) {
|
||||
return false;
|
||||
}
|
||||
action.accept(createCustomer(resultSet));
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}, false).onClose(() -> mutedClose(connection, statement, resultSet));
|
||||
} catch (SQLException e) {
|
||||
throw new CustomException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Connection getConnection() throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
private void mutedClose(Connection connection, PreparedStatement statement, ResultSet resultSet) {
|
||||
try {
|
||||
resultSet.close();
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.info("Exception thrown " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Customer createCustomer(ResultSet resultSet) throws SQLException {
|
||||
return new Customer(resultSet.getInt("ID"),
|
||||
resultSet.getString("FNAME"),
|
||||
resultSet.getString("LNAME"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Customer> getById(int id) throws Exception {
|
||||
|
||||
ResultSet resultSet = null;
|
||||
|
||||
try (var connection = getConnection();
|
||||
var statement = connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) {
|
||||
|
||||
statement.setInt(1, id);
|
||||
resultSet = statement.executeQuery();
|
||||
if (resultSet.next()) {
|
||||
return Optional.of(createCustomer(resultSet));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
} finally {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Customer customer) throws Exception {
|
||||
if (getById(customer.getId()).isPresent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try (var connection = getConnection();
|
||||
var statement = connection.prepareStatement("INSERT INTO CUSTOMERS VALUES (?,?,?)")) {
|
||||
statement.setInt(1, customer.getId());
|
||||
statement.setString(2, customer.getFirstName());
|
||||
statement.setString(3, customer.getLastName());
|
||||
statement.execute();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(Customer customer) throws Exception {
|
||||
try (var connection = getConnection();
|
||||
var statement =
|
||||
connection
|
||||
.prepareStatement("UPDATE CUSTOMERS SET FNAME = ?, LNAME = ? WHERE ID = ?")) {
|
||||
statement.setString(1, customer.getFirstName());
|
||||
statement.setString(2, customer.getLastName());
|
||||
statement.setInt(3, customer.getId());
|
||||
return statement.executeUpdate() > 0;
|
||||
} catch (SQLException ex) {
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(Customer customer) throws Exception {
|
||||
try (var connection = getConnection();
|
||||
var statement = connection.prepareStatement("DELETE FROM CUSTOMERS WHERE ID = ?")) {
|
||||
statement.setInt(1, customer.getId());
|
||||
return statement.executeUpdate() > 0;
|
||||
} catch (SQLException ex) {
|
||||
throw new CustomException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Finally here's how we use our DAO to manage customers.
|
||||
|
||||
```java
|
||||
final var dataSource = createDataSource();
|
||||
createSchema(dataSource);
|
||||
final var customerDao = new DbCustomerDao(dataSource);
|
||||
|
||||
addCustomers(customerDao);
|
||||
log.info(ALL_CUSTOMERS);
|
||||
try (var customerStream = customerDao.getAll()) {
|
||||
customerStream.forEach((customer) -> log.info(customer.toString()));
|
||||
}
|
||||
log.info("customerDao.getCustomerById(2): " + customerDao.getById(2));
|
||||
final var customer = new Customer(4, "Dan", "Danson");
|
||||
customerDao.add(customer);
|
||||
log.info(ALL_CUSTOMERS + customerDao.getAll());
|
||||
customer.setFirstName("Daniel");
|
||||
customer.setLastName("Danielson");
|
||||
customerDao.update(customer);
|
||||
log.info(ALL_CUSTOMERS);
|
||||
try (var customerStream = customerDao.getAll()) {
|
||||
customerStream.forEach((cust) -> log.info(cust.toString()));
|
||||
}
|
||||
customerDao.delete(customer);
|
||||
log.info(ALL_CUSTOMERS + customerDao.getAll());
|
||||
|
||||
deleteSchema(dataSource);
|
||||
```
|
||||
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -23,4 +313,4 @@ Use the Data Access Object in any of the following situations
|
||||
|
||||
## Credits
|
||||
|
||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||
* [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)
|
||||
|
@ -35,7 +35,7 @@ public class Customer {
|
||||
/**
|
||||
* Creates an instance of customer.
|
||||
*/
|
||||
public Customer(final int id, final String firstName, final String lastName) {
|
||||
public Customer(int id, String firstName, String lastName) {
|
||||
this.id = id;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
|
@ -9,8 +9,89 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Pass data with multiple attributes in one shot from client to server,
|
||||
to avoid multiple calls to remote server.
|
||||
Pass data with multiple attributes in one shot from client to server, to avoid multiple calls to remote server.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> We need to fetch information about customers from remote database. Instead of querying the attributes one at a time, we use DTOs to transfer all the relevant attributes in a single shot.
|
||||
|
||||
In plain words
|
||||
|
||||
> Using DTO relevant information can be fetched with a single backend query.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In the field of programming a data transfer object (DTO) is an object that carries data between processes. The
|
||||
motivation for its use is that communication between processes is usually done resorting to remote interfaces
|
||||
(e.g., web services), where each call is an expensive operation. Because the majority of the cost of each call is
|
||||
related to the round-trip time between the client and the server, one way of reducing the number of calls is to use an
|
||||
object (the DTO) that aggregates the data that would have been transferred by the several calls, but that is served by
|
||||
one call only.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's first introduce our simple customer DTO class.
|
||||
|
||||
```java
|
||||
public class CustomerDto {
|
||||
private final String id;
|
||||
private final String firstName;
|
||||
private final String lastName;
|
||||
|
||||
public CustomerDto(String id, String firstName, String lastName) {
|
||||
this.id = id;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Customer resource class acts as the server for customer information.
|
||||
|
||||
```java
|
||||
public class CustomerResource {
|
||||
private List<CustomerDto> customers;
|
||||
|
||||
public CustomerResource(List<CustomerDto> customers) {
|
||||
this.customers = customers;
|
||||
}
|
||||
|
||||
public List<CustomerDto> getAllCustomers() {
|
||||
return customers;
|
||||
}
|
||||
|
||||
public void save(CustomerDto customer) {
|
||||
customers.add(customer);
|
||||
}
|
||||
|
||||
public void delete(String customerId) {
|
||||
customers.removeIf(customer -> customer.getId().equals(customerId));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now fetching customer information is easy since we have the DTOs.
|
||||
|
||||
```java
|
||||
var allCustomers = customerResource.getAllCustomers();
|
||||
allCustomers.forEach(customer -> LOGGER.info(customer.getFirstName()));
|
||||
// Kelly
|
||||
// Alfonso
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -26,3 +107,5 @@ Use the Data Transfer Object pattern when
|
||||
|
||||
* [Design Pattern - Transfer Object Pattern](https://www.tutorialspoint.com/design_pattern/transfer_object_pattern.htm)
|
||||
* [Data Transfer Object](https://msdn.microsoft.com/en-us/library/ff649585.aspx)
|
||||
* [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=f27d2644fbe5026ea448791a8ad09c94)
|
||||
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=014237a67c9d46f384b35e10151956bd)
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<class-diagram version="1.2.0" 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.datatransfer.CustomerClientApp" project="data-transfer-object"
|
||||
<class id="1" language="java" name="com.iluwatar.datatransfer.App" project="data-transfer-object"
|
||||
file="/data-transfer-object/src/main/java/com/iluwatar/datatransfer/CustomerClientApp.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="145" y="93"/>
|
||||
|
@ -48,7 +48,7 @@
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.iluwatar.datatransfer.CustomerClientApp</mainClass>
|
||||
<mainClass>com.iluwatar.datatransfer.App</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
|
@ -32,15 +32,15 @@ import org.slf4j.LoggerFactory;
|
||||
* The Data Transfer Object pattern is a design pattern in which an data transfer object is used to
|
||||
* serve related information together to avoid multiple call for each piece of information.
|
||||
*
|
||||
* <p>In this example, ({@link CustomerClientApp}) as as customer details consumer i.e. client to
|
||||
* <p>In this example, ({@link App}) as as customer details consumer i.e. client to
|
||||
* request for customer details to server.
|
||||
*
|
||||
* <p>CustomerResource ({@link CustomerResource}) act as server to serve customer information. And
|
||||
* The CustomerDto ({@link CustomerDto} is data transfer object to share customer information.
|
||||
*/
|
||||
public class CustomerClientApp {
|
||||
public class App {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CustomerClientApp.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||
|
||||
/**
|
||||
* Method as act client and request to server for details.
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.datatransfer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class AppTest {
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
App.main(new String[]{});
|
||||
}
|
||||
}
|
@ -131,6 +131,9 @@ Use Decorator
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1)
|
||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](https://www.amazon.com/gp/product/1937785467/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1937785467&linkCode=as2&tag=javadesignpat-20&linkId=7e4e2fb7a141631491534255252fd08b)
|
||||
* [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)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
* [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=f27d2644fbe5026ea448791a8ad09c94)
|
||||
|
@ -9,12 +9,78 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Dependency Injection is a software design pattern in which one or
|
||||
more dependencies (or services) are injected, or passed by reference, into a
|
||||
dependent object (or client) and are made part of the client's state. The
|
||||
pattern separates the creation of a client's dependencies from its own
|
||||
behavior, which allows program designs to be loosely coupled and to follow the
|
||||
inversion of control and single responsibility principles.
|
||||
Dependency Injection is a software design pattern in which one or more dependencies (or services) are injected, or
|
||||
passed by reference, into a dependent object (or client) and are made part of the client's state. The pattern separates
|
||||
the creation of a client's dependencies from its own behavior, which allows program designs to be loosely coupled and
|
||||
to follow the inversion of control and single responsibility principles.
|
||||
|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> The old wizard likes to fill his pipe and smoke tobacco once in a while. However, he doesn't want to depend on a single tobacco brand only but likes to be able to enjoy them all interchangeably.
|
||||
|
||||
In plain words
|
||||
|
||||
> Dependency Injection separates creation of client's dependencies from its own behavior.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In software engineering, dependency injection is a technique in which an object receives other objects that it depends on. These other objects are called dependencies.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's first introduce the tobacco brands.
|
||||
|
||||
```java
|
||||
public abstract class Tobacco {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Tobacco.class);
|
||||
|
||||
public void smoke(Wizard wizard) {
|
||||
LOGGER.info("{} smoking {}", wizard.getClass().getSimpleName(),
|
||||
this.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
public class SecondBreakfastTobacco extends Tobacco {
|
||||
}
|
||||
|
||||
public class RivendellTobacco extends Tobacco {
|
||||
}
|
||||
|
||||
public class OldTobyTobacco extends Tobacco {
|
||||
}
|
||||
```
|
||||
|
||||
Next here's the wizard class hierarchy.
|
||||
|
||||
```java
|
||||
public interface Wizard {
|
||||
|
||||
void smoke();
|
||||
}
|
||||
|
||||
public class AdvancedWizard implements Wizard {
|
||||
|
||||
private Tobacco tobacco;
|
||||
|
||||
public AdvancedWizard(Tobacco tobacco) {
|
||||
this.tobacco = tobacco;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void smoke() {
|
||||
tobacco.smoke(this);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And lastly we can show how easy it is to give the old wizard any brand of tobacco.
|
||||
|
||||
```java
|
||||
var advancedWizard = new AdvancedWizard(new SecondBreakfastTobacco());
|
||||
advancedWizard.smoke();
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -22,5 +88,12 @@ inversion of control and single responsibility principles.
|
||||
## Applicability
|
||||
Use the Dependency Injection pattern when
|
||||
|
||||
* when you need to remove knowledge of concrete implementation from object
|
||||
* to enable unit testing of classes in isolation using mock objects or stubs
|
||||
* When you need to remove knowledge of concrete implementation from object
|
||||
* To enable unit testing of classes in isolation using mock objects or stubs
|
||||
|
||||
## Credits
|
||||
|
||||
* [Dependency Injection Principles, Practices, and Patterns](https://www.amazon.com/gp/product/161729473X/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=161729473X&linkId=57079257a5c7d33755493802f3b884bd)
|
||||
* [Clean Code: A Handbook of Agile Software Craftsmanship](https://www.amazon.com/gp/product/0132350882/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0132350882&linkCode=as2&tag=javadesignpat-20&linkId=2c390d89cc9e61c01b9e7005c7842871)
|
||||
* [Java 9 Dependency Injection: Write loosely coupled code with Spring 5 and Guice](https://www.amazon.com/gp/product/1788296257/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=1788296257&linkId=4e9137a3bf722a8b5b156cce1eec0fc1)
|
||||
* [Google Guice Tutorial: Open source Java based dependency injection framework](https://www.amazon.com/gp/product/B083P7DZ8M/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B083P7DZ8M&linkId=04f0f902c877921e45215b624a124bfe)
|
||||
|
@ -9,6 +9,9 @@ tags:
|
||||
- Performance
|
||||
---
|
||||
|
||||
## Also known as
|
||||
* IsDirty pattern
|
||||
|
||||
## Intent
|
||||
To avoid expensive re-acquisition of resources. The resources retain their identity, are kept in some
|
||||
fast-access storage, and are re-used to avoid having to acquire them again.
|
||||
@ -24,3 +27,4 @@ Use the Dirty Flag pattern when
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Dirty Flag](https://www.takeupcode.com/podcast/89-design-patterns-dirty-flag/)
|
||||
* [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)
|
||||
|
@ -29,3 +29,4 @@ Use the Aggregator pattern when
|
||||
|
||||
* [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/Aggregator.html)
|
||||
* [Apache Camel - Documentation](http://camel.apache.org/aggregator2.html)
|
||||
* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
|
||||
|
@ -23,3 +23,4 @@ Use the Message Channel pattern when
|
||||
## Real world examples
|
||||
|
||||
* [akka-camel](http://doc.akka.io/docs/akka/snapshot/scala/camel.html)
|
||||
* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
|
||||
|
@ -21,4 +21,5 @@ Use the Publish Subscribe Channel pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||
* [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)
|
||||
* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
|
||||
|
@ -28,3 +28,4 @@ Use the Splitter pattern when
|
||||
|
||||
* [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/Sequencer.html)
|
||||
* [Apache Camel - Documentation](http://camel.apache.org/splitter.html)
|
||||
* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
|
||||
|
@ -26,3 +26,4 @@ Use the Wire Tap pattern when
|
||||
|
||||
* [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/WireTap.html)
|
||||
* [Apache Camel - Documentation](http://camel.apache.org/wire-tap.html)
|
||||
* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
|
||||
|
@ -5,7 +5,8 @@ folder: event-sourcing
|
||||
permalink: /patterns/event-sourcing/
|
||||
categories: Architectural
|
||||
tags:
|
||||
- Performance
|
||||
- Performance
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Intent
|
||||
@ -30,3 +31,4 @@ Use the Event Sourcing pattern when
|
||||
* [Martin Fowler - Event Sourcing] (https://martinfowler.com/eaaDev/EventSourcing.html)
|
||||
* [Event Sourcing | Microsoft Docs] (https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing)
|
||||
* [Reference 3: Introducing Event Sourcing] (https://msdn.microsoft.com/en-us/library/jj591559.aspx)
|
||||
* [Event Sourcing pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing)
|
||||
|
@ -9,10 +9,56 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Execute Around idiom frees the user from certain actions that
|
||||
should always be executed before and after the business method. A good example
|
||||
of this is resource allocation and deallocation leaving the user to specify
|
||||
only what to do with the resource.
|
||||
Execute Around idiom frees the user from certain actions that should always be executed before and after the business
|
||||
method. A good example of this is resource allocation and deallocation leaving the user to specify only what to do with
|
||||
the resource.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> We need to provide a class that can be used to write text strings to files. To make it easy for the user we let our service class open and close the file automatically, the user only has to specify what is written into which file.
|
||||
|
||||
In plain words
|
||||
|
||||
> Execute Around idiom handles boilerplate code before and after business method.
|
||||
|
||||
[Stack Overflow](https://stackoverflow.com/questions/341971/what-is-the-execute-around-idiom) says
|
||||
|
||||
> Basically it's the pattern where you write a method to do things which are always required, e.g. resource allocation and clean-up, and make the caller pass in "what we want to do with the resource".
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's introduce our file writer class.
|
||||
|
||||
```java
|
||||
@FunctionalInterface
|
||||
public interface FileWriterAction {
|
||||
|
||||
void writeFile(FileWriter writer) throws IOException;
|
||||
|
||||
}
|
||||
|
||||
public class SimpleFileWriter {
|
||||
|
||||
public SimpleFileWriter(String filename, FileWriterAction action) throws IOException {
|
||||
try (var writer = new FileWriter(filename)) {
|
||||
action.writeFile(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To utilize the file writer the following code is needed.
|
||||
|
||||
```java
|
||||
FileWriterAction writeHello = writer -> {
|
||||
writer.write("Hello");
|
||||
writer.append(" ");
|
||||
writer.append("there!");
|
||||
};
|
||||
new SimpleFileWriter("testfile.txt", writeHello);
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -24,4 +70,4 @@ Use the Execute Around idiom when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1)
|
||||
* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](https://www.amazon.com/gp/product/1937785467/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1937785467&linkCode=as2&tag=javadesignpat-20&linkId=7e4e2fb7a141631491534255252fd08b)
|
||||
|
@ -204,4 +204,5 @@ Use the Facade pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
@ -83,4 +83,6 @@ Use the Factory Method pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
@ -9,26 +9,151 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using this pattern results in code that can be read nearly as human language.
|
||||
A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using
|
||||
this pattern results in code that can be read nearly as human language.
|
||||
|
||||
## Implementation
|
||||
## Explanation
|
||||
|
||||
The Fluent Interface pattern is useful when you want to provide an easy readable, flowing API. Those interfaces tend
|
||||
to mimic domain specific languages, so they can nearly be read as human languages.
|
||||
|
||||
A fluent interface can be implemented using any of
|
||||
|
||||
* Method Chaining - calling a method returns some object on which further methods can be called.
|
||||
* Static Factory Methods and Imports
|
||||
* Named parameters - can be simulated in Java using static factory methods.
|
||||
|
||||
Real world example
|
||||
|
||||
> We need to select numbers based on different criteria from the list. It's a great chance to utilize fluent interface pattern to provide readable easy-to-use developer experience.
|
||||
|
||||
In plain words
|
||||
|
||||
> Fluent Interface pattern provides easily readable flowing interface to code.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL).
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
In this example two implementations of a `FluentIterable` interface are given.
|
||||
|
||||
```java
|
||||
public interface FluentIterable<E> extends Iterable<E> {
|
||||
|
||||
FluentIterable<E> filter(Predicate<? super E> predicate);
|
||||
|
||||
Optional<E> first();
|
||||
|
||||
FluentIterable<E> first(int count);
|
||||
|
||||
Optional<E> last();
|
||||
|
||||
FluentIterable<E> last(int count);
|
||||
|
||||
<T> FluentIterable<T> map(Function<? super E, T> function);
|
||||
|
||||
List<E> asList();
|
||||
|
||||
static <E> List<E> copyToList(Iterable<E> iterable) {
|
||||
var copy = new ArrayList<E>();
|
||||
iterable.forEach(copy::add);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `SimpleFluentIterable` evaluates eagerly and would be too costly for real world applications.
|
||||
|
||||
```java
|
||||
public class SimpleFluentIterable<E> implements FluentIterable<E> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The `LazyFluentIterable` is evaluated on termination.
|
||||
|
||||
```java
|
||||
public class LazyFluentIterable<E> implements FluentIterable<E> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Their usage is demonstrated with a simple number list that is filtered, transformed and collected. The
|
||||
result is printed afterwards.
|
||||
|
||||
```java
|
||||
var integerList = List.of(1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68);
|
||||
|
||||
prettyPrint("The initial list contains: ", integerList);
|
||||
|
||||
var firstFiveNegatives = SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(negatives())
|
||||
.first(3)
|
||||
.asList();
|
||||
prettyPrint("The first three negative values are: ", firstFiveNegatives);
|
||||
|
||||
|
||||
var lastTwoPositives = SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(positives())
|
||||
.last(2)
|
||||
.asList();
|
||||
prettyPrint("The last two positive values are: ", lastTwoPositives);
|
||||
|
||||
SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(number -> number % 2 == 0)
|
||||
.first()
|
||||
.ifPresent(evenNumber -> LOGGER.info("The first even number is: {}", evenNumber));
|
||||
|
||||
|
||||
var transformedList = SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(negatives())
|
||||
.map(transformToString())
|
||||
.asList();
|
||||
prettyPrint("A string-mapped list of negative numbers contains: ", transformedList);
|
||||
|
||||
|
||||
var lastTwoOfFirstFourStringMapped = LazyFluentIterable
|
||||
.from(integerList)
|
||||
.filter(positives())
|
||||
.first(4)
|
||||
.last(2)
|
||||
.map(number -> "String[" + valueOf(number) + "]")
|
||||
.asList();
|
||||
prettyPrint("The lazy list contains the last two of the first four positive numbers "
|
||||
+ "mapped to Strings: ", lastTwoOfFirstFourStringMapped);
|
||||
|
||||
LazyFluentIterable
|
||||
.from(integerList)
|
||||
.filter(negatives())
|
||||
.first(2)
|
||||
.last()
|
||||
.ifPresent(number -> LOGGER.info("Last amongst first two negatives: {}", number));
|
||||
|
||||
// The initial list contains: 1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68.
|
||||
// The first three negative values are: -61, -22, -87.
|
||||
// The last two positive values are: 23, 2.
|
||||
// The first even number is: 14
|
||||
// A string-mapped list of negative numbers contains: String[-61], String[-22], String[-87], String[-82], String[-98], String[-68].
|
||||
// The lazy list contains the last two of the first four positive numbers mapped to Strings: String[18], String[6].
|
||||
// Last amongst first two negatives: -22
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
## Applicability
|
||||
Use the Fluent Interface pattern when
|
||||
|
||||
* you provide an API that would benefit from a DSL-like usage
|
||||
* you have objects that are difficult to configure or use
|
||||
* You provide an API that would benefit from a DSL-like usage
|
||||
* You have objects that are difficult to configure or use
|
||||
|
||||
## Real world examples
|
||||
## Known uses
|
||||
|
||||
* [Java 8 Stream API](http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html)
|
||||
* [Google Guava FluentInterable](https://github.com/google/guava/wiki/FunctionalExplained)
|
||||
@ -41,3 +166,4 @@ Use the Fluent Interface pattern when
|
||||
* [Fluent Interface - Martin Fowler](http://www.martinfowler.com/bliki/FluentInterface.html)
|
||||
* [Evolutionary architecture and emergent design: Fluent interfaces - Neal Ford](http://www.ibm.com/developerworks/library/j-eaed14/)
|
||||
* [Internal DSL](http://www.infoq.com/articles/internal-dsls-java)
|
||||
* [Domain Specific Languages](https://www.amazon.com/gp/product/0321712943/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=0321712943&linkId=ad8351d6f5be7d8b7ecdb650731f85df)
|
||||
|
@ -128,4 +128,5 @@ true
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
@ -29,6 +29,7 @@ Use the Front Controller pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||
* [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)
|
||||
* [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns)
|
||||
* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)
|
||||
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=d9f7d37b032ca6e96253562d075fcc4a)
|
||||
* [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=f27d2644fbe5026ea448791a8ad09c94)
|
||||
|
@ -9,33 +9,227 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
A game loop runs continuously during gameplay. Each turn of the loop, it processes user input without blocking, updates the game state, and renders the game. It tracks the passage of time to control the rate of gameplay.
|
||||
A game loop runs continuously during gameplay. Each turn of the loop, it processes user input without blocking, updates
|
||||
the game state, and renders the game. It tracks the passage of time to control the rate of gameplay.
|
||||
|
||||
This pattern decouple the progression of game time from user input and processor speed.
|
||||
This pattern decouples progression of game time from user input and processor speed.
|
||||
|
||||
## Applicability
|
||||
This pattern is used in every game engine.
|
||||
|
||||
## Explanation
|
||||
Game loop is the main process of all the game rendering threads. It drives input process, internal status update, rendering, AI and all the other processes.
|
||||
Real world example
|
||||
|
||||
There are a lot of implementations of game loop:
|
||||
> Game loop is the main process of all the game rendering threads. It's present in all modern games. It drives input process, internal status update, rendering, AI and all the other processes.
|
||||
|
||||
- Frame-based game loop
|
||||
In plain words
|
||||
|
||||
Frame-based game loop is the easiest implementation. The loop always keeps spinning for the following three processes: processInput, update and render. The problem with it is you have no control over how fast the game runs. On a fast machine, that loop will spin so fast users won’t be able to see what’s going on. On a slow machine, the game will crawl. If you have a part of the game that’s content-heavy or does more AI or physics, the game will actually play slower there.
|
||||
> Game Loop pattern ensures that game time progresses in equal speed in all different hardware setups.
|
||||
|
||||
- Variable-step game loop
|
||||
Wikipedia says
|
||||
|
||||
The variable-step game loop chooses a time step to advance based on how much real time passed since the last frame. The longer the frame takes, the bigger steps the game takes. It always keeps up with real time because it will take bigger and bigger steps to get there.
|
||||
> The central component of any game, from a programming standpoint, is the game loop. The game loop allows the game to run smoothly regardless of a user's input or lack thereof.
|
||||
|
||||
- Fixed-step game loop
|
||||
**Programmatic Example**
|
||||
|
||||
For fixed-step game loop, a certain amount of real time has elapsed since the last turn of the game loop. This is how much game time need to be simulated for the game’s “now” to catch up with the player’s.
|
||||
Let's start with something simple. Here's a bullet that will move in our game. For demonstration it's enough that it has 1-dimensional position.
|
||||
|
||||
```java
|
||||
public class Bullet {
|
||||
|
||||
private float position;
|
||||
|
||||
public Bullet() {
|
||||
position = 0.0f;
|
||||
}
|
||||
|
||||
public float getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(float position) {
|
||||
this.position = position;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
GameController is responsible for moving objects in the game. Including the aforementioned bullet.
|
||||
|
||||
```java
|
||||
public class GameController {
|
||||
|
||||
protected final Bullet bullet;
|
||||
|
||||
public GameController() {
|
||||
bullet = new Bullet();
|
||||
}
|
||||
|
||||
public void moveBullet(float offset) {
|
||||
var currentPosition = bullet.getPosition();
|
||||
bullet.setPosition(currentPosition + offset);
|
||||
}
|
||||
|
||||
public float getBulletPosition() {
|
||||
return bullet.getPosition();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we introduce the game loop. Or actually in this demo we have 3 different game loops.
|
||||
|
||||
```java
|
||||
public enum GameStatus {
|
||||
|
||||
RUNNING, STOPPED
|
||||
}
|
||||
|
||||
public abstract class GameLoop {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
protected volatile GameStatus status;
|
||||
|
||||
protected GameController controller;
|
||||
|
||||
private Thread gameThread;
|
||||
|
||||
public GameLoop() {
|
||||
controller = new GameController();
|
||||
status = GameStatus.STOPPED;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
status = GameStatus.RUNNING;
|
||||
gameThread = new Thread(() -> processGameLoop());
|
||||
gameThread.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
status = GameStatus.STOPPED;
|
||||
}
|
||||
|
||||
public boolean isGameRunning() {
|
||||
return status == GameStatus.RUNNING;
|
||||
}
|
||||
|
||||
protected void processInput() {
|
||||
try {
|
||||
var lag = new Random().nextInt(200) + 50;
|
||||
Thread.sleep(lag);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected void render() {
|
||||
var position = controller.getBulletPosition();
|
||||
logger.info("Current bullet position: " + position);
|
||||
}
|
||||
|
||||
protected abstract void processGameLoop();
|
||||
}
|
||||
|
||||
public class FrameBasedGameLoop extends GameLoop {
|
||||
|
||||
@Override
|
||||
protected void processGameLoop() {
|
||||
while (isGameRunning()) {
|
||||
processInput();
|
||||
update();
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
protected void update() {
|
||||
controller.moveBullet(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
public class VariableStepGameLoop extends GameLoop {
|
||||
|
||||
@Override
|
||||
protected void processGameLoop() {
|
||||
var lastFrameTime = System.currentTimeMillis();
|
||||
while (isGameRunning()) {
|
||||
processInput();
|
||||
var currentFrameTime = System.currentTimeMillis();
|
||||
var elapsedTime = currentFrameTime - lastFrameTime;
|
||||
update(elapsedTime);
|
||||
lastFrameTime = currentFrameTime;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
protected void update(Long elapsedTime) {
|
||||
controller.moveBullet(0.5f * elapsedTime / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
public class FixedStepGameLoop extends GameLoop {
|
||||
|
||||
private static final long MS_PER_FRAME = 20;
|
||||
|
||||
@Override
|
||||
protected void processGameLoop() {
|
||||
var previousTime = System.currentTimeMillis();
|
||||
var lag = 0L;
|
||||
while (isGameRunning()) {
|
||||
var currentTime = System.currentTimeMillis();
|
||||
var elapsedTime = currentTime - previousTime;
|
||||
previousTime = currentTime;
|
||||
lag += elapsedTime;
|
||||
|
||||
processInput();
|
||||
|
||||
while (lag >= MS_PER_FRAME) {
|
||||
update();
|
||||
lag -= MS_PER_FRAME;
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
protected void update() {
|
||||
controller.moveBullet(0.5f * MS_PER_FRAME / 1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Finally we can show all these game loops in action.
|
||||
|
||||
```java
|
||||
try {
|
||||
LOGGER.info("Start frame-based game loop:");
|
||||
var frameBasedGameLoop = new FrameBasedGameLoop();
|
||||
frameBasedGameLoop.run();
|
||||
Thread.sleep(GAME_LOOP_DURATION_TIME);
|
||||
frameBasedGameLoop.stop();
|
||||
LOGGER.info("Stop frame-based game loop.");
|
||||
|
||||
LOGGER.info("Start variable-step game loop:");
|
||||
var variableStepGameLoop = new VariableStepGameLoop();
|
||||
variableStepGameLoop.run();
|
||||
Thread.sleep(GAME_LOOP_DURATION_TIME);
|
||||
variableStepGameLoop.stop();
|
||||
LOGGER.info("Stop variable-step game loop.");
|
||||
|
||||
LOGGER.info("Start fixed-step game loop:");
|
||||
var fixedStepGameLoop = new FixedStepGameLoop();
|
||||
fixedStepGameLoop.run();
|
||||
Thread.sleep(GAME_LOOP_DURATION_TIME);
|
||||
fixedStepGameLoop.stop();
|
||||
LOGGER.info("Stop variable-step game loop.");
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.error(e.getMessage());
|
||||
}
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
## Credits
|
||||
|
||||
* [Game Programming Patterns - Game Loop](http://gameprogrammingpatterns.com/game-loop.html)
|
||||
* [Game Programming Patterns](https://www.amazon.com/gp/product/0990582906/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0990582906&linkId=1289749a703b3fe0e24cd8d604d7c40b)
|
||||
* [Game Engine Architecture, Third Edition](https://www.amazon.com/gp/product/1138035459/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1138035459&linkId=94502746617211bc40e0ef49d29333ac)
|
||||
|
@ -34,4 +34,4 @@ Use Half-Sync/Half-Async pattern when
|
||||
## Credits
|
||||
|
||||
* [Douglas C. Schmidt and Charles D. Cranor - Half Sync/Half Async](https://www.dre.vanderbilt.edu/~schmidt/PDF/PLoP-95.pdf)
|
||||
* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697)
|
||||
* [Pattern Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://www.amazon.com/gp/product/0471606952/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0471606952&linkCode=as2&tag=javadesignpat-20&linkId=889e4af72dca8261129bf14935e0f8dc)
|
||||
|
@ -34,4 +34,6 @@ trees. The Interpreter pattern works best when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
@ -15,6 +15,105 @@ Cursor
|
||||
Provide a way to access the elements of an aggregate object
|
||||
sequentially without exposing its underlying representation.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> Treasure chest contains a set of magical items. There multiple types of items such as rings, potions and weapons. The items can be browsed by type using an iterator the treasure chest provides.
|
||||
|
||||
In plain words
|
||||
|
||||
> Containers can provide a representation agnostic iterator interface to provide access to the elements.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
The main class in our example is the treasure chest that contains items.
|
||||
|
||||
```java
|
||||
public class TreasureChest {
|
||||
|
||||
private List<Item> items;
|
||||
|
||||
public TreasureChest() {
|
||||
items = List.of(
|
||||
new Item(ItemType.POTION, "Potion of courage"),
|
||||
new Item(ItemType.RING, "Ring of shadows"),
|
||||
new Item(ItemType.POTION, "Potion of wisdom"),
|
||||
new Item(ItemType.POTION, "Potion of blood"),
|
||||
new Item(ItemType.WEAPON, "Sword of silver +1"),
|
||||
new Item(ItemType.POTION, "Potion of rust"),
|
||||
new Item(ItemType.POTION, "Potion of healing"),
|
||||
new Item(ItemType.RING, "Ring of armor"),
|
||||
new Item(ItemType.WEAPON, "Steel halberd"),
|
||||
new Item(ItemType.WEAPON, "Dagger of poison"));
|
||||
}
|
||||
|
||||
public Iterator<Item> iterator(ItemType itemType) {
|
||||
return new TreasureChestItemIterator(this, itemType);
|
||||
}
|
||||
|
||||
public List<Item> getItems() {
|
||||
return new ArrayList<>(items);
|
||||
}
|
||||
}
|
||||
|
||||
public class Item {
|
||||
|
||||
private ItemType type;
|
||||
private String name;
|
||||
|
||||
public Item(ItemType type, String name) {
|
||||
this.setType(type);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ItemType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public final void setType(ItemType type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ItemType {
|
||||
|
||||
ANY, WEAPON, RING, POTION
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
The iterator interface is extremely simple.
|
||||
|
||||
```java
|
||||
public interface Iterator<T> {
|
||||
|
||||
boolean hasNext();
|
||||
|
||||
T next();
|
||||
}
|
||||
```
|
||||
|
||||
In the following example we iterate through the ring type items found in the chest.
|
||||
|
||||
```java
|
||||
var itemIterator = TREASURE_CHEST.iterator(ItemType.RING);
|
||||
while (itemIterator.hasNext()) {
|
||||
LOGGER.info(itemIterator.next().toString());
|
||||
}
|
||||
// Ring of shadows
|
||||
// Ring of armor
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
@ -32,4 +131,5 @@ Use the Iterator pattern
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
@ -103,4 +103,4 @@ Use the Layers architecture when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697)
|
||||
* [Pattern Oriented Software Architecture Volume 1: A System of Patterns](https://www.amazon.com/gp/product/0471958697/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0471958697&linkCode=as2&tag=javadesignpat-20&linkId=e3f42d7a2a4cc8c619bbc0136b20dadb)
|
||||
|
@ -28,4 +28,5 @@ Use the Lazy Loading idiom when
|
||||
|
||||
## Credits
|
||||
|
||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||
* [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)
|
||||
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=d9f7d37b032ca6e96253562d075fcc4a)
|
||||
|
@ -30,4 +30,4 @@ Do not use this pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [ Cloud Design Patterns: Prescriptive Architecture Guidance for Cloud Applications](https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn568104(v=pandp.10))
|
||||
* [Leader Election pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/leader-election)
|
||||
|
@ -27,4 +27,4 @@ Use the Marker Interface pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Effective Java 2nd Edition by Joshua Bloch](https://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683)
|
||||
* [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb)
|
||||
|
@ -34,4 +34,5 @@ Use the Mediator pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
@ -12,8 +12,177 @@ tags:
|
||||
Token
|
||||
|
||||
## Intent
|
||||
Without violating encapsulation, capture and externalize an
|
||||
object's internal state so that the object can be restored to this state later.
|
||||
Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored
|
||||
to this state later.
|
||||
|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> We are working on astrology application where we need to analyze star properties over time. We are creating snapshots of star state using Memento pattern.
|
||||
|
||||
In plain words
|
||||
|
||||
> Memento pattern captures object internal state making it easy to store and restore objects in any point of time.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's first define the types of stars we are capable to handle.
|
||||
|
||||
```java
|
||||
public enum StarType {
|
||||
|
||||
SUN("sun"), RED_GIANT("red giant"), WHITE_DWARF("white dwarf"), SUPERNOVA("supernova"), DEAD(
|
||||
"dead star"), UNDEFINED("");
|
||||
|
||||
private String title;
|
||||
|
||||
StarType(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next let's jump straight to the essentials. Here's the star class along with the mementos that we need manipulate.
|
||||
|
||||
```java
|
||||
public interface StarMemento {
|
||||
}
|
||||
|
||||
public class Star {
|
||||
|
||||
private StarType type;
|
||||
private int ageYears;
|
||||
private int massTons;
|
||||
|
||||
public Star(StarType startType, int startAge, int startMass) {
|
||||
this.type = startType;
|
||||
this.ageYears = startAge;
|
||||
this.massTons = startMass;
|
||||
}
|
||||
|
||||
public void timePasses() {
|
||||
ageYears *= 2;
|
||||
massTons *= 8;
|
||||
switch (type) {
|
||||
case RED_GIANT:
|
||||
type = StarType.WHITE_DWARF;
|
||||
break;
|
||||
case SUN:
|
||||
type = StarType.RED_GIANT;
|
||||
break;
|
||||
case SUPERNOVA:
|
||||
type = StarType.DEAD;
|
||||
break;
|
||||
case WHITE_DWARF:
|
||||
type = StarType.SUPERNOVA;
|
||||
break;
|
||||
case DEAD:
|
||||
ageYears *= 2;
|
||||
massTons = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StarMemento getMemento() {
|
||||
|
||||
StarMementoInternal state = new StarMementoInternal();
|
||||
state.setAgeYears(ageYears);
|
||||
state.setMassTons(massTons);
|
||||
state.setType(type);
|
||||
return state;
|
||||
}
|
||||
|
||||
void setMemento(StarMemento memento) {
|
||||
|
||||
StarMementoInternal state = (StarMementoInternal) memento;
|
||||
this.type = state.getType();
|
||||
this.ageYears = state.getAgeYears();
|
||||
this.massTons = state.getMassTons();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s age: %d years mass: %d tons", type.toString(), ageYears, massTons);
|
||||
}
|
||||
|
||||
private static class StarMementoInternal implements StarMemento {
|
||||
|
||||
private StarType type;
|
||||
private int ageYears;
|
||||
private int massTons;
|
||||
|
||||
public StarType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(StarType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getAgeYears() {
|
||||
return ageYears;
|
||||
}
|
||||
|
||||
public void setAgeYears(int ageYears) {
|
||||
this.ageYears = ageYears;
|
||||
}
|
||||
|
||||
public int getMassTons() {
|
||||
return massTons;
|
||||
}
|
||||
|
||||
public void setMassTons(int massTons) {
|
||||
this.massTons = massTons;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And finally here's how we use the mementos to store and restore star states.
|
||||
|
||||
```java
|
||||
Stack<StarMemento> states = new Stack<>();
|
||||
Star star = new Star(StarType.SUN, 10000000, 500000);
|
||||
LOGGER.info(star.toString());
|
||||
states.add(star.getMemento());
|
||||
star.timePasses();
|
||||
LOGGER.info(star.toString());
|
||||
states.add(star.getMemento());
|
||||
star.timePasses();
|
||||
LOGGER.info(star.toString());
|
||||
states.add(star.getMemento());
|
||||
star.timePasses();
|
||||
LOGGER.info(star.toString());
|
||||
states.add(star.getMemento());
|
||||
star.timePasses();
|
||||
LOGGER.info(star.toString());
|
||||
while (states.size() > 0) {
|
||||
star.setMemento(states.pop());
|
||||
LOGGER.info(star.toString());
|
||||
}
|
||||
|
||||
// sun age: 10000000 years mass: 500000 tons
|
||||
// red giant age: 20000000 years mass: 4000000 tons
|
||||
// white dwarf age: 40000000 years mass: 32000000 tons
|
||||
// supernova age: 80000000 years mass: 256000000 tons
|
||||
// dead star age: 160000000 years mass: 2048000000 tons
|
||||
// supernova age: 80000000 years mass: 256000000 tons
|
||||
// white dwarf age: 40000000 years mass: 32000000 tons
|
||||
// red giant age: 20000000 years mass: 4000000 tons
|
||||
// sun age: 10000000 years mass: 500000 tons
|
||||
```
|
||||
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -30,4 +199,5 @@ Use the Memento pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
@ -25,5 +25,7 @@ Use the Model-View-Controller pattern when
|
||||
## Credits
|
||||
|
||||
* [Trygve Reenskaug - Model-view-controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)
|
||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||
* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)
|
||||
* [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)
|
||||
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=d9f7d37b032ca6e96253562d075fcc4a)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [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=f27d2644fbe5026ea448791a8ad09c94)
|
||||
|
@ -12,8 +12,88 @@ tags:
|
||||
Registry
|
||||
|
||||
## Intent
|
||||
Ensure a class only has limited number of instances, and provide a
|
||||
global point of access to them.
|
||||
Ensure a class only has limited number of instances and provide a global point of access to them.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> The Nazgûl, also called ringwraiths or the Nine Riders, are Sauron's most terrible servants. By definition there's always nine of them.
|
||||
|
||||
In plain words
|
||||
|
||||
> Multiton pattern ensures there's predefined amount of instances available globally.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In software engineering, the multiton pattern is a design pattern which generalizes the singleton pattern. Whereas the singleton allows only one instance of a class to be created, the multiton pattern allows for the controlled creation of multiple instances, which it manages through the use of a map.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Nazgul is the multiton class.
|
||||
|
||||
```java
|
||||
public enum NazgulName {
|
||||
|
||||
KHAMUL, MURAZOR, DWAR, JI_INDUR, AKHORAHIL, HOARMURATH, ADUNAPHEL, REN, UVATHA;
|
||||
}
|
||||
|
||||
public final class Nazgul {
|
||||
|
||||
private static Map<NazgulName, Nazgul> nazguls;
|
||||
|
||||
private NazgulName name;
|
||||
|
||||
static {
|
||||
nazguls = new ConcurrentHashMap<>();
|
||||
nazguls.put(NazgulName.KHAMUL, new Nazgul(NazgulName.KHAMUL));
|
||||
nazguls.put(NazgulName.MURAZOR, new Nazgul(NazgulName.MURAZOR));
|
||||
nazguls.put(NazgulName.DWAR, new Nazgul(NazgulName.DWAR));
|
||||
nazguls.put(NazgulName.JI_INDUR, new Nazgul(NazgulName.JI_INDUR));
|
||||
nazguls.put(NazgulName.AKHORAHIL, new Nazgul(NazgulName.AKHORAHIL));
|
||||
nazguls.put(NazgulName.HOARMURATH, new Nazgul(NazgulName.HOARMURATH));
|
||||
nazguls.put(NazgulName.ADUNAPHEL, new Nazgul(NazgulName.ADUNAPHEL));
|
||||
nazguls.put(NazgulName.REN, new Nazgul(NazgulName.REN));
|
||||
nazguls.put(NazgulName.UVATHA, new Nazgul(NazgulName.UVATHA));
|
||||
}
|
||||
|
||||
private Nazgul(NazgulName name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static Nazgul getInstance(NazgulName name) {
|
||||
return nazguls.get(name);
|
||||
}
|
||||
|
||||
public NazgulName getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And here's how we access the Nazgul instances.
|
||||
|
||||
```java
|
||||
LOGGER.info("KHAMUL={}", Nazgul.getInstance(NazgulName.KHAMUL));
|
||||
LOGGER.info("MURAZOR={}", Nazgul.getInstance(NazgulName.MURAZOR));
|
||||
LOGGER.info("DWAR={}", Nazgul.getInstance(NazgulName.DWAR));
|
||||
LOGGER.info("JI_INDUR={}", Nazgul.getInstance(NazgulName.JI_INDUR));
|
||||
LOGGER.info("AKHORAHIL={}", Nazgul.getInstance(NazgulName.AKHORAHIL));
|
||||
LOGGER.info("HOARMURATH={}", Nazgul.getInstance(NazgulName.HOARMURATH));
|
||||
LOGGER.info("ADUNAPHEL={}", Nazgul.getInstance(NazgulName.ADUNAPHEL));
|
||||
LOGGER.info("REN={}", Nazgul.getInstance(NazgulName.REN));
|
||||
LOGGER.info("UVATHA={}", Nazgul.getInstance(NazgulName.UVATHA));
|
||||
|
||||
// KHAMUL=com.iluwatar.multiton.Nazgul@2b214b94
|
||||
// MURAZOR=com.iluwatar.multiton.Nazgul@17814b1c
|
||||
// DWAR=com.iluwatar.multiton.Nazgul@7ac9af2a
|
||||
// JI_INDUR=com.iluwatar.multiton.Nazgul@7bb004b8
|
||||
// AKHORAHIL=com.iluwatar.multiton.Nazgul@78e89bfe
|
||||
// HOARMURATH=com.iluwatar.multiton.Nazgul@652ce654
|
||||
// ADUNAPHEL=com.iluwatar.multiton.Nazgul@522ba524
|
||||
// REN=com.iluwatar.multiton.Nazgul@29c5ee1d
|
||||
// UVATHA=com.iluwatar.multiton.Nazgul@15cea7b0
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
@ -18,6 +18,143 @@ implements the expected interface, but whose method body is empty. The
|
||||
advantage of this approach over a working default implementation is that a Null
|
||||
Object is very predictable and has no side effects: it does nothing.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> We are building a binary tree from nodes. There are ordinary nodes and "empty" nodes. Traversing the tree normally should not cause errors, so we use null object pattern where necessary.
|
||||
|
||||
In plain words
|
||||
|
||||
> Null Object pattern handles "empty" objects gracefully.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral ("null") behavior. The null object design pattern describes the uses of such objects and their behavior (or lack thereof).
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Here's the definitions for node interface and its implementations.
|
||||
|
||||
```java
|
||||
public interface Node {
|
||||
|
||||
String getName();
|
||||
|
||||
int getTreeSize();
|
||||
|
||||
Node getLeft();
|
||||
|
||||
Node getRight();
|
||||
|
||||
void walk();
|
||||
}
|
||||
|
||||
public class NodeImpl implements Node {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NodeImpl.class);
|
||||
|
||||
private final String name;
|
||||
private final Node left;
|
||||
private final Node right;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public NodeImpl(String name, Node left, Node right) {
|
||||
this.name = name;
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTreeSize() {
|
||||
return 1 + left.getTreeSize() + right.getTreeSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getLeft() {
|
||||
return left;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getRight() {
|
||||
return right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void walk() {
|
||||
LOGGER.info(name);
|
||||
if (left.getTreeSize() > 0) {
|
||||
left.walk();
|
||||
}
|
||||
if (right.getTreeSize() > 0) {
|
||||
right.walk();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class NullNode implements Node {
|
||||
|
||||
private static NullNode instance = new NullNode();
|
||||
|
||||
private NullNode() {
|
||||
}
|
||||
|
||||
public static NullNode getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTreeSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getLeft() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getRight() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void walk() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Then we can construct and traverse the binary tree without errors as follows.
|
||||
|
||||
```java
|
||||
Node root =
|
||||
new NodeImpl("1", new NodeImpl("11", new NodeImpl("111", NullNode.getInstance(),
|
||||
NullNode.getInstance()), NullNode.getInstance()), new NodeImpl("12",
|
||||
NullNode.getInstance(), new NodeImpl("122", NullNode.getInstance(),
|
||||
NullNode.getInstance())));
|
||||
root.walk();
|
||||
|
||||
// 1
|
||||
// 11
|
||||
// 111
|
||||
// 12
|
||||
// 122
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
@ -28,4 +165,5 @@ Use the Null Object pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Pattern Languages of Program Design](http://www.amazon.com/Pattern-Languages-Program-Design-Coplien/dp/0201607344/ref=sr_1_1)
|
||||
* [Pattern Languages of Program Design 3](https://www.amazon.com/gp/product/0201310112/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201310112&linkCode=as2&tag=javadesignpat-20&linkId=7372ffb8a4e39a3bb10f199b89aef921)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
@ -15,6 +15,104 @@ short periods of time it is advantageous to utilize the Object Pool pattern.
|
||||
The Object Pool provides a cache for instantiated objects tracking which ones
|
||||
are in use and which are available.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> In our war game we need to use oliphaunts, massive and mythic beasts, but the problem is that they are extremely expensive to create. The solution is to create a pool of them, track which ones are in-use, and instead of disposing them re-use the instances.
|
||||
|
||||
In plain words
|
||||
|
||||
> Object Pool manages a set of instances instead of creating and destroying them on demand.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> The object pool pattern is a software creational design pattern that uses a set of initialized objects kept ready to use – a "pool" – rather than allocating and destroying them on demand.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Here's the basic Oliphaunt class. These are very expensive to create.
|
||||
|
||||
```java
|
||||
public class Oliphaunt {
|
||||
|
||||
private static AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
private final int id;
|
||||
|
||||
public Oliphaunt() {
|
||||
id = counter.incrementAndGet();
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Oliphaunt id=%d", id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next we present the Object Pool and more specifically Oliphaunt Pool.
|
||||
|
||||
```java
|
||||
public abstract class ObjectPool<T> {
|
||||
|
||||
private Set<T> available = new HashSet<>();
|
||||
private Set<T> inUse = new HashSet<>();
|
||||
|
||||
protected abstract T create();
|
||||
|
||||
public synchronized T checkOut() {
|
||||
if (available.isEmpty()) {
|
||||
available.add(create());
|
||||
}
|
||||
var instance = available.iterator().next();
|
||||
available.remove(instance);
|
||||
inUse.add(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public synchronized void checkIn(T instance) {
|
||||
inUse.remove(instance);
|
||||
available.add(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return String.format("Pool available=%d inUse=%d", available.size(), inUse.size());
|
||||
}
|
||||
}
|
||||
|
||||
public class OliphauntPool extends ObjectPool<Oliphaunt> {
|
||||
|
||||
@Override
|
||||
protected Oliphaunt create() {
|
||||
return new Oliphaunt();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And finally here's how we utilize the pool.
|
||||
|
||||
```java
|
||||
var pool = new OliphauntPool();
|
||||
var oliphaunt1 = pool.checkOut();
|
||||
var oliphaunt2 = pool.checkOut();
|
||||
var oliphaunt3 = pool.checkOut();
|
||||
pool.checkIn(oliphaunt1);
|
||||
pool.checkIn(oliphaunt2);
|
||||
var oliphaunt4 = pool.checkOut();
|
||||
var oliphaunt5 = pool.checkOut();
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
|
@ -13,9 +13,149 @@ tags:
|
||||
Dependents, Publish-Subscribe
|
||||
|
||||
## Intent
|
||||
Define a one-to-many dependency between objects so that when one
|
||||
object changes state, all its dependents are notified and updated
|
||||
automatically.
|
||||
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified
|
||||
and updated automatically.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> In a land far away lives the races of hobbits and orcs. Both of them are mostly outdoors so they closely follow the changes in weather. One could say that they are constantly observing the weather.
|
||||
|
||||
In plain words
|
||||
|
||||
> Register as an observer to receive state changes in the object.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's first introduce the weather observer interface and our races, orcs and hobbits.
|
||||
|
||||
```java
|
||||
public interface WeatherObserver {
|
||||
|
||||
void update(WeatherType currentWeather);
|
||||
}
|
||||
|
||||
public class Orcs implements WeatherObserver {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Orcs.class);
|
||||
|
||||
@Override
|
||||
public void update(WeatherType currentWeather) {
|
||||
switch (currentWeather) {
|
||||
case COLD:
|
||||
LOGGER.info("The orcs are freezing cold.");
|
||||
break;
|
||||
case RAINY:
|
||||
LOGGER.info("The orcs are dripping wet.");
|
||||
break;
|
||||
case SUNNY:
|
||||
LOGGER.info("The sun hurts the orcs' eyes.");
|
||||
break;
|
||||
case WINDY:
|
||||
LOGGER.info("The orc smell almost vanishes in the wind.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Hobbits implements WeatherObserver {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Hobbits.class);
|
||||
|
||||
@Override
|
||||
public void update(WeatherType currentWeather) {
|
||||
switch (currentWeather) {
|
||||
case COLD:
|
||||
LOGGER.info("The hobbits are shivering in the cold weather.");
|
||||
break;
|
||||
case RAINY:
|
||||
LOGGER.info("The hobbits look for cover from the rain.");
|
||||
break;
|
||||
case SUNNY:
|
||||
LOGGER.info("The happy hobbits bade in the warm sun.");
|
||||
break;
|
||||
case WINDY:
|
||||
LOGGER.info("The hobbits hold their hats tightly in the windy weather.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then here's the weather that is constantly changing.
|
||||
|
||||
```java
|
||||
public class Weather {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Weather.class);
|
||||
|
||||
private WeatherType currentWeather;
|
||||
private List<WeatherObserver> observers;
|
||||
|
||||
public Weather() {
|
||||
observers = new ArrayList<>();
|
||||
currentWeather = WeatherType.SUNNY;
|
||||
}
|
||||
|
||||
public void addObserver(WeatherObserver obs) {
|
||||
observers.add(obs);
|
||||
}
|
||||
|
||||
public void removeObserver(WeatherObserver obs) {
|
||||
observers.remove(obs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes time pass for weather.
|
||||
*/
|
||||
public void timePasses() {
|
||||
var enumValues = WeatherType.values();
|
||||
currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length];
|
||||
LOGGER.info("The weather changed to {}.", currentWeather);
|
||||
notifyObservers();
|
||||
}
|
||||
|
||||
private void notifyObservers() {
|
||||
for (var obs : observers) {
|
||||
obs.update(currentWeather);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the full example in action.
|
||||
|
||||
```java
|
||||
var weather = new Weather();
|
||||
weather.addObserver(new Orcs());
|
||||
weather.addObserver(new Hobbits());
|
||||
|
||||
weather.timePasses();
|
||||
// The weather changed to rainy.
|
||||
// The orcs are dripping wet.
|
||||
// The hobbits look for cover from the rain.
|
||||
weather.timePasses();
|
||||
// The weather changed to windy.
|
||||
// The orc smell almost vanishes in the wind.
|
||||
// The hobbits hold their hats tightly in the windy weather.
|
||||
weather.timePasses();
|
||||
// The weather changed to cold.
|
||||
// The orcs are freezing cold.
|
||||
// The hobbits are shivering in the cold weather.
|
||||
weather.timePasses();
|
||||
// The weather changed to sunny.
|
||||
// The sun hurts the orcs' eyes.
|
||||
// The happy hobbits bade in the warm sun.
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -40,5 +180,7 @@ Use the Observer pattern in any of the following situations
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Java Generics and Collections](https://www.amazon.com/gp/product/0596527756/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596527756&linkCode=as2&tag=javadesignpat-20&linkId=246e5e2c26fe1c3ada6a70b15afcb195)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
@ -9,7 +9,83 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Allows processing of data in a series of stages by giving in an initial input and passing the processed output to be used by the next stages.
|
||||
Allows processing of data in a series of stages by giving in an initial input and passing the processed output to be
|
||||
used by the next stages.
|
||||
|
||||
## Explanation
|
||||
|
||||
The Pipeline pattern uses ordered stages to process a sequence of input values. Each implemented task is represented by
|
||||
a stage of the pipeline. You can think of pipelines as similar to assembly lines in a factory, where each item in the
|
||||
assembly line is constructed in stages. The partially assembled item is passed from one assembly stage to another. The
|
||||
outputs of the assembly line occur in the same order as that of the inputs.
|
||||
|
||||
Real world example
|
||||
|
||||
> Suppose we wanted to pass through a string to a series of filtering stages and convert it as a char array on the last stage.
|
||||
|
||||
In plain words
|
||||
|
||||
> Pipeline pattern is an assembly line where partial results are passed from one stage to another.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In software engineering, a pipeline consists of a chain of processing elements (processes, threads, coroutines, functions, etc.), arranged so that the output of each element is the input of the next; the name is by analogy to a physical pipeline.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
The stages of our pipeline are called `Handler`s.
|
||||
|
||||
```java
|
||||
interface Handler<I, O> {
|
||||
O process(I input);
|
||||
}
|
||||
```
|
||||
|
||||
In our string processing example we have 3 different concrete `Handler`s.
|
||||
|
||||
```java
|
||||
class RemoveAlphabetsHandler implements Handler<String, String> {
|
||||
...
|
||||
}
|
||||
|
||||
class RemoveDigitsHandler implements Handler<String, String> {
|
||||
...
|
||||
}
|
||||
|
||||
class ConvertToCharArrayHandler implements Handler<String, char[]> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Here is the `Pipeline` that will gather and execute the handlers one by one.
|
||||
|
||||
```java
|
||||
class Pipeline<I, O> {
|
||||
|
||||
private final Handler<I, O> currentHandler;
|
||||
|
||||
Pipeline(Handler<I, O> currentHandler) {
|
||||
this.currentHandler = currentHandler;
|
||||
}
|
||||
|
||||
<K> Pipeline<I, K> addHandler(Handler<O, K> newHandler) {
|
||||
return new Pipeline<>(input -> newHandler.process(currentHandler.process(input)));
|
||||
}
|
||||
|
||||
O execute(I input) {
|
||||
return currentHandler.process(input);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And here's the `Pipeline` in action processing the string.
|
||||
|
||||
```java
|
||||
var filters = new Pipeline<>(new RemoveAlphabetsHandler())
|
||||
.addHandler(new RemoveDigitsHandler())
|
||||
.addHandler(new ConvertToCharArrayHandler());
|
||||
filters.execute("GoYankees123!");
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -21,16 +97,16 @@ Use the Pipeline pattern when you want to
|
||||
* Add readability to complex sequence of operations by providing a fluent builder as an interface
|
||||
* Improve testability of code since stages will most likely be doing a single thing, complying to the [Single Responsibility Principle (SRP)](https://java-design-patterns.com/principles/#single-responsibility-principle)
|
||||
|
||||
## Typical Use Case
|
||||
|
||||
* Implement stages and execute them in an ordered manner
|
||||
|
||||
## Real world examples
|
||||
## Known uses
|
||||
|
||||
* [java.util.Stream](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html)
|
||||
* [Maven Build Lifecycle](http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html)
|
||||
* [Functional Java](https://github.com/functionaljava/functionaljava)
|
||||
|
||||
## Related patterns
|
||||
|
||||
* [Chain of Responsibility](https://java-design-patterns.com/patterns/chain/)
|
||||
|
||||
## Credits
|
||||
|
||||
* [The Pipeline Pattern — for fun and profit](https://medium.com/@aaronweatherall/the-pipeline-pattern-for-fun-and-profit-9b5f43a98130)
|
||||
|
@ -59,8 +59,9 @@ public class App {
|
||||
then is expected to receive an input of char[] array since that is the type being returned
|
||||
by the previous handler, ConvertToCharArrayHandler.
|
||||
*/
|
||||
new Pipeline<>(new RemoveAlphabetsHandler())
|
||||
var filters = new Pipeline<>(new RemoveAlphabetsHandler())
|
||||
.addHandler(new RemoveDigitsHandler())
|
||||
.addHandler(new ConvertToCharArrayHandler());
|
||||
filters.execute("GoYankees123!");
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,250 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Poison Pill is known predefined data item that allows to provide
|
||||
graceful shutdown for separate distributed consumption process.
|
||||
Poison Pill is known predefined data item that allows to provide graceful shutdown for separate distributed consumption
|
||||
process.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> Let's think about a message queue with one producer and one consumer. The producer keeps pushing new messages in the queue and the consumer keeps reading them. Finally when it's time to gracefully shut down the producer sends the poison pill message.
|
||||
|
||||
In plain words
|
||||
|
||||
> Poison Pill is a known message structure that ends the message exchange.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's define the message structure first.
|
||||
|
||||
```java
|
||||
public interface Message {
|
||||
|
||||
Message POISON_PILL = new Message() {
|
||||
|
||||
@Override
|
||||
public void addHeader(Headers header, String value) {
|
||||
throw poison();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(Headers header) {
|
||||
throw poison();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Headers, String> getHeaders() {
|
||||
throw poison();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBody(String body) {
|
||||
throw poison();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBody() {
|
||||
throw poison();
|
||||
}
|
||||
|
||||
private RuntimeException poison() {
|
||||
return new UnsupportedOperationException("Poison");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
enum Headers {
|
||||
DATE, SENDER
|
||||
}
|
||||
|
||||
void addHeader(Headers header, String value);
|
||||
|
||||
String getHeader(Headers header);
|
||||
|
||||
Map<Headers, String> getHeaders();
|
||||
|
||||
void setBody(String body);
|
||||
|
||||
String getBody();
|
||||
}
|
||||
|
||||
public class SimpleMessage implements Message {
|
||||
|
||||
private Map<Headers, String> headers = new HashMap<>();
|
||||
private String body;
|
||||
|
||||
@Override
|
||||
public void addHeader(Headers header, String value) {
|
||||
headers.put(header, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(Headers header) {
|
||||
return headers.get(header);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Headers, String> getHeaders() {
|
||||
return Collections.unmodifiableMap(headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next we define the types related to the message queue.
|
||||
|
||||
```java
|
||||
public interface MqPublishPoint {
|
||||
|
||||
void put(Message msg) throws InterruptedException;
|
||||
}
|
||||
|
||||
public interface MqSubscribePoint {
|
||||
|
||||
Message take() throws InterruptedException;
|
||||
}
|
||||
|
||||
public interface MessageQueue extends MqPublishPoint, MqSubscribePoint {
|
||||
}
|
||||
|
||||
public class SimpleMessageQueue implements MessageQueue {
|
||||
|
||||
private final BlockingQueue<Message> queue;
|
||||
|
||||
public SimpleMessageQueue(int bound) {
|
||||
queue = new ArrayBlockingQueue<>(bound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Message msg) throws InterruptedException {
|
||||
queue.put(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message take() throws InterruptedException {
|
||||
return queue.take();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we need to create the message producer and consumer.
|
||||
|
||||
```java
|
||||
public class Producer {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Producer.class);
|
||||
|
||||
private final MqPublishPoint queue;
|
||||
private final String name;
|
||||
private boolean isStopped;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public Producer(String name, MqPublishPoint queue) {
|
||||
this.name = name;
|
||||
this.queue = queue;
|
||||
this.isStopped = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message to queue.
|
||||
*/
|
||||
public void send(String body) {
|
||||
if (isStopped) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Producer %s was stopped and fail to deliver requested message [%s].", body, name));
|
||||
}
|
||||
var msg = new SimpleMessage();
|
||||
msg.addHeader(Headers.DATE, new Date().toString());
|
||||
msg.addHeader(Headers.SENDER, name);
|
||||
msg.setBody(body);
|
||||
|
||||
try {
|
||||
queue.put(msg);
|
||||
} catch (InterruptedException e) {
|
||||
// allow thread to exit
|
||||
LOGGER.error("Exception caught.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop system by sending poison pill.
|
||||
*/
|
||||
public void stop() {
|
||||
isStopped = true;
|
||||
try {
|
||||
queue.put(Message.POISON_PILL);
|
||||
} catch (InterruptedException e) {
|
||||
// allow thread to exit
|
||||
LOGGER.error("Exception caught.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Consumer {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Consumer.class);
|
||||
|
||||
private final MqSubscribePoint queue;
|
||||
private final String name;
|
||||
|
||||
public Consumer(String name, MqSubscribePoint queue) {
|
||||
this.name = name;
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume message.
|
||||
*/
|
||||
public void consume() {
|
||||
while (true) {
|
||||
try {
|
||||
var msg = queue.take();
|
||||
if (Message.POISON_PILL.equals(msg)) {
|
||||
LOGGER.info("Consumer {} receive request to terminate.", name);
|
||||
break;
|
||||
}
|
||||
var sender = msg.getHeader(Headers.SENDER);
|
||||
var body = msg.getBody();
|
||||
LOGGER.info("Message [{}] from [{}] received by [{}]", body, sender, name);
|
||||
} catch (InterruptedException e) {
|
||||
// allow thread to exit
|
||||
LOGGER.error("Exception caught.", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Finally we are ready to present the whole example in action.
|
||||
|
||||
```java
|
||||
var queue = new SimpleMessageQueue(10000);
|
||||
|
||||
final var producer = new Producer("PRODUCER_1", queue);
|
||||
final var consumer = new Consumer("CONSUMER_1", queue);
|
||||
|
||||
new Thread(consumer::consume).start();
|
||||
|
||||
new Thread(() -> {
|
||||
producer.send("hand shake");
|
||||
producer.send("some very important information");
|
||||
producer.send("bye!");
|
||||
producer.stop();
|
||||
}).start();
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
12
pom.xml
12
pom.xml
@ -54,6 +54,12 @@
|
||||
<annotation-api.version>1.3.2</annotation-api.version>
|
||||
<system-rules.version>1.19.0</system-rules.version>
|
||||
<urm.version>1.4.8</urm.version>
|
||||
<!-- SonarCloud -->
|
||||
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
||||
<sonar.organization>iluwatar</sonar.organization>
|
||||
<sonar.projectKey>iluwatar_java-design-patterns</sonar.projectKey>
|
||||
<sonar.moduleKey>${artifactId}</sonar.moduleKey>
|
||||
<sonar.projectName>Java Design Patterns</sonar.projectName>
|
||||
</properties>
|
||||
<modules>
|
||||
<module>abstract-factory</module>
|
||||
@ -184,6 +190,7 @@
|
||||
<module>combinator</module>
|
||||
<module>update-method</module>
|
||||
<module>leader-followers</module>
|
||||
<module>strangler</module>
|
||||
<module>arrange-act-assert</module>
|
||||
</modules>
|
||||
|
||||
@ -400,6 +407,11 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonarsource.scanner.maven</groupId>
|
||||
<artifactId>sonar-maven-plugin</artifactId>
|
||||
<version>3.7.0.1746</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
|
@ -6,6 +6,7 @@ permalink: /patterns/priority-queue/
|
||||
categories: Behavioral
|
||||
tags:
|
||||
- Decoupling
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Intent
|
||||
@ -18,12 +19,11 @@ Applications may delegate specific tasks to other services; for example, to perf
|
||||

|
||||
|
||||
## Applicability
|
||||
Use the Property pattern when
|
||||
Use the Priority Queue pattern when
|
||||
|
||||
* The system must handle multiple tasks that might have different priorities.
|
||||
* Different users or tenants should be served with different priority..
|
||||
|
||||
## Real world examples
|
||||
## Credits
|
||||
|
||||
* [ Priority Queue Pattern](https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn589794(v=pandp.10))
|
||||
Microsoft Azure does not provide a queuing mechanism that natively support automatic prioritization of messages through sorting. However, it does provide Azure Service Bus topics and subscriptions, which support a queuing mechanism that provides message filtering, together with a wide range of flexible capabilities that make it ideal for use in almost all priority queue implementations.
|
||||
* [Priority Queue pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/priority-queue)
|
||||
|
@ -9,18 +9,277 @@ tags:
|
||||
---
|
||||
|
||||
## Also known as
|
||||
|
||||
CompletableFuture
|
||||
|
||||
## Intent
|
||||
A Promise represents a proxy for a value not necessarily known when the promise is created. It
|
||||
allows you to associate dependent promises to an asynchronous action's eventual success value or
|
||||
failure reason. Promises are a way to write async code that still appears as though it is executing
|
||||
in a synchronous way.
|
||||
|
||||
A Promise represents a proxy for a value not necessarily known when the promise is created. It allows you to associate
|
||||
dependent promises to an asynchronous action's eventual success value or failure reason. Promises are a way to write
|
||||
async code that still appears as though it is executing in a synchronous way.
|
||||
|
||||
## Explanation
|
||||
|
||||
The Promise object is used for asynchronous computations. A Promise represents an operation that hasn't completed yet,
|
||||
but is expected in the future.
|
||||
|
||||
Promises provide a few advantages over callback objects:
|
||||
* Functional composition and error handling
|
||||
* Prevents callback hell and provides callback aggregation
|
||||
|
||||
Real world example
|
||||
|
||||
> We are developing a software solution that downloads files and calculates the number of lines and character
|
||||
frequencies in those files. Promise is an ideal solution to make the code concise and easy to understand.
|
||||
|
||||
In plain words
|
||||
|
||||
> Promise is a placeholder for an asynchronous operation that is ongoing.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In computer science, future, promise, delay, and deferred refer to constructs used for synchronizing program
|
||||
execution in some concurrent programming languages. They describe an object that acts as a proxy for a result that is
|
||||
initially unknown, usually because the computation of its value is not yet complete.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
In the example a file is downloaded and its line count is calculated. The calculated line count is then consumed and
|
||||
printed on console.
|
||||
|
||||
Let's first introduce a support class we need for implementation. Here's `PromiseSupport`.
|
||||
|
||||
```java
|
||||
class PromiseSupport<T> implements Future<T> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PromiseSupport.class);
|
||||
|
||||
private static final int RUNNING = 1;
|
||||
private static final int FAILED = 2;
|
||||
private static final int COMPLETED = 3;
|
||||
|
||||
private final Object lock;
|
||||
|
||||
private volatile int state = RUNNING;
|
||||
private T value;
|
||||
private Exception exception;
|
||||
|
||||
PromiseSupport() {
|
||||
this.lock = new Object();
|
||||
}
|
||||
|
||||
void fulfill(T value) {
|
||||
this.value = value;
|
||||
this.state = COMPLETED;
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
void fulfillExceptionally(Exception exception) {
|
||||
this.exception = exception;
|
||||
this.state = FAILED;
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return state > RUNNING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws InterruptedException, ExecutionException {
|
||||
synchronized (lock) {
|
||||
while (state == RUNNING) {
|
||||
lock.wait();
|
||||
}
|
||||
}
|
||||
if (state == COMPLETED) {
|
||||
return value;
|
||||
}
|
||||
throw new ExecutionException(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long timeout, TimeUnit unit) throws ExecutionException {
|
||||
synchronized (lock) {
|
||||
while (state == RUNNING) {
|
||||
try {
|
||||
lock.wait(unit.toMillis(timeout));
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.warn("Interrupted!", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == COMPLETED) {
|
||||
return value;
|
||||
}
|
||||
throw new ExecutionException(exception);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With `PromiseSupport` in place we can implement the actual `Promise`.
|
||||
|
||||
```java
|
||||
public class Promise<T> extends PromiseSupport<T> {
|
||||
|
||||
private Runnable fulfillmentAction;
|
||||
private Consumer<? super Throwable> exceptionHandler;
|
||||
|
||||
public Promise() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fulfill(T value) {
|
||||
super.fulfill(value);
|
||||
postFulfillment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fulfillExceptionally(Exception exception) {
|
||||
super.fulfillExceptionally(exception);
|
||||
handleException(exception);
|
||||
postFulfillment();
|
||||
}
|
||||
|
||||
private void handleException(Exception exception) {
|
||||
if (exceptionHandler == null) {
|
||||
return;
|
||||
}
|
||||
exceptionHandler.accept(exception);
|
||||
}
|
||||
|
||||
private void postFulfillment() {
|
||||
if (fulfillmentAction == null) {
|
||||
return;
|
||||
}
|
||||
fulfillmentAction.run();
|
||||
}
|
||||
|
||||
public Promise<T> fulfillInAsync(final Callable<T> task, Executor executor) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
fulfill(task.call());
|
||||
} catch (Exception ex) {
|
||||
fulfillExceptionally(ex);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public Promise<Void> thenAccept(Consumer<? super T> action) {
|
||||
var dest = new Promise<Void>();
|
||||
fulfillmentAction = new ConsumeAction(this, dest, action);
|
||||
return dest;
|
||||
}
|
||||
|
||||
public Promise<T> onError(Consumer<? super Throwable> exceptionHandler) {
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public <V> Promise<V> thenApply(Function<? super T, V> func) {
|
||||
Promise<V> dest = new Promise<>();
|
||||
fulfillmentAction = new TransformAction<V>(this, dest, func);
|
||||
return dest;
|
||||
}
|
||||
|
||||
private class ConsumeAction implements Runnable {
|
||||
|
||||
private final Promise<T> src;
|
||||
private final Promise<Void> dest;
|
||||
private final Consumer<? super T> action;
|
||||
|
||||
private ConsumeAction(Promise<T> src, Promise<Void> dest, Consumer<? super T> action) {
|
||||
this.src = src;
|
||||
this.dest = dest;
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
action.accept(src.get());
|
||||
dest.fulfill(null);
|
||||
} catch (Throwable throwable) {
|
||||
dest.fulfillExceptionally((Exception) throwable.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TransformAction<V> implements Runnable {
|
||||
|
||||
private final Promise<T> src;
|
||||
private final Promise<V> dest;
|
||||
private final Function<? super T, V> func;
|
||||
|
||||
private TransformAction(Promise<T> src, Promise<V> dest, Function<? super T, V> func) {
|
||||
this.src = src;
|
||||
this.dest = dest;
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
dest.fulfill(func.apply(src.get()));
|
||||
} catch (Throwable throwable) {
|
||||
dest.fulfillExceptionally((Exception) throwable.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we can show the full example in action. Here's how to download and count the number of lines in a file using
|
||||
`Promise`.
|
||||
|
||||
```java
|
||||
countLines().thenAccept(
|
||||
count -> {
|
||||
LOGGER.info("Line count is: {}", count);
|
||||
taskCompleted();
|
||||
}
|
||||
);
|
||||
|
||||
private Promise<Integer> countLines() {
|
||||
return download(DEFAULT_URL).thenApply(Utility::countLines);
|
||||
}
|
||||
|
||||
private Promise<String> download(String urlString) {
|
||||
return new Promise<String>()
|
||||
.fulfillInAsync(
|
||||
() -> Utility.downloadFile(urlString), executor)
|
||||
.onError(
|
||||
throwable -> {
|
||||
throwable.printStackTrace();
|
||||
taskCompleted();
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Class diagram
|
||||
|
||||

|
||||
|
||||
## Applicability
|
||||
|
||||
Promise pattern is applicable in concurrent programming when some work needs to be done asynchronously
|
||||
and:
|
||||
|
||||
@ -35,10 +294,17 @@ and:
|
||||
* [Guava ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained)
|
||||
|
||||
## Related Patterns
|
||||
* Async Method Invocation
|
||||
* Callback
|
||||
|
||||
* [Async Method Invocation](https://java-design-patterns.com/patterns/async-method-invocation/)
|
||||
* [Callback](https://java-design-patterns.com/patterns/callback/)
|
||||
|
||||
## Tutorials
|
||||
|
||||
* [Guide To CompletableFuture](https://www.baeldung.com/java-completablefuture)
|
||||
|
||||
## Credits
|
||||
|
||||
* [You are missing the point to Promises](https://gist.github.com/domenic/3889970)
|
||||
* [Functional style callbacks using CompletableFuture](https://www.infoq.com/articles/Functional-Style-Callbacks-Using-CompletableFuture)
|
||||
* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://www.amazon.com/gp/product/1617291994/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617291994&linkId=995af46887bb7b65e6c788a23eaf7146)
|
||||
* [Modern Java in Action: Lambdas, streams, functional and reactive programming](https://www.amazon.com/gp/product/1617293563/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617293563&linkId=f70fe0d3e1efaff89554a6479c53759c)
|
||||
|
@ -10,10 +10,13 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Specify the kinds of objects to create using a prototypical
|
||||
instance, and create new objects by copying this prototype.
|
||||
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
|
||||
|
||||
## Explanation
|
||||
|
||||
First it should be noted that Prototype pattern is not used to gain performance benefits. It's only used for creating
|
||||
new objects from prototype instance.
|
||||
|
||||
Real world example
|
||||
|
||||
> Remember Dolly? The sheep that was cloned! Lets not get into the details but the key point here is that it is all about cloning.
|
||||
@ -78,4 +81,5 @@ Use the Prototype pattern when a system should be independent of how its product
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
@ -140,4 +140,5 @@ are several common situations in which the Proxy pattern is applicable
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
@ -7,6 +7,7 @@ categories: Concurrency
|
||||
tags:
|
||||
- Decoupling
|
||||
- Performance
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Intent
|
||||
@ -32,4 +33,4 @@ for both the task and the service.
|
||||
|
||||
## Credits
|
||||
|
||||
* [Microsoft Cloud Design Patterns: Queue-Based Load Leveling Pattern](https://msdn.microsoft.com/en-us/library/dn589783.aspx)
|
||||
* [Queue-Based Load Leveling pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/queue-based-load-leveling)
|
||||
|
@ -30,6 +30,6 @@ Use Reactor pattern when
|
||||
## Credits
|
||||
|
||||
* [Douglas C. Schmidt - Reactor](https://www.dre.vanderbilt.edu/~schmidt/PDF/Reactor.pdf)
|
||||
* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697)
|
||||
* [Pattern Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://www.amazon.com/gp/product/0471606952/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0471606952&linkCode=as2&tag=javadesignpat-20&linkId=889e4af72dca8261129bf14935e0f8dc)
|
||||
* [Doug Lea - Scalable IO in Java](http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf)
|
||||
* [Netty](http://netty.io/)
|
||||
|
@ -9,11 +9,236 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Repository layer is added between the domain and data mapping
|
||||
layers to isolate domain objects from details of the database access code and
|
||||
to minimize scattering and duplication of query code. The Repository pattern is
|
||||
especially useful in systems where number of domain classes is large or heavy
|
||||
querying is utilized.
|
||||
Repository layer is added between the domain and data mapping layers to isolate domain objects from details of the
|
||||
database access code and to minimize scattering and duplication of query code. The Repository pattern is especially
|
||||
useful in systems where number of domain classes is large or heavy querying is utilized.
|
||||
|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> Let's say we need a persistent data store for persons. Adding new persons and searching for them according to different criteria must be easy.
|
||||
|
||||
In plain words
|
||||
|
||||
> Repository architectural pattern creates a uniform layer of data repositories that can be used for CRUD operations.
|
||||
|
||||
[Microsoft documentation](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-design) says
|
||||
|
||||
> Repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's first look at the person class that we need to persist.
|
||||
|
||||
```java
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
private String name;
|
||||
private String surname;
|
||||
|
||||
private int age;
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public Person(String name, String surname, int age) {
|
||||
this.name = name;
|
||||
this.surname = surname;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getSurname() {
|
||||
return surname;
|
||||
}
|
||||
|
||||
public void setSurname(String surname) {
|
||||
this.surname = surname;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person [id=" + id + ", name=" + name + ", surname=" + surname + ", age=" + age + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final var prime = 31;
|
||||
var result = 1;
|
||||
result = prime * result + age;
|
||||
result = prime * result + (id == null ? 0 : id.hashCode());
|
||||
result = prime * result + (name == null ? 0 : name.hashCode());
|
||||
result = prime * result + (surname == null ? 0 : surname.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
var other = (Person) obj;
|
||||
if (age != other.age) {
|
||||
return false;
|
||||
}
|
||||
if (id == null) {
|
||||
if (other.id != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!id.equals(other.id)) {
|
||||
return false;
|
||||
}
|
||||
if (name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
if (surname == null) {
|
||||
return other.surname == null;
|
||||
}
|
||||
return surname.equals(other.surname);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We are using Spring Data to create the repository so it becomes really simple.
|
||||
|
||||
```java
|
||||
@Repository
|
||||
public interface PersonRepository
|
||||
extends CrudRepository<Person, Long>, JpaSpecificationExecutor<Person> {
|
||||
|
||||
Person findByName(String name);
|
||||
}
|
||||
```
|
||||
|
||||
Additionally we define a helper class for specification queries.
|
||||
|
||||
```java
|
||||
public class PersonSpecifications {
|
||||
|
||||
public static class AgeBetweenSpec implements Specification<Person> {
|
||||
|
||||
private int from;
|
||||
|
||||
private int to;
|
||||
|
||||
public AgeBetweenSpec(int from, int to) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
|
||||
return cb.between(root.get("age"), from, to);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class NameEqualSpec implements Specification<Person> {
|
||||
|
||||
public String name;
|
||||
|
||||
public NameEqualSpec(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
|
||||
return cb.equal(root.get("name"), this.name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
And here's the repository in action.
|
||||
|
||||
```java
|
||||
var peter = new Person("Peter", "Sagan", 17);
|
||||
var nasta = new Person("Nasta", "Kuzminova", 25);
|
||||
var john = new Person("John", "lawrence", 35);
|
||||
var terry = new Person("Terry", "Law", 36);
|
||||
|
||||
repository.save(peter);
|
||||
repository.save(nasta);
|
||||
repository.save(john);
|
||||
repository.save(terry);
|
||||
|
||||
LOGGER.info("Count Person records: {}", repository.count());
|
||||
|
||||
var persons = (List<Person>) repository.findAll();
|
||||
persons.stream().map(Person::toString).forEach(LOGGER::info);
|
||||
|
||||
nasta.setName("Barbora");
|
||||
nasta.setSurname("Spotakova");
|
||||
repository.save(nasta);
|
||||
|
||||
repository.findById(2L).ifPresent(p -> LOGGER.info("Find by id 2: {}", p));
|
||||
repository.deleteById(2L);
|
||||
|
||||
LOGGER.info("Count Person records: {}", repository.count());
|
||||
|
||||
repository
|
||||
.findOne(new PersonSpecifications.NameEqualSpec("John"))
|
||||
.ifPresent(p -> LOGGER.info("Find by John is {}", p));
|
||||
|
||||
persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40));
|
||||
|
||||
LOGGER.info("Find Person with age between 20,40: ");
|
||||
persons.stream().map(Person::toString).forEach(LOGGER::info);
|
||||
|
||||
repository.deleteAll();
|
||||
|
||||
// Count Person records: 4
|
||||
// Person [id=1, name=Peter, surname=Sagan, age=17]
|
||||
// Person [id=2, name=Nasta, surname=Kuzminova, age=25]
|
||||
// Person [id=3, name=John, surname=lawrence, age=35]
|
||||
// Person [id=4, name=Terry, surname=Law, age=36]
|
||||
// Find by id 2: Person [id=2, name=Barbora, surname=Spotakova, age=25]
|
||||
// Count Person records: 3
|
||||
// Find by John is Person [id=3, name=John, surname=lawrence, age=35]
|
||||
// Find Person with age between 20,40:
|
||||
// Person [id=3, name=John, surname=lawrence, age=35]
|
||||
// Person [id=4, name=Terry, surname=Law, age=36]
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -35,3 +260,5 @@ Use the Repository pattern when
|
||||
* [Don’t use DAO, use Repository](http://thinkinginobjects.com/2012/08/26/dont-use-dao-use-repository/)
|
||||
* [Advanced Spring Data JPA - Specifications and Querydsl](https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/)
|
||||
* [Repository Pattern Benefits and Spring Implementation](https://stackoverflow.com/questions/40068965/repository-pattern-benefits-and-spring-implementation)
|
||||
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=d9f7d37b032ca6e96253562d075fcc4a)
|
||||
* [Design patterns that I often avoid: Repository pattern](https://www.infoworld.com/article/3117713/design-patterns-that-i-often-avoid-repository-pattern.html)
|
||||
|
@ -6,18 +6,15 @@ permalink: /patterns/retry/
|
||||
categories: Behavioral
|
||||
tags:
|
||||
- Performance
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Retry / resiliency
|
||||
Enables an application to handle transient failures from external resources.
|
||||
|
||||
## Intent
|
||||
Transparently retry certain operations that involve communication with external
|
||||
resources, particularly over the network, isolating calling code from the
|
||||
retry implementation details.
|
||||
Transparently retry certain operations that involve communication with external resources, particularly over the
|
||||
network, isolating calling code from the retry implementation details.
|
||||
|
||||
## Explanation
|
||||
The `Retry` pattern consists retrying operations on remote resources over the
|
||||
Retry pattern consists retrying operations on remote resources over the
|
||||
network a set number of times. It closely depends on both business and technical
|
||||
requirements: how much time will the business allow the end user to wait while
|
||||
the operation finishes? What are the performance characteristics of the
|
||||
@ -30,11 +27,7 @@ Another concern is the impact on the calling code by implementing the retry
|
||||
mechanism. The retry mechanics should ideally be completely transparent to the
|
||||
calling code (service interface remains unaltered). There are two general
|
||||
approaches to this problem: from an enterprise architecture standpoint
|
||||
(**strategic**), and a shared library standpoint (**tactical**).
|
||||
|
||||
*(As an aside, one interesting property is that, since implementations tend to
|
||||
be configurable at runtime, daily monitoring and operation of this capability
|
||||
is shifted over to operations support instead of the developers themselves.)*
|
||||
(strategic), and a shared library standpoint (tactical).
|
||||
|
||||
From a strategic point of view, this would be solved by having requests
|
||||
be redirected to a separate intermediary system, traditionally an
|
||||
@ -42,11 +35,26 @@ be redirected to a separate intermediary system, traditionally an
|
||||
a [Service Mesh](https://medium.com/microservices-in-practice/service-mesh-for-microservices-2953109a3c9a).
|
||||
|
||||
From a tactical point of view, this would be solved by reusing shared libraries
|
||||
like [Hystrix](https://github.com/Netflix/Hystrix)[1]. This is the type of
|
||||
solution showcased in the simple example that accompanies this *README*.
|
||||
like [Hystrix](https://github.com/Netflix/Hystrix) (please note that *Hystrix* is a complete implementation of
|
||||
the [Circuit Breaker](https://java-design-patterns.com/patterns/circuit-breaker/) pattern, of which the Retry pattern
|
||||
can be considered a subset of.). This is the type of solution showcased in the simple example that accompanies this
|
||||
*README*.
|
||||
|
||||
In our hypothetical application, we have a generic interface for all
|
||||
operations on remote interfaces:
|
||||
Real world example
|
||||
|
||||
> Our application uses a service providing customer information. Once in a while the service seems to be flaky and can return errors or sometimes it just times out. To circumvent these problems we apply the retry pattern.
|
||||
|
||||
In plain words
|
||||
|
||||
> Retry pattern transparently retries failed operations over network.
|
||||
|
||||
[Microsoft documentation](https://docs.microsoft.com/en-us/azure/architecture/patterns/retry) says
|
||||
|
||||
> Enable an application to handle transient failures when it tries to connect to a service or network resource, by transparently retrying a failed operation. This can improve the stability of the application.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
In our hypothetical application, we have a generic interface for all operations on remote interfaces.
|
||||
|
||||
```java
|
||||
public interface BusinessOperation<T> {
|
||||
@ -54,8 +62,7 @@ public interface BusinessOperation<T> {
|
||||
}
|
||||
```
|
||||
|
||||
And we have an implementation of this interface that finds our customers
|
||||
by looking up a database:
|
||||
And we have an implementation of this interface that finds our customers by looking up a database.
|
||||
|
||||
```java
|
||||
public final class FindCustomer implements BusinessOperation<String> {
|
||||
@ -122,20 +129,12 @@ more importantly we did *not* instruct our `Retry` to ignore, then the operation
|
||||
would have failed immediately upon receiving the error, not matter how many
|
||||
attempts were left.
|
||||
|
||||
<br/><br/>
|
||||
|
||||
[1] Please note that *Hystrix* is a complete implementation of the *Circuit
|
||||
Breaker* pattern, of which the *Retry* pattern can be considered a subset of.
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
## Applicability
|
||||
Whenever an application needs to communicate with an external resource,
|
||||
particularly in a cloud environment, and if the business requirements allow it.
|
||||
|
||||
## Presentations
|
||||
You can view Microsoft's article [here](https://docs.microsoft.com/en-us/azure/architecture/patterns/retry).
|
||||
Whenever an application needs to communicate with an external resource, particularly in a cloud environment, and if
|
||||
the business requirements allow it.
|
||||
|
||||
## Consequences
|
||||
**Pros:**
|
||||
@ -150,4 +149,9 @@ You can view Microsoft's article [here](https://docs.microsoft.com/en-us/azure/a
|
||||
|
||||
## Related Patterns
|
||||
|
||||
* [Circuit Breaker](https://martinfowler.com/bliki/CircuitBreaker.html)
|
||||
* [Circuit Breaker](https://java-design-patterns.com/patterns/circuit-breaker/)
|
||||
|
||||
## Credits
|
||||
|
||||
* [Retry pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/retry)
|
||||
* [Cloud Design Patterns: Prescriptive Architecture Guidance for Cloud Applications](https://www.amazon.com/gp/product/1621140369/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=1621140369&linkId=3e3f686af5e60a7a453b48adb286797b)
|
||||
|
@ -46,3 +46,4 @@ Use the Saga pattern, if:
|
||||
## Credits
|
||||
|
||||
- [Pattern: Saga](https://microservices.io/patterns/data/saga.html)
|
||||
- [Saga distributed transactions pattern](https://docs.microsoft.com/en-us/azure/architecture/reference-architectures/saga/saga)
|
||||
|
@ -9,12 +9,210 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Service Layer is an abstraction over domain logic. Typically
|
||||
applications require multiple kinds of interfaces to the data they store and
|
||||
logic they implement: data loaders, user interfaces, integration gateways, and
|
||||
others. Despite their different purposes, these interfaces often need common
|
||||
interactions with the application to access and manipulate its data and invoke
|
||||
its business logic. The Service Layer fulfills this role.
|
||||
|
||||
Service Layer is an abstraction over domain logic. It defines application's boundary with a layer of services that
|
||||
establishes a set of available operations and coordinates the application's response in each operation.
|
||||
|
||||
## Explanation
|
||||
|
||||
Typically applications require different kinds of interfaces to the data they store and the logic they implement.
|
||||
Despite their different purposes, these interfaces often need common interactions with the application to access and
|
||||
manipulate its data and invoke its business logic. Encoding the logic of the interactions separately in each module
|
||||
causes a lot of duplication. It's better to centralize building the business logic inside single Service Layer to avoid
|
||||
these pitfalls.
|
||||
|
||||
Real world example
|
||||
|
||||
> We are writing an application that tracks wizards, spellbooks and spells. Wizards may have spellbooks and spellbooks
|
||||
may have spells.
|
||||
|
||||
In plain words
|
||||
|
||||
> Service Layer is an abstraction over application's business logic.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> Service layer is an architectural pattern, applied within the service-orientation design paradigm, which aims to
|
||||
organize the services, within a service inventory, into a set of logical layers. Services that are categorized into
|
||||
a particular layer share functionality. This helps to reduce the conceptual overhead related to managing the service
|
||||
inventory, as the services belonging to the same layer address a smaller set of activities.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
The example application demonstrates interactions between a client `App` and a service `MagicService` that allows
|
||||
interaction between wizards, spellbooks and spells. The service is implemented with 3-layer architecture
|
||||
(entity, dao, service).
|
||||
|
||||
For this explanation we are looking at one vertical slice of the system. Let's start from the entity layer and look at
|
||||
`Wizard` class. Other entities not shown here are `Spellbook` and `Spell`.
|
||||
|
||||
```java
|
||||
@Entity
|
||||
@Table(name = "WIZARD")
|
||||
public class Wizard extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
@Column(name = "WIZARD_ID")
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToMany(cascade = CascadeType.ALL)
|
||||
private Set<Spellbook> spellbooks;
|
||||
|
||||
public Wizard() {
|
||||
spellbooks = new HashSet<>();
|
||||
}
|
||||
|
||||
public Wizard(String name) {
|
||||
this();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Spellbook> getSpellbooks() {
|
||||
return spellbooks;
|
||||
}
|
||||
|
||||
public void setSpellbooks(Set<Spellbook> spellbooks) {
|
||||
this.spellbooks = spellbooks;
|
||||
}
|
||||
|
||||
public void addSpellbook(Spellbook spellbook) {
|
||||
spellbook.getWizards().add(this);
|
||||
spellbooks.add(spellbook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Above the entity layer we have DAOs. For `Wizard` the DAO layer looks as follows.
|
||||
|
||||
```java
|
||||
public interface WizardDao extends Dao<Wizard> {
|
||||
|
||||
Wizard findByName(String name);
|
||||
}
|
||||
|
||||
public class WizardDaoImpl extends DaoBaseImpl<Wizard> implements WizardDao {
|
||||
|
||||
@Override
|
||||
public Wizard findByName(String name) {
|
||||
Transaction tx = null;
|
||||
Wizard result;
|
||||
try (var session = getSessionFactory().openSession()) {
|
||||
tx = session.beginTransaction();
|
||||
var criteria = session.createCriteria(persistentClass);
|
||||
criteria.add(Restrictions.eq("name", name));
|
||||
result = (Wizard) criteria.uniqueResult();
|
||||
tx.commit();
|
||||
} catch (Exception e) {
|
||||
if (tx != null) {
|
||||
tx.rollback();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next we can look at the Service Layer, which in our case consists of a single `MagicService`.
|
||||
|
||||
```java
|
||||
public interface MagicService {
|
||||
|
||||
List<Wizard> findAllWizards();
|
||||
|
||||
List<Spellbook> findAllSpellbooks();
|
||||
|
||||
List<Spell> findAllSpells();
|
||||
|
||||
List<Wizard> findWizardsWithSpellbook(String name);
|
||||
|
||||
List<Wizard> findWizardsWithSpell(String name);
|
||||
}
|
||||
|
||||
public class MagicServiceImpl implements MagicService {
|
||||
|
||||
private WizardDao wizardDao;
|
||||
private SpellbookDao spellbookDao;
|
||||
private SpellDao spellDao;
|
||||
|
||||
public MagicServiceImpl(WizardDao wizardDao, SpellbookDao spellbookDao, SpellDao spellDao) {
|
||||
this.wizardDao = wizardDao;
|
||||
this.spellbookDao = spellbookDao;
|
||||
this.spellDao = spellDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Wizard> findAllWizards() {
|
||||
return wizardDao.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Spellbook> findAllSpellbooks() {
|
||||
return spellbookDao.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Spell> findAllSpells() {
|
||||
return spellDao.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Wizard> findWizardsWithSpellbook(String name) {
|
||||
var spellbook = spellbookDao.findByName(name);
|
||||
return new ArrayList<>(spellbook.getWizards());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Wizard> findWizardsWithSpell(String name) {
|
||||
var spell = spellDao.findByName(name);
|
||||
var spellbook = spell.getSpellbook();
|
||||
return new ArrayList<>(spellbook.getWizards());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And finally we can show how the client `App` interacts with `MagicService` in the Service Layer.
|
||||
|
||||
```java
|
||||
var service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao);
|
||||
LOGGER.info("Enumerating all wizards");
|
||||
service.findAllWizards().stream().map(Wizard::getName).forEach(LOGGER::info);
|
||||
LOGGER.info("Enumerating all spellbooks");
|
||||
service.findAllSpellbooks().stream().map(Spellbook::getName).forEach(LOGGER::info);
|
||||
LOGGER.info("Enumerating all spells");
|
||||
service.findAllSpells().stream().map(Spell::getName).forEach(LOGGER::info);
|
||||
LOGGER.info("Find wizards with spellbook 'Book of Idores'");
|
||||
var wizardsWithSpellbook = service.findWizardsWithSpellbook("Book of Idores");
|
||||
wizardsWithSpellbook.forEach(w -> LOGGER.info("{} has 'Book of Idores'", w.getName()));
|
||||
LOGGER.info("Find wizards with spell 'Fireball'");
|
||||
var wizardsWithSpell = service.findWizardsWithSpell("Fireball");
|
||||
wizardsWithSpell.forEach(w -> LOGGER.info("{} has 'Fireball'", w.getName()));
|
||||
```
|
||||
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -28,4 +226,4 @@ Use the Service Layer pattern when
|
||||
## Credits
|
||||
|
||||
* [Martin Fowler - Service Layer](http://martinfowler.com/eaaCatalog/serviceLayer.html)
|
||||
* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)
|
||||
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=d9f7d37b032ca6e96253562d075fcc4a)
|
||||
|
@ -43,6 +43,7 @@
|
||||
<logger name="com.iluwatar" additivity="false">
|
||||
<level value="DEBUG"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</logger>
|
||||
|
||||
<logger name="org.hibernate" additivity="false">
|
||||
|
@ -40,4 +40,4 @@ to a number of services that they don't potentially need.
|
||||
|
||||
## Credits
|
||||
|
||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||
* [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)
|
||||
|
@ -26,4 +26,4 @@ This pattern offers the following benefits:
|
||||
|
||||
## Credits
|
||||
|
||||
* [Cloud Design Patterns: Prescriptive Architecture Guidance for Cloud Applications - Sharding Pattern](https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn589797(v=pandp.10)?redirectedfrom=MSDN)
|
||||
* [Sharding pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/sharding)
|
||||
|
@ -77,5 +77,7 @@ Use the Singleton pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
126
state/README.md
126
state/README.md
@ -12,8 +12,126 @@ tags:
|
||||
Objects for States
|
||||
|
||||
## Intent
|
||||
Allow an object to alter its behavior when its internal state
|
||||
changes. The object will appear to change its class.
|
||||
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
|
||||
|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> When observing a mammoth in its natural habitat it seems to change its behavior based on the situation. It may first appear calm but over time when it detects a threat it gets angry and dangerous to its surroundings.
|
||||
|
||||
In plain words
|
||||
|
||||
> State pattern allows an object to change its behavior.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> The state pattern is a behavioral software design pattern that allows an object to alter its behavior when its internal state changes. This pattern is close to the concept of finite-state machines. The state pattern can be interpreted as a strategy pattern, which is able to switch a strategy through invocations of methods defined in the pattern's interface.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Here is the state interface and its concrete implementations.
|
||||
|
||||
```java
|
||||
public interface State {
|
||||
|
||||
void onEnterState();
|
||||
|
||||
void observe();
|
||||
}
|
||||
|
||||
public class PeacefulState implements State {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PeacefulState.class);
|
||||
|
||||
private Mammoth mammoth;
|
||||
|
||||
public PeacefulState(Mammoth mammoth) {
|
||||
this.mammoth = mammoth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void observe() {
|
||||
LOGGER.info("{} is calm and peaceful.", mammoth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnterState() {
|
||||
LOGGER.info("{} calms down.", mammoth);
|
||||
}
|
||||
}
|
||||
|
||||
public class AngryState implements State {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AngryState.class);
|
||||
|
||||
private Mammoth mammoth;
|
||||
|
||||
public AngryState(Mammoth mammoth) {
|
||||
this.mammoth = mammoth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void observe() {
|
||||
LOGGER.info("{} is furious!", mammoth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnterState() {
|
||||
LOGGER.info("{} gets angry!", mammoth);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And here is the mammoth containing the state.
|
||||
|
||||
```java
|
||||
public class Mammoth {
|
||||
|
||||
private State state;
|
||||
|
||||
public Mammoth() {
|
||||
state = new PeacefulState(this);
|
||||
}
|
||||
|
||||
public void timePasses() {
|
||||
if (state.getClass().equals(PeacefulState.class)) {
|
||||
changeStateTo(new AngryState(this));
|
||||
} else {
|
||||
changeStateTo(new PeacefulState(this));
|
||||
}
|
||||
}
|
||||
|
||||
private void changeStateTo(State newState) {
|
||||
this.state = newState;
|
||||
this.state.onEnterState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "The mammoth";
|
||||
}
|
||||
|
||||
public void observe() {
|
||||
this.state.observe();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And here is the full example how the mammoth behaves over time.
|
||||
|
||||
```java
|
||||
var mammoth = new Mammoth();
|
||||
mammoth.observe();
|
||||
mammoth.timePasses();
|
||||
mammoth.observe();
|
||||
mammoth.timePasses();
|
||||
mammoth.observe();
|
||||
|
||||
// The mammoth gets angry!
|
||||
// The mammoth is furious!
|
||||
// The mammoth calms down.
|
||||
// The mammoth is calm and peaceful.
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -31,4 +149,6 @@ Use the State pattern in either of the following cases
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
30
strangler/README.md
Normal file
30
strangler/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Strangler
|
||||
folder: strangler
|
||||
permalink: /patterns/strangler/
|
||||
categories: Structural
|
||||
tags:
|
||||
- Extensibility
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Intent
|
||||
Incrementally migrate a legacy system by gradually replacing specific pieces of functionality
|
||||
with new applications and services. As features from the legacy system are replaced, the new
|
||||
system eventually covers all the old system's features and may has its own new features, then
|
||||
strangling the old system and allowing you to decommission it.
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
## Applicability
|
||||
This strangler pattern is a safe way to phase one thing out for something better, cheaper, or
|
||||
more expandable. Especially when you want to update legacy system with new techniques and need
|
||||
continuously develop new features at the same time. Note that this pattern indeed need extra effort,
|
||||
so usually use it when the system is not so simple.
|
||||
|
||||
## Credits
|
||||
|
||||
* [Strangler pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/strangler)
|
||||
* [Legacy Application Strangulation : Case Studies](https://paulhammant.com/2013/07/14/legacy-application-strangulation-case-studies/)
|
BIN
strangler/etc/strangler.png
Normal file
BIN
strangler/etc/strangler.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
61
strangler/etc/strangler.puml
Normal file
61
strangler/etc/strangler.puml
Normal file
@ -0,0 +1,61 @@
|
||||
@startuml
|
||||
|
||||
package com.iluwatar.strangler {
|
||||
class App {
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
|
||||
class OldArithmetic {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
- source : OldSource
|
||||
+ sum(nums : int...)
|
||||
+ mul(nums : int...)
|
||||
}
|
||||
|
||||
class HalfArithmetic {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
- oldSource : OldSource
|
||||
- newSource : HalfSource
|
||||
+ sum(nums : int...)
|
||||
+ mul(nums : int...)
|
||||
+ ifHasZero(nums : int...)
|
||||
}
|
||||
|
||||
class NewArithmetic {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
- source : NewSource
|
||||
+ sum(nums : int...)
|
||||
+ mul(nums : int...)
|
||||
+ ifHasZero(nums : int...)
|
||||
}
|
||||
|
||||
class OldSource {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
+ accumulateSum(nums : int...)
|
||||
+ accumulateMul(nums : int...)
|
||||
}
|
||||
|
||||
class HalfSource {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
+ accumulateSum(nums : int...)
|
||||
+ ifNonZero(nums : int...)
|
||||
}
|
||||
|
||||
class NewSource {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
+ accumulateSum(nums : int...)
|
||||
+ accumulateMul(nums : int...)
|
||||
+ ifNonZero(nums : int...)
|
||||
}
|
||||
}
|
||||
OldArithmetic o--> OldSource
|
||||
HalfArithmetic o--> OldSource
|
||||
HalfArithmetic o--> HalfSource
|
||||
NewArithmetic o--> NewSource
|
||||
@enduml
|
61
strangler/etc/strangler.urm.puml
Normal file
61
strangler/etc/strangler.urm.puml
Normal file
@ -0,0 +1,61 @@
|
||||
@startuml
|
||||
package com.iluwatar.strangler {
|
||||
class App {
|
||||
+ App()
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
class HalfArithmetic {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
- newSource : HalfSource
|
||||
- oldSource : OldSource
|
||||
+ HalfArithmetic(newSource : HalfSource, oldSource : OldSource)
|
||||
+ ifHasZero(nums : int[]) : boolean
|
||||
+ mul(nums : int[]) : int
|
||||
+ sum(nums : int[]) : int
|
||||
}
|
||||
class HalfSource {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
+ HalfSource()
|
||||
+ accumulateSum(nums : int[]) : int
|
||||
+ ifNonZero(nums : int[]) : boolean
|
||||
}
|
||||
class NewArithmetic {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
- source : NewSource
|
||||
+ NewArithmetic(source : NewSource)
|
||||
+ ifHasZero(nums : int[]) : boolean
|
||||
+ mul(nums : int[]) : int
|
||||
+ sum(nums : int[]) : int
|
||||
}
|
||||
class NewSource {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
+ NewSource()
|
||||
+ accumulateMul(nums : int[]) : int
|
||||
+ accumulateSum(nums : int[]) : int
|
||||
+ ifNonZero(nums : int[]) : boolean
|
||||
}
|
||||
class OldArithmetic {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
- source : OldSource
|
||||
+ OldArithmetic(source : OldSource)
|
||||
+ mul(nums : int[]) : int
|
||||
+ sum(nums : int[]) : int
|
||||
}
|
||||
class OldSource {
|
||||
- LOGGER : Logger {static}
|
||||
- VERSION : String {static}
|
||||
+ OldSource()
|
||||
+ accumulateMul(nums : int[]) : int
|
||||
+ accumulateSum(nums : int[]) : int
|
||||
}
|
||||
}
|
||||
OldArithmetic --> "-source" OldSource
|
||||
NewArithmetic --> "-source" NewSource
|
||||
HalfArithmetic --> "-newSource" HalfSource
|
||||
HalfArithmetic --> "-oldSource" OldSource
|
||||
@enduml
|
66
strangler/pom.xml
Normal file
66
strangler/pom.xml
Normal file
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
The MIT License
|
||||
Copyright © 2014-2019 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.
|
||||
|
||||
-->
|
||||
<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.23.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>strangler</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.strangler.App</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
69
strangler/src/main/java/com/iluwatar/strangler/App.java
Normal file
69
strangler/src/main/java/com/iluwatar/strangler/App.java
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>The Strangler pattern is a software design pattern that incrementally migrate a legacy
|
||||
* system by gradually replacing specific pieces of functionality with new applications and
|
||||
* services. As features from the legacy system are replaced, the new system eventually
|
||||
* replaces all of the old system's features, strangling the old system and allowing you
|
||||
* to decommission it.</p>
|
||||
*
|
||||
* <p>This pattern is not only about updating but also enhancement.</p>
|
||||
*
|
||||
* <p>In this example, {@link OldArithmetic} indicates old system and its implementation depends
|
||||
* on its source ({@link OldSource}). Now we tend to update system with new techniques and
|
||||
* new features. In reality, the system may too complex, so usually need gradual migration.
|
||||
* {@link HalfArithmetic} indicates system in the process of migration, its implementation
|
||||
* depends on old one ({@link OldSource}) and under development one ({@link HalfSource}). The
|
||||
* {@link HalfSource} covers part of {@link OldSource} and add new functionality. You can release
|
||||
* this version system with new features, which also supports old version system functionalities.
|
||||
* After whole migration, the new system ({@link NewArithmetic}) only depends on new source
|
||||
* ({@link NewSource}).</p>
|
||||
*
|
||||
*/
|
||||
public class App {
|
||||
/**
|
||||
* Program entry point.
|
||||
* @param args command line args
|
||||
*/
|
||||
public static void main(final String[] args) {
|
||||
final var nums = new int[]{1, 2, 3, 4, 5};
|
||||
//Before migration
|
||||
final var oldSystem = new OldArithmetic(new OldSource());
|
||||
oldSystem.sum(nums);
|
||||
oldSystem.mul(nums);
|
||||
//In process of migration
|
||||
final var halfSystem = new HalfArithmetic(new HalfSource(), new OldSource());
|
||||
halfSystem.sum(nums);
|
||||
halfSystem.mul(nums);
|
||||
halfSystem.ifHasZero(nums);
|
||||
//After migration
|
||||
final var newSystem = new NewArithmetic(new NewSource());
|
||||
newSystem.sum(nums);
|
||||
newSystem.mul(nums);
|
||||
newSystem.ifHasZero(nums);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* System under migration. Depends on old version source ({@link OldSource}) and
|
||||
* developing one ({@link HalfSource}).
|
||||
*/
|
||||
public class HalfArithmetic {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HalfArithmetic.class);
|
||||
private static final String VERSION = "1.5";
|
||||
|
||||
private final HalfSource newSource;
|
||||
private final OldSource oldSource;
|
||||
|
||||
public HalfArithmetic(HalfSource newSource, OldSource oldSource) {
|
||||
this.newSource = newSource;
|
||||
this.oldSource = oldSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulate sum.
|
||||
* @param nums numbers need to add together
|
||||
* @return accumulate sum
|
||||
*/
|
||||
public int sum(int... nums) {
|
||||
LOGGER.info("Arithmetic sum {}", VERSION);
|
||||
return newSource.accumulateSum(nums);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulate multiplication.
|
||||
* @param nums numbers need to multiply together
|
||||
* @return accumulate multiplication
|
||||
*/
|
||||
public int mul(int... nums) {
|
||||
LOGGER.info("Arithmetic mul {}", VERSION);
|
||||
return oldSource.accumulateMul(nums);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chech if has any zero.
|
||||
* @param nums numbers need to check
|
||||
* @return if has any zero, return true, else, return false
|
||||
*/
|
||||
public boolean ifHasZero(int... nums) {
|
||||
LOGGER.info("Arithmetic check zero {}", VERSION);
|
||||
return !newSource.ifNonZero(nums);
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Source under development. Replace part of old source and has added some new features.
|
||||
*/
|
||||
public class HalfSource {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HalfSource.class);
|
||||
private static final String VERSION = "1.5";
|
||||
|
||||
/**
|
||||
* Implement accumulate sum with new technique.
|
||||
* Replace old one in {@link OldSource}
|
||||
*/
|
||||
public int accumulateSum(int... nums) {
|
||||
LOGGER.info("Source module {}", VERSION);
|
||||
return Arrays.stream(nums).reduce(0, Integer::sum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all number is not zero.
|
||||
* New feature.
|
||||
*/
|
||||
public boolean ifNonZero(int... nums) {
|
||||
LOGGER.info("Source module {}", VERSION);
|
||||
return Arrays.stream(nums).allMatch(num -> num != 0);
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* System after whole migration. Only depends on new version source ({@link NewSource}).
|
||||
*/
|
||||
public class NewArithmetic {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NewArithmetic.class);
|
||||
private static final String VERSION = "2.0";
|
||||
|
||||
private final NewSource source;
|
||||
|
||||
public NewArithmetic(NewSource source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulate sum.
|
||||
* @param nums numbers need to add together
|
||||
* @return accumulate sum
|
||||
*/
|
||||
public int sum(int... nums) {
|
||||
LOGGER.info("Arithmetic sum {}", VERSION);
|
||||
return source.accumulateSum(nums);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulate multiplication.
|
||||
* @param nums numbers need to multiply together
|
||||
* @return accumulate multiplication
|
||||
*/
|
||||
public int mul(int... nums) {
|
||||
LOGGER.info("Arithmetic mul {}", VERSION);
|
||||
return source.accumulateMul(nums);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chech if has any zero.
|
||||
* @param nums numbers need to check
|
||||
* @return if has any zero, return true, else, return false
|
||||
*/
|
||||
public boolean ifHasZero(int... nums) {
|
||||
LOGGER.info("Arithmetic check zero {}", VERSION);
|
||||
return !source.ifNonZero(nums);
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* New source. Completely covers functionalities of old source with new techniques
|
||||
* and also has some new features.
|
||||
*/
|
||||
public class NewSource {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NewSource.class);
|
||||
private static final String VERSION = "2.0";
|
||||
|
||||
public int accumulateSum(int... nums) {
|
||||
LOGGER.info("Source module {}", VERSION);
|
||||
return Arrays.stream(nums).reduce(0, Integer::sum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement accumulate multiply with new technique.
|
||||
* Replace old one in {@link OldSource}
|
||||
*/
|
||||
public int accumulateMul(int... nums) {
|
||||
LOGGER.info("Source module {}", VERSION);
|
||||
return Arrays.stream(nums).reduce(1, (a, b) -> a * b);
|
||||
}
|
||||
|
||||
public boolean ifNonZero(int... nums) {
|
||||
LOGGER.info("Source module {}", VERSION);
|
||||
return Arrays.stream(nums).allMatch(num -> num != 0);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Old version system depends on old version source ({@link OldSource}).
|
||||
*/
|
||||
public class OldArithmetic {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(OldArithmetic.class);
|
||||
private static final String VERSION = "1.0";
|
||||
|
||||
private final OldSource source;
|
||||
|
||||
public OldArithmetic(OldSource source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulate sum.
|
||||
* @param nums numbers need to add together
|
||||
* @return accumulate sum
|
||||
*/
|
||||
public int sum(int... nums) {
|
||||
LOGGER.info("Arithmetic sum {}", VERSION);
|
||||
return source.accumulateSum(nums);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulate multiplication.
|
||||
* @param nums numbers need to multiply together
|
||||
* @return accumulate multiplication
|
||||
*/
|
||||
public int mul(int... nums) {
|
||||
LOGGER.info("Arithmetic mul {}", VERSION);
|
||||
return source.accumulateMul(nums);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Old source with techniques out of date.
|
||||
*/
|
||||
public class OldSource {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(OldSource.class);
|
||||
private static final String VERSION = "1.0";
|
||||
|
||||
/**
|
||||
* Implement accumulate sum with old technique.
|
||||
*/
|
||||
public int accumulateSum(int... nums) {
|
||||
LOGGER.info("Source module {}", VERSION);
|
||||
var sum = 0;
|
||||
for (final var num : nums) {
|
||||
sum += num;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement accumulate multiply with old technique.
|
||||
*/
|
||||
public int accumulateMul(int... nums) {
|
||||
LOGGER.info("Source module {}", VERSION);
|
||||
var sum = 1;
|
||||
for (final var num : nums) {
|
||||
sum *= num;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
36
strangler/src/test/java/com/iluwatar/strangler/AppTest.java
Normal file
36
strangler/src/test/java/com/iluwatar/strangler/AppTest.java
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Application test
|
||||
*/
|
||||
public class AppTest {
|
||||
@Test
|
||||
public void test() {
|
||||
App.main(new String[]{});
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Test methods in HalfArithmetic
|
||||
*/
|
||||
class HalfArithmeticTest {
|
||||
private static final HalfArithmetic arithmetic = new HalfArithmetic(new HalfSource(), new OldSource());
|
||||
|
||||
@Test
|
||||
public void testSum() {
|
||||
assertEquals(0, arithmetic.sum(-1, 0, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMul() {
|
||||
assertEquals(0, arithmetic.mul(-1, 0, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIfHasZero() {
|
||||
assertTrue(arithmetic.ifHasZero(-1, 0, 1));
|
||||
}
|
||||
}
|
@ -21,28 +21,27 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.callback;
|
||||
package com.iluwatar.strangler;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* This example generates the exact same output as {@link App} however the callback has been defined
|
||||
* as a Lambdas expression.
|
||||
* Test methods in HalfSource
|
||||
*/
|
||||
public final class LambdasApp {
|
||||
public class HalfSourceTest {
|
||||
private static final HalfSource source = new HalfSource();
|
||||
|
||||
private static final Logger LOGGER = getLogger(LambdasApp.class);
|
||||
|
||||
private LambdasApp() {
|
||||
@Test
|
||||
public void testAccumulateSum() {
|
||||
assertEquals(0, source.accumulateSum(-1, 0, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Program entry point.
|
||||
*/
|
||||
public static void main(final String[] args) {
|
||||
var task = new SimpleTask();
|
||||
task.executeWith(() -> LOGGER.info("I'm done now."));
|
||||
@Test
|
||||
public void testIfNonZero() {
|
||||
assertFalse(source.ifNonZero(-1, 0, 1));
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Test methods in NewArithmetic
|
||||
*/
|
||||
class NewArithmeticTest {
|
||||
private static final NewArithmetic arithmetic = new NewArithmetic(new NewSource());
|
||||
|
||||
@Test
|
||||
public void testSum() {
|
||||
assertEquals(0, arithmetic.sum(-1, 0, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMul() {
|
||||
assertEquals(0, arithmetic.mul(-1, 0, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIfHasZero() {
|
||||
assertTrue(arithmetic.ifHasZero(-1, 0, 1));
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
/**
|
||||
* Test methods in NewSource
|
||||
*/
|
||||
public class NewSourceTest {
|
||||
private static final NewSource source = new NewSource();
|
||||
|
||||
@Test
|
||||
public void testAccumulateSum() {
|
||||
assertEquals(0, source.accumulateSum(-1, 0, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccumulateMul() {
|
||||
assertEquals(0, source.accumulateMul(-1, 0, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIfNonZero() {
|
||||
assertFalse(source.ifNonZero(-1, 0, 1));
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Test methods in OldArithmetic
|
||||
*/
|
||||
class OldArithmeticTest {
|
||||
private static final OldArithmetic arithmetic = new OldArithmetic(new OldSource());
|
||||
|
||||
@Test
|
||||
public void testSum() {
|
||||
assertEquals(0, arithmetic.sum(-1, 0, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMul() {
|
||||
assertEquals(0, arithmetic.mul(-1, 0, 1));
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 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.strangler;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Test methods in OldSource
|
||||
*/
|
||||
public class OldSourceTest {
|
||||
private static final OldSource source = new OldSource();
|
||||
|
||||
@Test
|
||||
public void testAccumulateSum() {
|
||||
assertEquals(0, source.accumulateSum(-1, 0, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccumulateMul() {
|
||||
assertEquals(0, source.accumulateMul(-1, 0, 1));
|
||||
}
|
||||
}
|
@ -12,9 +12,106 @@ tags:
|
||||
Policy
|
||||
|
||||
## Intent
|
||||
Define a family of algorithms, encapsulate each one, and make them
|
||||
interchangeable. Strategy lets the algorithm vary independently from clients
|
||||
that use it.
|
||||
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary
|
||||
independently from clients that use it.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> Slaying dragons is a dangerous profession. With experience it becomes easier. Veteran dragonslayers have developed different fighting strategies against different types of dragons.
|
||||
|
||||
In plain words
|
||||
|
||||
> Strategy pattern allows choosing the best suited algorithm at runtime.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's first introduce the dragon slaying strategy interface and its implementations.
|
||||
|
||||
```java
|
||||
@FunctionalInterface
|
||||
public interface DragonSlayingStrategy {
|
||||
|
||||
void execute();
|
||||
}
|
||||
|
||||
public class MeleeStrategy implements DragonSlayingStrategy {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MeleeStrategy.class);
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
LOGGER.info("With your Excalibur you sever the dragon's head!");
|
||||
}
|
||||
}
|
||||
|
||||
public class ProjectileStrategy implements DragonSlayingStrategy {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ProjectileStrategy.class);
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
LOGGER.info("You shoot the dragon with the magical crossbow and it falls dead on the ground!");
|
||||
}
|
||||
}
|
||||
|
||||
public class SpellStrategy implements DragonSlayingStrategy {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SpellStrategy.class);
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
LOGGER.info("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And here is the mighty dragonslayer who is able to pick his fighting strategy based on the opponent.
|
||||
|
||||
```java
|
||||
public class DragonSlayer {
|
||||
|
||||
private DragonSlayingStrategy strategy;
|
||||
|
||||
public DragonSlayer(DragonSlayingStrategy strategy) {
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
public void changeStrategy(DragonSlayingStrategy strategy) {
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
public void goToBattle() {
|
||||
strategy.execute();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Finally here's dragonslayer in action.
|
||||
|
||||
```java
|
||||
LOGGER.info("Green dragon spotted ahead!");
|
||||
var dragonSlayer = new DragonSlayer(new MeleeStrategy());
|
||||
dragonSlayer.goToBattle();
|
||||
LOGGER.info("Red dragon emerges.");
|
||||
dragonSlayer.changeStrategy(new ProjectileStrategy());
|
||||
dragonSlayer.goToBattle();
|
||||
LOGGER.info("Black dragon lands before you.");
|
||||
dragonSlayer.changeStrategy(new SpellStrategy());
|
||||
dragonSlayer.goToBattle();
|
||||
|
||||
// Green dragon spotted ahead!
|
||||
// With your Excalibur you sever the dragon's head!
|
||||
// Red dragon emerges.
|
||||
// You shoot the dragon with the magical crossbow and it falls dead on the ground!
|
||||
// Black dragon lands before you.
|
||||
// You cast the spell of disintegration and the dragon vaporizes in a pile of dust!
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -33,5 +130,7 @@ Use the Strategy pattern when
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](https://www.amazon.com/gp/product/1937785467/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1937785467&linkCode=as2&tag=javadesignpat-20&linkId=7e4e2fb7a141631491534255252fd08b)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
@ -9,11 +9,115 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Define the skeleton of an algorithm in an operation, deferring some
|
||||
steps to subclasses. Template method lets subclasses redefine certain steps of
|
||||
an algorithm without changing the algorithm's structure.
|
||||
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets
|
||||
subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
|
||||
|
||||
To make sure that subclasses don’t override the template method, the template method should be declared `final`.
|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> The general steps in stealing an item are the same. First you pick the target, next you confuse him somehow and finally you steal the item. However there are many ways to implement these steps.
|
||||
|
||||
In plain words
|
||||
|
||||
> Template Method pattern outlines the general steps in the parent class and lets the concrete child implementations define the details.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In object-oriented programming, the template method is one of the behavioral design patterns identified by Gamma et al. in the book Design Patterns. The template method is a method in a superclass, usually an abstract superclass, and defines the skeleton of an operation in terms of a number of high-level steps. These steps are themselves implemented by additional helper methods in the same class as the template method.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's first introduce the template method class along with its concrete implementations.
|
||||
|
||||
```java
|
||||
public abstract class StealingMethod {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(StealingMethod.class);
|
||||
|
||||
protected abstract String pickTarget();
|
||||
|
||||
protected abstract void confuseTarget(String target);
|
||||
|
||||
protected abstract void stealTheItem(String target);
|
||||
|
||||
public void steal() {
|
||||
var target = pickTarget();
|
||||
LOGGER.info("The target has been chosen as {}.", target);
|
||||
confuseTarget(target);
|
||||
stealTheItem(target);
|
||||
}
|
||||
}
|
||||
|
||||
public class SubtleMethod extends StealingMethod {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SubtleMethod.class);
|
||||
|
||||
@Override
|
||||
protected String pickTarget() {
|
||||
return "shop keeper";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void confuseTarget(String target) {
|
||||
LOGGER.info("Approach the {} with tears running and hug him!", target);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stealTheItem(String target) {
|
||||
LOGGER.info("While in close contact grab the {}'s wallet.", target);
|
||||
}
|
||||
}
|
||||
|
||||
public class HitAndRunMethod extends StealingMethod {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HitAndRunMethod.class);
|
||||
|
||||
@Override
|
||||
protected String pickTarget() {
|
||||
return "old goblin woman";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void confuseTarget(String target) {
|
||||
LOGGER.info("Approach the {} from behind.", target);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stealTheItem(String target) {
|
||||
LOGGER.info("Grab the handbag and run away fast!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the halfling thief class containing the template method.
|
||||
|
||||
```java
|
||||
public class HalflingThief {
|
||||
|
||||
private StealingMethod method;
|
||||
|
||||
public HalflingThief(StealingMethod method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public void steal() {
|
||||
method.steal();
|
||||
}
|
||||
|
||||
public void changeMethod(StealingMethod method) {
|
||||
this.method = method;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And finally we show how the halfling thief utilizes the different stealing methods.
|
||||
|
||||
```java
|
||||
var thief = new HalflingThief(new HitAndRunMethod());
|
||||
thief.steal();
|
||||
thief.changeMethod(new SubtleMethod());
|
||||
thief.steal();
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -37,4 +141,6 @@ Method `GenericServlet.init(ServletConfig config)` is the template method in thi
|
||||
|
||||
## Credits
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
|
@ -15,6 +15,146 @@ the system spend more time creating and destroying the threads than executing
|
||||
the actual tasks. Thread Pool solves this problem by reusing existing threads
|
||||
and eliminating the latency of creating new threads.
|
||||
|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> We have a large number of relatively short tasks at hand. We need to peel huge amounts of potatoes and serve mighty amount of coffee cups. Creating a new thread for each task would be a waste so we establish a thread pool.
|
||||
|
||||
In plain words
|
||||
|
||||
> Thread Pool is a concurrency pattern where threads are allocated once and reused between tasks.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In computer programming, a thread pool is a software design pattern for achieving concurrency of execution in a computer program. Often also called a replicated workers or worker-crew model, a thread pool maintains multiple threads waiting for tasks to be allocated for concurrent execution by the supervising program. By maintaining a pool of threads, the model increases performance and avoids latency in execution due to frequent creation and destruction of threads for short-lived tasks. The number of available threads is tuned to the computing resources available to the program, such as a parallel task queue after completion of execution.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Let's first look at our task hierarchy. We have a base class and then concrete CoffeeMakingTask and PotatoPeelingTask.
|
||||
|
||||
```java
|
||||
public abstract class Task {
|
||||
|
||||
private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
|
||||
|
||||
private final int id;
|
||||
private final int timeMs;
|
||||
|
||||
public Task(final int timeMs) {
|
||||
this.id = ID_GENERATOR.incrementAndGet();
|
||||
this.timeMs = timeMs;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getTimeMs() {
|
||||
return timeMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("id=%d timeMs=%d", id, timeMs);
|
||||
}
|
||||
}
|
||||
|
||||
public class CoffeeMakingTask extends Task {
|
||||
|
||||
private static final int TIME_PER_CUP = 100;
|
||||
|
||||
public CoffeeMakingTask(int numCups) {
|
||||
super(numCups * TIME_PER_CUP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s %s", this.getClass().getSimpleName(), super.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public class PotatoPeelingTask extends Task {
|
||||
|
||||
private static final int TIME_PER_POTATO = 200;
|
||||
|
||||
public PotatoPeelingTask(int numPotatoes) {
|
||||
super(numPotatoes * TIME_PER_POTATO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s %s", this.getClass().getSimpleName(), super.toString());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next we present a runnable Worker class that the thread pool will utilize to handle all the potato peeling and coffee
|
||||
making.
|
||||
|
||||
```java
|
||||
public class Worker implements Runnable {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Worker.class);
|
||||
|
||||
private final Task task;
|
||||
|
||||
public Worker(final Task task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
LOGGER.info("{} processing {}", Thread.currentThread().getName(), task.toString());
|
||||
try {
|
||||
Thread.sleep(task.getTimeMs());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we are ready to show the full example in action.
|
||||
|
||||
```java
|
||||
LOGGER.info("Program started");
|
||||
|
||||
// Create a list of tasks to be executed
|
||||
var tasks = List.of(
|
||||
new PotatoPeelingTask(3),
|
||||
new PotatoPeelingTask(6),
|
||||
new CoffeeMakingTask(2),
|
||||
new CoffeeMakingTask(6),
|
||||
new PotatoPeelingTask(4),
|
||||
new CoffeeMakingTask(2),
|
||||
new PotatoPeelingTask(4),
|
||||
new CoffeeMakingTask(9),
|
||||
new PotatoPeelingTask(3),
|
||||
new CoffeeMakingTask(2),
|
||||
new PotatoPeelingTask(4),
|
||||
new CoffeeMakingTask(2),
|
||||
new CoffeeMakingTask(7),
|
||||
new PotatoPeelingTask(4),
|
||||
new PotatoPeelingTask(5));
|
||||
|
||||
// Creates a thread pool that reuses a fixed number of threads operating off a shared
|
||||
// unbounded queue. At any point, at most nThreads threads will be active processing
|
||||
// tasks. If additional tasks are submitted when all threads are active, they will wait
|
||||
// in the queue until a thread is available.
|
||||
var executor = Executors.newFixedThreadPool(3);
|
||||
|
||||
// Allocate new worker for each task
|
||||
// The worker is executed when a thread becomes
|
||||
// available in the thread pool
|
||||
tasks.stream().map(Worker::new).forEach(executor::execute);
|
||||
// All tasks were executed, now shutdown
|
||||
executor.shutdown();
|
||||
while (!executor.isTerminated()) {
|
||||
Thread.yield();
|
||||
}
|
||||
LOGGER.info("Program finished");
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
@ -22,3 +162,8 @@ and eliminating the latency of creating new threads.
|
||||
Use the Thread Pool pattern when
|
||||
|
||||
* You have a large number of short-lived tasks to be executed in parallel
|
||||
|
||||
## Credits
|
||||
|
||||
* [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0134685997&linkId=e1b9ddd5e669591642c4f30d40cd9f6b)
|
||||
* [Java Concurrency in Practice](https://www.amazon.com/gp/product/0321349601/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321349601&linkId=fbedb3bad3c6cbead5afa56eea39ed59)
|
||||
|
@ -6,11 +6,170 @@ permalink: /patterns/throttling/
|
||||
categories: Behavioral
|
||||
tags:
|
||||
- Performance
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Intent
|
||||
Ensure that a given client is not able to access service resources more than the assigned limit.
|
||||
|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> A large multinational corporation offers API to its customers. The API is rate-limited and each customer can only make certain amount of calls per second.
|
||||
|
||||
In plain words
|
||||
|
||||
> Throttling pattern is used to rate-limit access to a resource.
|
||||
|
||||
[Microsoft documentation](https://docs.microsoft.com/en-us/azure/architecture/patterns/throttling) says
|
||||
|
||||
> Control the consumption of resources used by an instance of an application, an individual tenant, or an entire service. This can allow the system to continue to function and meet service level agreements, even when an increase in demand places an extreme load on resources.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Tenant class presents the clients of the API. CallsCount tracks the number of API calls per tenant.
|
||||
|
||||
```java
|
||||
public class Tenant {
|
||||
|
||||
private String name;
|
||||
private int allowedCallsPerSecond;
|
||||
|
||||
public Tenant(String name, int allowedCallsPerSecond, CallsCount callsCount) {
|
||||
if (allowedCallsPerSecond < 0) {
|
||||
throw new InvalidParameterException("Number of calls less than 0 not allowed");
|
||||
}
|
||||
this.name = name;
|
||||
this.allowedCallsPerSecond = allowedCallsPerSecond;
|
||||
callsCount.addTenant(name);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getAllowedCallsPerSecond() {
|
||||
return allowedCallsPerSecond;
|
||||
}
|
||||
}
|
||||
|
||||
public final class CallsCount {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CallsCount.class);
|
||||
private Map<String, AtomicLong> tenantCallsCount = new ConcurrentHashMap<>();
|
||||
|
||||
public void addTenant(String tenantName) {
|
||||
tenantCallsCount.putIfAbsent(tenantName, new AtomicLong(0));
|
||||
}
|
||||
|
||||
public void incrementCount(String tenantName) {
|
||||
tenantCallsCount.get(tenantName).incrementAndGet();
|
||||
}
|
||||
|
||||
public long getCount(String tenantName) {
|
||||
return tenantCallsCount.get(tenantName).get();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
LOGGER.debug("Resetting the map.");
|
||||
tenantCallsCount.replaceAll((k, v) -> new AtomicLong(0));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next we introduce the service that the tenants are calling. To track the call count we use the throttler timer.
|
||||
|
||||
```java
|
||||
public interface Throttler {
|
||||
|
||||
void start();
|
||||
}
|
||||
|
||||
public class ThrottleTimerImpl implements Throttler {
|
||||
|
||||
private final int throttlePeriod;
|
||||
private final CallsCount callsCount;
|
||||
|
||||
public ThrottleTimerImpl(int throttlePeriod, CallsCount callsCount) {
|
||||
this.throttlePeriod = throttlePeriod;
|
||||
this.callsCount = callsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
new Timer(true).schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
callsCount.reset();
|
||||
}
|
||||
}, 0, throttlePeriod);
|
||||
}
|
||||
}
|
||||
|
||||
class B2BService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(B2BService.class);
|
||||
private final CallsCount callsCount;
|
||||
|
||||
public B2BService(Throttler timer, CallsCount callsCount) {
|
||||
this.callsCount = callsCount;
|
||||
timer.start();
|
||||
}
|
||||
|
||||
public int dummyCustomerApi(Tenant tenant) {
|
||||
var tenantName = tenant.getName();
|
||||
var count = callsCount.getCount(tenantName);
|
||||
LOGGER.debug("Counter for {} : {} ", tenant.getName(), count);
|
||||
if (count >= tenant.getAllowedCallsPerSecond()) {
|
||||
LOGGER.error("API access per second limit reached for: {}", tenantName);
|
||||
return -1;
|
||||
}
|
||||
callsCount.incrementCount(tenantName);
|
||||
return getRandomCustomerId();
|
||||
}
|
||||
|
||||
private int getRandomCustomerId() {
|
||||
return ThreadLocalRandom.current().nextInt(1, 10000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we are ready to see the full example in action. Tenant Adidas is rate-limited to 5 calls per second and Nike to 6.
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
var callsCount = new CallsCount();
|
||||
var adidas = new Tenant("Adidas", 5, callsCount);
|
||||
var nike = new Tenant("Nike", 6, callsCount);
|
||||
|
||||
var executorService = Executors.newFixedThreadPool(2);
|
||||
executorService.execute(() -> makeServiceCalls(adidas, callsCount));
|
||||
executorService.execute(() -> makeServiceCalls(nike, callsCount));
|
||||
executorService.shutdown();
|
||||
|
||||
try {
|
||||
executorService.awaitTermination(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.error("Executor Service terminated: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void makeServiceCalls(Tenant tenant, CallsCount callsCount) {
|
||||
var timer = new ThrottleTimerImpl(10, callsCount);
|
||||
var service = new B2BService(timer, callsCount);
|
||||
// Sleep is introduced to keep the output in check and easy to view and analyze the results.
|
||||
IntStream.range(0, 20).forEach(i -> {
|
||||
service.dummyCustomerApi(tenant);
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.error("Thread interrupted: {}", e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
@ -19,3 +178,8 @@ The Throttling pattern should be used:
|
||||
|
||||
* When a service access needs to be restricted to not have high impacts on the performance of the service.
|
||||
* When multiple clients are consuming the same service resources and restriction has to be made according to the usage per client.
|
||||
|
||||
## Credits
|
||||
|
||||
* [Throttling pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/throttling)
|
||||
* [Cloud Design Patterns: Prescriptive Architecture Guidance for Cloud Applications (Microsoft patterns & practices)](https://www.amazon.com/gp/product/B00ITGHBBS/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=B00ITGHBBS&linkId=12aacdd0cec04f372e7152689525631a)
|
||||
|
@ -9,10 +9,189 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Tolerant Reader is an integration pattern that helps creating
|
||||
robust communication systems. The idea is to be as tolerant as possible when
|
||||
reading data from another service. This way, when the communication schema
|
||||
changes, the readers must not break.
|
||||
Tolerant Reader is an integration pattern that helps creating robust communication systems. The idea is to be as
|
||||
tolerant as possible when reading data from another service. This way, when the communication schema changes, the
|
||||
readers must not break.
|
||||
|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> We are persisting rainbowfish objects to file and later on they need to be restored. What makes it problematic is that rainbowfish data structure is versioned and evolves over time. New version of rainbowfish needs to be able to restore old versions as well.
|
||||
|
||||
In plain words
|
||||
|
||||
> Tolerant Reader pattern is used to create robust communication mechanisms between services.
|
||||
|
||||
[Robustness Principle](https://java-design-patterns.com/principles/#robustness-principle) says
|
||||
|
||||
> Be conservative in what you do, be liberal in what you accept from others
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Here's the versioned rainbowfish. Notice how the second version introduces additional properties.
|
||||
|
||||
```java
|
||||
public class RainbowFish implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
private int lengthMeters;
|
||||
private int weightTons;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public RainbowFish(String name, int age, int lengthMeters, int weightTons) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.lengthMeters = lengthMeters;
|
||||
this.weightTons = weightTons;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public int getLengthMeters() {
|
||||
return lengthMeters;
|
||||
}
|
||||
|
||||
public int getWeightTons() {
|
||||
return weightTons;
|
||||
}
|
||||
}
|
||||
|
||||
public class RainbowFishV2 extends RainbowFish {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private boolean sleeping;
|
||||
private boolean hungry;
|
||||
private boolean angry;
|
||||
|
||||
public RainbowFishV2(String name, int age, int lengthMeters, int weightTons) {
|
||||
super(name, age, lengthMeters, weightTons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public RainbowFishV2(String name, int age, int lengthMeters, int weightTons, boolean sleeping,
|
||||
boolean hungry, boolean angry) {
|
||||
this(name, age, lengthMeters, weightTons);
|
||||
this.sleeping = sleeping;
|
||||
this.hungry = hungry;
|
||||
this.angry = angry;
|
||||
}
|
||||
|
||||
public boolean getSleeping() {
|
||||
return sleeping;
|
||||
}
|
||||
|
||||
public boolean getHungry() {
|
||||
return hungry;
|
||||
}
|
||||
|
||||
public boolean getAngry() {
|
||||
return angry;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next we introduce the rainbowfish serializer. This is the class that implements the Tolerant Reader pattern.
|
||||
|
||||
```java
|
||||
public final class RainbowFishSerializer {
|
||||
|
||||
private RainbowFishSerializer() {
|
||||
}
|
||||
|
||||
public static void writeV1(RainbowFish rainbowFish, String filename) throws IOException {
|
||||
var map = Map.of(
|
||||
"name", rainbowFish.getName(),
|
||||
"age", String.format("%d", rainbowFish.getAge()),
|
||||
"lengthMeters", String.format("%d", rainbowFish.getLengthMeters()),
|
||||
"weightTons", String.format("%d", rainbowFish.getWeightTons())
|
||||
);
|
||||
|
||||
try (var fileOut = new FileOutputStream(filename);
|
||||
var objOut = new ObjectOutputStream(fileOut)) {
|
||||
objOut.writeObject(map);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeV2(RainbowFishV2 rainbowFish, String filename) throws IOException {
|
||||
var map = Map.of(
|
||||
"name", rainbowFish.getName(),
|
||||
"age", String.format("%d", rainbowFish.getAge()),
|
||||
"lengthMeters", String.format("%d", rainbowFish.getLengthMeters()),
|
||||
"weightTons", String.format("%d", rainbowFish.getWeightTons()),
|
||||
"angry", Boolean.toString(rainbowFish.getAngry()),
|
||||
"hungry", Boolean.toString(rainbowFish.getHungry()),
|
||||
"sleeping", Boolean.toString(rainbowFish.getSleeping())
|
||||
);
|
||||
|
||||
try (var fileOut = new FileOutputStream(filename);
|
||||
var objOut = new ObjectOutputStream(fileOut)) {
|
||||
objOut.writeObject(map);
|
||||
}
|
||||
}
|
||||
|
||||
public static RainbowFish readV1(String filename) throws IOException, ClassNotFoundException {
|
||||
Map<String, String> map;
|
||||
|
||||
try (var fileIn = new FileInputStream(filename);
|
||||
var objIn = new ObjectInputStream(fileIn)) {
|
||||
map = (Map<String, String>) objIn.readObject();
|
||||
}
|
||||
|
||||
return new RainbowFish(
|
||||
map.get("name"),
|
||||
Integer.parseInt(map.get("age")),
|
||||
Integer.parseInt(map.get("lengthMeters")),
|
||||
Integer.parseInt(map.get("weightTons"))
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And finally here's the full example in action.
|
||||
|
||||
```java
|
||||
var fishV1 = new RainbowFish("Zed", 10, 11, 12);
|
||||
LOGGER.info("fishV1 name={} age={} length={} weight={}", fishV1.getName(),
|
||||
fishV1.getAge(), fishV1.getLengthMeters(), fishV1.getWeightTons());
|
||||
RainbowFishSerializer.writeV1(fishV1, "fish1.out");
|
||||
|
||||
var deserializedRainbowFishV1 = RainbowFishSerializer.readV1("fish1.out");
|
||||
LOGGER.info("deserializedFishV1 name={} age={} length={} weight={}",
|
||||
deserializedRainbowFishV1.getName(), deserializedRainbowFishV1.getAge(),
|
||||
deserializedRainbowFishV1.getLengthMeters(), deserializedRainbowFishV1.getWeightTons());
|
||||
|
||||
var fishV2 = new RainbowFishV2("Scar", 5, 12, 15, true, true, true);
|
||||
LOGGER.info(
|
||||
"fishV2 name={} age={} length={} weight={} sleeping={} hungry={} angry={}",
|
||||
fishV2.getName(), fishV2.getAge(), fishV2.getLengthMeters(), fishV2.getWeightTons(),
|
||||
fishV2.getHungry(), fishV2.getAngry(), fishV2.getSleeping());
|
||||
RainbowFishSerializer.writeV2(fishV2, "fish2.out");
|
||||
|
||||
var deserializedFishV2 = RainbowFishSerializer.readV1("fish2.out");
|
||||
LOGGER.info("deserializedFishV2 name={} age={} length={} weight={}",
|
||||
deserializedFishV2.getName(), deserializedFishV2.getAge(),
|
||||
deserializedFishV2.getLengthMeters(), deserializedFishV2.getWeightTons());
|
||||
|
||||
// fishV1 name=Zed age=10 length=11 weight=12
|
||||
// deserializedFishV1 name=Zed age=10 length=11 weight=12
|
||||
// fishV2 name=Scar age=5 length=12 weight=15 sleeping=true hungry=true angry=true
|
||||
// deserializedFishV2 name=Scar age=5 length=12 weight=15
|
||||
```
|
||||
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -25,3 +204,4 @@ Use the Tolerant Reader pattern when
|
||||
## Credits
|
||||
|
||||
* [Martin Fowler - Tolerant Reader](http://martinfowler.com/bliki/TolerantReader.html)
|
||||
* [Service Design Patterns: Fundamental Design Solutions for SOAP/WSDL and RESTful Web Services](https://www.amazon.com/gp/product/032154420X/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=032154420X&linkId=94f9516e747ac2b449a959d5b096c73c)
|
||||
|
@ -9,14 +9,111 @@ tags:
|
||||
---
|
||||
|
||||
## Intent
|
||||
Trampoline pattern is used for implementing algorithms recursively in Java without blowing the stack
|
||||
and to interleave the execution of functions without hard coding them together
|
||||
It is possible by representing a computation in one of 2 states : done | more
|
||||
(completed with result, or a reference to the reminder of the computation,
|
||||
something like the way a java.util.Supplier does).
|
||||
|
||||
Trampoline pattern is used for implementing algorithms recursively in Java without blowing the stack and to interleave
|
||||
the execution of functions without hard coding them together.
|
||||
|
||||
## Explanation
|
||||
Trampoline pattern allows to define recursive algorithms by iterative loop.
|
||||
|
||||
Recursion is a frequently adopted technique for solving algorithmic problems in a divide and conquer
|
||||
style. For example calculating fibonacci accumulating sum and factorials. In these kinds of problems recursion is
|
||||
more straightforward than their loop counterpart. Furthermore recursion may need less code and looks more concise.
|
||||
There is a saying that every recursion problem can be solved using a loop with the cost of writing code that is more
|
||||
difficult to understand.
|
||||
|
||||
However recursion type solutions have one big caveat. For each recursive call it typically needs an intermediate value
|
||||
stored and there is a limited amount of stack memory available. Running out of stack memory creates a stack overflow
|
||||
error and halts the program execution.
|
||||
|
||||
Trampoline pattern is a trick that allows us define recursive algorithms in Java without blowing the stack.
|
||||
|
||||
Real world example
|
||||
|
||||
> A recursive Fibonacci calculation without the stack overflow problem using the Trampoline pattern.
|
||||
|
||||
In plain words
|
||||
|
||||
> Trampoline pattern allows recursion without running out of stack memory.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In Java, trampoline refers to using reflection to avoid using inner classes, for example in event listeners. The time overhead of a reflection call is traded for the space overhead of an inner class. Trampolines in Java usually involve the creation of a GenericListener to pass events to an outer class.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Here's the `Trampoline` implementation in Java.
|
||||
|
||||
When `get` is called on the returned Trampoline, internally it will iterate calling `jump` on the returned `Trampoline`
|
||||
as long as the concrete instance returned is `Trampoline`, stopping once the returned instance is `done`.
|
||||
|
||||
```java
|
||||
public interface Trampoline<T> {
|
||||
|
||||
T get();
|
||||
|
||||
default Trampoline<T> jump() {
|
||||
return this;
|
||||
}
|
||||
|
||||
default T result() {
|
||||
return get();
|
||||
}
|
||||
|
||||
default boolean complete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static <T> Trampoline<T> done(final T result) {
|
||||
return () -> result;
|
||||
}
|
||||
|
||||
static <T> Trampoline<T> more(final Trampoline<Trampoline<T>> trampoline) {
|
||||
return new Trampoline<T>() {
|
||||
@Override
|
||||
public boolean complete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trampoline<T> jump() {
|
||||
return trampoline.result();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return trampoline(this);
|
||||
}
|
||||
|
||||
T trampoline(final Trampoline<T> trampoline) {
|
||||
return Stream.iterate(trampoline, Trampoline::jump)
|
||||
.filter(Trampoline::complete)
|
||||
.findFirst()
|
||||
.map(Trampoline::result)
|
||||
.orElseThrow();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Using the `Trampoline` to get Fibonacci values.
|
||||
|
||||
```java
|
||||
public static Trampoline<Integer> loop(int times, int prod) {
|
||||
if (times == 0) {
|
||||
return Trampoline.done(prod);
|
||||
} else {
|
||||
return Trampoline.more(() -> loop(times - 1, prod * times));
|
||||
}
|
||||
}
|
||||
|
||||
log.info("start pattern");
|
||||
var result = loop(10, 1).result();
|
||||
log.info("result {}", result);
|
||||
|
||||
// start pattern
|
||||
// result 3628800
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
@ -27,18 +124,16 @@ Use the Trampoline pattern when
|
||||
* For implementing tail recursive function. This pattern allows to switch on a stackless operation.
|
||||
* For interleaving the execution of two or more functions on the same thread.
|
||||
|
||||
## Known uses(real world examples)
|
||||
## Known uses
|
||||
|
||||
* Trampoline refers to using reflection to avoid using inner classes, for example in event listeners.
|
||||
The time overhead of a reflection call is traded for the space overhead of an inner class.
|
||||
Trampolines in Java usually involve the creation of a GenericListener to pass events to an outer class.
|
||||
|
||||
|
||||
## Tutorials
|
||||
|
||||
* [Trampolining: a practical guide for awesome Java Developers](https://medium.com/@johnmcclean/trampolining-a-practical-guide-for-awesome-java-developers-4b657d9c3076)
|
||||
* [Trampoline in java ](http://mindprod.com/jgloss/trampoline.html)
|
||||
* [cyclops-react](https://github.com/aol/cyclops-react)
|
||||
|
||||
## Credits
|
||||
|
||||
* [library 'cyclops-react' uses the pattern](https://github.com/aol/cyclops-react)
|
||||
* [Trampolining: a practical guide for awesome Java Developers](https://medium.com/@johnmcclean/trampolining-a-practical-guide-for-awesome-java-developers-4b657d9c3076)
|
||||
* [Trampoline in java ](http://mindprod.com/jgloss/trampoline.html)
|
||||
* [Laziness, trampolines, monoids and other functional amenities: this is not your father's Java](https://www.slideshare.net/mariofusco/lazine)
|
||||
* [Trampoline implementation](https://github.com/bodar/totallylazy/blob/master/src/com/googlecode/totallylazy/Trampoline.java)
|
||||
* [What is a trampoline function?](https://stackoverflow.com/questions/189725/what-is-a-trampoline-function)
|
||||
* [Modern Java in Action: Lambdas, streams, functional and reactive programming](https://www.amazon.com/gp/product/1617293563/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617293563&linkId=ad53ae6f9f7c0982e759c3527bd2595c)
|
||||
* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://www.amazon.com/gp/product/1617291994/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617291994&linkId=e3e5665b0732c59c9d884896ffe54f4f)
|
||||
|
@ -30,5 +30,5 @@ This pattern can be used when:
|
||||
|
||||
## Credits
|
||||
|
||||
* [Game Programming Patterns/Type Object](http://gameprogrammingpatterns.com/type-object.html) by Robert Nystrom
|
||||
* [http://www.cs.sjsu.edu/~pearce/modules/patterns/analysis/top.htm]
|
||||
* [Game Programming Patterns - Type Object](http://gameprogrammingpatterns.com/type-object.html)
|
||||
* [Types as Objects Pattern](http://www.cs.sjsu.edu/~pearce/modules/patterns/analysis/top.htm)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user