Compare commits

..

114 Commits

Author SHA1 Message Date
allcontributors[bot]
4b18e223cd docs: update .all-contributorsrc [skip ci] 2020-11-28 12:24:58 +00:00
allcontributors[bot]
13c6de036f docs: update README.md [skip ci] 2020-11-28 12:24:57 +00:00
Ilkka Seppälä
6f979d0cb2 Fix version number class diagram not showing 2020-11-24 08:10:34 +02:00
Ilkka Seppälä
f084f8bf41 Merge pull request #1599 from iluwatar/all-contributors/add-manannikov
docs: add manannikov as a contributor
2020-11-23 22:38:24 +02:00
allcontributors[bot]
6c95868b8d docs: update .all-contributorsrc [skip ci] 2020-11-23 20:37:49 +00:00
allcontributors[bot]
4c7f1b7822 docs: update README.md [skip ci] 2020-11-23 20:37:48 +00:00
Ilkka Seppälä
0c44b53909 Merge pull request #1563 from manannikov/Issue#1284
#1284 Implement Version Number pattern
2020-11-23 22:35:43 +02:00
Pavel Manannikov
9ead3adf73 #1284 Divide tests 2020-11-23 20:59:00 +02:00
Pavel Manannikov
97e3a3debc #1284 Use local variable inference 2020-11-23 19:42:50 +02:00
Ilkka Seppälä
43ed09015d Add missing license header 2020-11-22 21:14:53 +02:00
Ilkka Seppälä
7eee546208 Update README.md 2020-11-08 19:39:21 +02:00
Ilkka Seppälä
dc31960710 Remove unused logger objects 2020-11-08 19:37:15 +02:00
Ilkka Seppälä
7c0fdad5a2 Merge pull request #1531 from ravening/command-functional
Refactor the command pattern to use lambda functions
2020-11-08 19:33:48 +02:00
Ilkka Seppälä
5a8933ea17 Merge pull request #1534 from swarajsaaj/#1510-Circuit-Breaker-Refactoring
#1510 Improvements done in Circuit Breaker
2020-11-07 20:04:47 +02:00
Ilkka Seppälä
d02233f0b7 Merge pull request #1594 from iluwatar/all-contributors/add-akashchandwani
docs: add akashchandwani as a contributor
2020-11-07 14:36:54 +02:00
allcontributors[bot]
d42bcab9fc docs: update .all-contributorsrc [skip ci] 2020-11-07 12:34:36 +00:00
allcontributors[bot]
77b2ff2150 docs: update README.md [skip ci] 2020-11-07 12:34:35 +00:00
Ilkka Seppälä
cff072d1ca Merge pull request #1593 from iluwatar/all-contributors/add-dsibilio
docs: add dsibilio as a contributor
2020-11-07 14:34:08 +02:00
allcontributors[bot]
80ba0407db docs: update .all-contributorsrc [skip ci] 2020-11-07 12:33:11 +00:00
allcontributors[bot]
6a09b909f2 docs: update README.md [skip ci] 2020-11-07 12:33:10 +00:00
Ilkka Seppälä
53ccc0e7e6 Merge pull request #1548 from dsibilio/patch-1
Add missing word to README.md
2020-11-07 14:31:26 +02:00
Ilkka Seppälä
a7a8e23b01 Merge pull request #1591 from iluwatar/all-contributors/add-Ascenio
docs: add Ascenio as a contributor
2020-11-07 09:49:20 +02:00
allcontributors[bot]
2783251d00 docs: update .all-contributorsrc [skip ci] 2020-11-07 07:47:42 +00:00
allcontributors[bot]
ac432968ae docs: update README.md [skip ci] 2020-11-07 07:47:41 +00:00
Ilkka Seppälä
f006782805 Merge pull request #1529 from mookkiah/issue_1500
issue 1500 - using JUnit 5 and resolved a build issue
2020-10-17 18:02:56 +03:00
Pavel Manannikov
2332520d67 #1284 Implement Version Number pattern 2020-10-13 20:17:25 +03:00
Rakesh Venkatesh
af1b611136 code refactor 2020-10-13 17:36:59 +02:00
Rakesh Venkatesh
4ff196ce35 Refactor the command pattern to use lambda functions
We can leverage the lambda expressins of Java 8 onwards
to implement command design pattern instead of traditional
non functional way
2020-10-13 16:45:19 +02:00
swarajsaaj
242ae6a412 #1510 Update class diagram 2020-10-11 00:22:28 +05:30
swarajsaaj
b689fe0a26 #1510 Fix comment length 2020-10-11 00:11:06 +05:30
swarajsaaj
7aea765dd1 #1510 Fix review comments 2020-10-10 23:57:53 +05:30
swarajsaaj
ea49cbfe94 Merge branch 'master' into #1510-Circuit-Breaker-Refactoring 2020-10-10 22:19:46 +05:30
Ilkka Seppälä
1f4a412e70 Merge pull request #1535 from ohbus/master
Cache SonarCloud packages
2020-10-10 18:51:00 +03:00
Domenico Sibilio
36d0a3718c Update README.md
Missing word in sentence.
2020-10-06 18:24:47 +02:00
Ilkka Seppälä
195a735814 #508 fix SpaceStationMir logging 2020-10-04 14:50:48 +03:00
Ilkka Seppälä
633d45aa67 Fix typo 2020-10-04 14:33:01 +03:00
Ilkka Seppälä
adc267e48e Merge pull request #1526 from xiaod-dev/translation-zh
Translation zh
2020-10-04 14:26:39 +03:00
Ilkka Seppälä
cc8d209c22 Merge pull request #1545 from iluwatar/all-contributors/add-ChFlick
docs: add ChFlick as a contributor
2020-10-04 14:24:48 +03:00
allcontributors[bot]
578b3528d5 docs: update .all-contributorsrc [skip ci] 2020-10-04 11:23:50 +00:00
allcontributors[bot]
5674f3c52e docs: update README.md [skip ci] 2020-10-04 11:23:49 +00:00
Ilkka Seppälä
4ad2bfefad Merge pull request #1525 from ChFlick/patch-1
Fix links on the event sourcing page
2020-10-04 14:21:25 +03:00
Ilkka Seppälä
e5d219609d Add missing license headers 2020-10-04 14:17:28 +03:00
Ilkka Seppälä
21ca47a22e Fix build 2020-10-04 14:17:10 +03:00
Ilkka Seppälä
ebd38bcfaa Update license header 2020-10-04 14:09:36 +03:00
Subhrodip Mohanta
a3753807ae add Caching of SonarCloud packages 2020-10-01 22:23:43 +05:30
Subhrodip Mohanta
64266d63fd Merge pull request #1 from iluwatar/master
Update README.md
2020-10-01 22:18:53 +05:30
swarajsaaj
4f62070eb2 #1510 Revert pom to include all modules 2020-10-01 21:13:30 +05:30
swarajsaaj
b29bd66369 #1510 Improvments done in Circuit Breaker 2020-10-01 21:09:39 +05:30
Mahendran Mookkiah
ad435dd2fd issue 1500 - using Junit 5 and resolved a build issue 2020-09-27 09:26:19 -04:00
Ilkka Seppälä
a125879d15 Update README.md 2020-09-26 13:01:58 +03:00
Mike
dac8e659ce add facade pattern 2020-09-22 16:50:46 +08:00
Mike
a1515b3b52 finish builder and chain pattern 2020-09-22 11:13:25 +08:00
Christoph Flick
d2e070d21d Fix links on the event sourcing page 2020-09-16 13:44:50 +02:00
Ilkka Seppälä
82eb41b641 Update README.md 2020-09-13 18:35:23 +03:00
Ilkka Seppälä
24e8fa1bad Update README.md 2020-09-13 18:32:12 +03:00
Ilkka Seppälä
9017975276 Update README.md 2020-09-13 18:26:52 +03:00
Ilkka Seppälä
87093cf221 Update README.md 2020-09-13 18:23:54 +03:00
Ilkka Seppälä
6cb5b4a683 Update README.md 2020-09-13 18:19:35 +03:00
Ilkka Seppälä
e6ddff5f25 Update README.md 2020-09-13 18:15:17 +03:00
Ilkka Seppälä
b5fddd469a Update README.md 2020-09-13 18:09:41 +03:00
Ilkka Seppälä
9cbc509c3a Update README.md 2020-09-13 18:09:18 +03:00
Ilkka Seppälä
9c648cbdb8 Update README.md 2020-09-13 18:04:30 +03:00
Ilkka Seppälä
e6cca86e25 Update README.md 2020-09-13 17:59:12 +03:00
Ilkka Seppälä
c204b242df Update README.md 2020-09-13 17:53:50 +03:00
Ilkka Seppälä
2e98dcf217 Update README.md 2020-09-13 17:39:10 +03:00
Ilkka Seppälä
9d44eea64f Merge pull request #1524 from iluwatar/all-contributors/add-swarajsaaj
docs: add swarajsaaj as a contributor
2020-09-13 17:24:32 +03:00
allcontributors[bot]
6f592f5e8a docs: update .all-contributorsrc [skip ci] 2020-09-13 14:23:53 +00:00
allcontributors[bot]
74b968942f docs: update README.md [skip ci] 2020-09-13 14:23:52 +00:00
Ilkka Seppälä
ebc0e8b3cd Merge pull request #1522 from swarajsaaj/master
#1313 Add Separated Interface pattern
2020-09-13 17:21:28 +03:00
swarajsaaj
9088ac51f6 #1313 Rename DomesticTax,ForeignTax and review fixes 2020-09-12 22:35:40 +05:30
Ilkka Seppälä
c8f7a8f0e6 Merge pull request #1523 from iluwatar/all-contributors/add-vdlald
docs: add vdlald as a contributor
2020-09-12 20:02:32 +03:00
allcontributors[bot]
c63af2ccbf docs: update .all-contributorsrc [skip ci] 2020-09-12 17:00:59 +00:00
allcontributors[bot]
9f3f5322d2 docs: update README.md [skip ci] 2020-09-12 17:00:58 +00:00
Ilkka Seppälä
5607a4974c Merge pull request #1517 from vdlald/847
847
2020-09-12 19:57:29 +03:00
Mike
93aa1046aa translate decorator and factory method pattern in Chinese 2020-09-11 09:34:11 +08:00
swarajsaaj
a2967c5a40 #1313 Add documentation and license header 2020-09-10 03:22:00 +05:30
swarajsaaj
7fd7735527 #1313 Add separated-interface module to parent pom 2020-09-10 03:10:58 +05:30
swarajsaaj
f6942cf18d #1313 Add Separated Interface design pattern 2020-09-10 02:57:56 +05:30
Ilkka Seppälä
ef326ee77e Update README.md 2020-09-06 19:56:07 +03:00
Ilkka Seppälä
8b5f532a50 Update README.md 2020-09-06 19:55:38 +03:00
Ilkka Seppälä
a1da1e4973 Cleanup factory 2020-09-06 19:46:13 +03:00
Ilkka Seppälä
9d21dff855 Merge pull request #1520 from iluwatar/all-contributors/add-ravening
docs: add ravening as a contributor
2020-09-06 19:39:08 +03:00
Ilkka Seppälä
9d75592e8b Merge branch 'master' into all-contributors/add-ravening 2020-09-06 19:38:44 +03:00
allcontributors[bot]
8d6738b729 docs: update .all-contributorsrc [skip ci] 2020-09-06 16:37:04 +00:00
allcontributors[bot]
3205dc2cf0 docs: update README.md [skip ci] 2020-09-06 16:37:03 +00:00
Ilkka Seppälä
16e1863ae7 Merge pull request #1519 from iluwatar/all-contributors/add-samilAyoub
docs: add samilAyoub as a contributor
2020-09-06 19:36:35 +03:00
allcontributors[bot]
b9db3c4763 docs: update .all-contributorsrc [skip ci] 2020-09-06 16:35:58 +00:00
allcontributors[bot]
d72206ba72 docs: update README.md [skip ci] 2020-09-06 16:35:57 +00:00
Ilkka Seppälä
922c699e49 Merge pull request #1516 from samilAyoub/add-simple-factory
Add Simple Factory Pattern implementation
2020-09-06 19:33:03 +03:00
Vladislav Golubinov
bab48efd7c fix style 2020-09-06 12:01:48 +03:00
Vladislav Golubinov
29eecfd048 forgot to run the App 2020-09-06 11:52:16 +03:00
Vladislav Golubinov
87cf6b791c refactor 2020-09-06 11:48:40 +03:00
Vladislav Golubinov
2e36a11e24 remove lombok, related to #1503 2020-09-06 11:42:39 +03:00
Samil Ayoub
bf41b1d9c9 Updates README.md:
- Adding class diagram
- Adding Pros and Cons
- replace "" with ''
2020-09-05 18:39:28 +01:00
Samil Ayoub
b3ef214cd6 Change tabs to spaces in pom.xml 2020-09-04 22:02:19 +01:00
Samil Ayoub
6caf78e4e5 updates :
- Using lambda expression to create cars
- Using spaces instead of tabs in pom.xml
2020-09-04 21:21:51 +01:00
Vladislav Golubinov
bd48d6ce10 refactor 2020-09-04 17:31:50 +03:00
Samil Ayoub
8b26452c75 bug fixing 2020-09-03 22:58:15 +01:00
Samil Ayoub
2bb252e08f Clean the code 2020-09-03 22:41:55 +01:00
Samil Ayoub
a023cfbb1a Merge branch 'master' into add-simple-factory 2020-09-03 22:26:49 +01:00
Samil Ayoub
badf0c6b8c - README.md is added
- Change the name to factory is done
- Local variable type inference is used
2020-09-03 22:08:54 +01:00
Samil Ayoub
c9718a5227 add README file 2020-09-03 21:08:28 +01:00
Vladislav Golubinov
e89042a782 remove boilerplate code 2020-09-03 20:04:47 +03:00
Vladislav Golubinov
fb890e80dd refactor 2020-09-03 20:02:52 +03:00
Samil Ayoub
b423fd30d4 Fix bugs, clean the code and add unit tests. 2020-09-02 18:12:42 +01:00
Ilkka Seppälä
3df8472bf8 Merge pull request #1515 from fedorskvorcov/fix-typo
Remove unnecessary word from text
2020-09-02 19:59:07 +03:00
Samil Ayoub
ac98b31b68 Add Maven Assembly plugin to pom.xml 2020-09-02 14:09:44 +01:00
Samil Ayoub
46b23f322f Add Simple Factory Pattern implementation
Java source code demonstrate simple factory design pattern
2020-09-02 13:46:53 +01:00
Fedor
e231cd8d1a Remove unnecessary word from text 2020-09-02 12:32:02 +03:00
Ilkka Seppälä
19378f3fdd Update README.md 2020-09-01 20:25:39 +03:00
Ilkka Seppälä
3f4d637510 Update README.md 2020-09-01 20:18:10 +03:00
Ilkka Seppälä
25cca3547d Update README.md 2020-09-01 20:06:47 +03:00
Ilkka Seppälä
b9b6777d15 Update README.md 2020-09-01 19:55:22 +03:00
Ilkka Seppälä
2a5b8c977a Merge pull request #1514 from iluwatar/all-contributors/add-fedorskvorcov
docs: add fedorskvorcov as a contributor
2020-09-01 16:40:23 +03:00
126 changed files with 4342 additions and 1572 deletions

View File

@@ -1082,7 +1082,8 @@
"avatar_url": "https://avatars1.githubusercontent.com/u/10645273?v=4",
"profile": "https://github.com/ravening",
"contributions": [
"code"
"code",
"review"
]
},
{
@@ -1185,6 +1186,87 @@
"contributions": [
"code"
]
},
{
"login": "samilAyoub",
"name": "samilAyoub",
"avatar_url": "https://avatars0.githubusercontent.com/u/61546990?v=4",
"profile": "https://github.com/samilAyoub",
"contributions": [
"code"
]
},
{
"login": "vdlald",
"name": "Vladislav Golubinov",
"avatar_url": "https://avatars0.githubusercontent.com/u/29997701?v=4",
"profile": "https://github.com/vdlald",
"contributions": [
"code"
]
},
{
"login": "swarajsaaj",
"name": "Swaraj",
"avatar_url": "https://avatars2.githubusercontent.com/u/6285049?v=4",
"profile": "https://github.com/swarajsaaj",
"contributions": [
"code"
]
},
{
"login": "ChFlick",
"name": "Christoph Flick",
"avatar_url": "https://avatars0.githubusercontent.com/u/4465376?v=4",
"profile": "http://christophflick.de",
"contributions": [
"doc"
]
},
{
"login": "Ascenio",
"name": "Ascênio",
"avatar_url": "https://avatars1.githubusercontent.com/u/7662016?v=4",
"profile": "https://github.com/Ascenio",
"contributions": [
"review"
]
},
{
"login": "dsibilio",
"name": "Domenico Sibilio",
"avatar_url": "https://avatars2.githubusercontent.com/u/24280982?v=4",
"profile": "https://www.linkedin.com/in/domenico-sibilio/",
"contributions": [
"doc"
]
},
{
"login": "akashchandwani",
"name": "Akash Chandwani",
"avatar_url": "https://avatars2.githubusercontent.com/u/3483277?v=4",
"profile": "https://github.com/akashchandwani",
"contributions": [
"review"
]
},
{
"login": "manannikov",
"name": "Pavlo Manannikov",
"avatar_url": "https://avatars2.githubusercontent.com/u/7019769?v=4",
"profile": "http://www.linkedin.com/in/manannikov",
"contributions": [
"code"
]
},
{
"login": "eimanip",
"name": "Eiman",
"avatar_url": "https://avatars0.githubusercontent.com/u/20307301?v=4",
"profile": "https://github.com/eimanip",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 4,

View File

@@ -30,29 +30,43 @@ on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
# Disabling shallow clone for improving relevancy of SonarQube reporting
fetch-depth: 0
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Cache SonarCloud packages
uses: actions/cache@v2
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
# Some tests need screen access
- name: Install xvfb
run: sudo apt-get install -y xvfb
# The SonarQube analysis is only for the master branch of the main repository.
# SonarQube scan does not work for forked repositories try changing it to xvfb-run mvn clean verify
# See https://jira.sonarsource.com/browse/MMF-1371

View File

@@ -10,7 +10,7 @@
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=coverage)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
[![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-130-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-139-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
# Introduction
@@ -31,7 +31,7 @@ This site showcases Java Design Patterns. The solutions have been developed by
experienced programmers and architects from the open source community. The
patterns can be browsed by their high level descriptions or by looking at their
source code. The source code examples are well commented and can be thought as
programming tutorials how to implement a specific pattern. We use the most
programming tutorials on 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
@@ -246,7 +246,7 @@ This project is licensed under the terms of the MIT license.
<tr>
<td align="center"><a href="https://github.com/nishant"><img src="https://avatars2.githubusercontent.com/u/15331971?v=4" width="100px;" alt=""/><br /><sub><b>Nishant Arora</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nishant" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/raja-peeyush-kumar-singh"><img src="https://avatars0.githubusercontent.com/u/5496024?v=4" width="100px;" alt=""/><br /><sub><b>Peeyush</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=raja-peeyush-kumar-singh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ravening"><img src="https://avatars1.githubusercontent.com/u/10645273?v=4" width="100px;" alt=""/><br /><sub><b>Rakesh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ravening" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ravening"><img src="https://avatars1.githubusercontent.com/u/10645273?v=4" width="100px;" alt=""/><br /><sub><b>Rakesh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ravening" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aravening" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/vINCENT8888801"><img src="https://avatars0.githubusercontent.com/u/8037883?v=4" width="100px;" alt=""/><br /><sub><b>Wei Seng</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vINCENT8888801" title="Code">💻</a></td>
</tr>
<tr>
@@ -264,6 +264,19 @@ This project is licensed under the terms of the MIT license.
<tr>
<td align="center"><a href="https://www.stefan-birkner.de"><img src="https://avatars1.githubusercontent.com/u/711349?v=4" width="100px;" alt=""/><br /><sub><b>Stefan Birkner</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=stefanbirkner" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/fedorskvorcov"><img src="https://avatars3.githubusercontent.com/u/43882212?v=4" width="100px;" alt=""/><br /><sub><b>Fedor Skvorcov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fedorskvorcov" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/samilAyoub"><img src="https://avatars0.githubusercontent.com/u/61546990?v=4" width="100px;" alt=""/><br /><sub><b>samilAyoub</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=samilAyoub" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vdlald"><img src="https://avatars0.githubusercontent.com/u/29997701?v=4" width="100px;" alt=""/><br /><sub><b>Vladislav Golubinov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vdlald" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/swarajsaaj"><img src="https://avatars2.githubusercontent.com/u/6285049?v=4" width="100px;" alt=""/><br /><sub><b>Swaraj</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=swarajsaaj" title="Code">💻</a></td>
<td align="center"><a href="http://christophflick.de"><img src="https://avatars0.githubusercontent.com/u/4465376?v=4" width="100px;" alt=""/><br /><sub><b>Christoph Flick</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ChFlick" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Ascenio"><img src="https://avatars1.githubusercontent.com/u/7662016?v=4" width="100px;" alt=""/><br /><sub><b>Ascênio</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AAscenio" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://www.linkedin.com/in/domenico-sibilio/"><img src="https://avatars2.githubusercontent.com/u/24280982?v=4" width="100px;" alt=""/><br /><sub><b>Domenico Sibilio</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dsibilio" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/akashchandwani"><img src="https://avatars2.githubusercontent.com/u/3483277?v=4" width="100px;" alt=""/><br /><sub><b>Akash Chandwani</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aakashchandwani" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="http://www.linkedin.com/in/manannikov"><img src="https://avatars2.githubusercontent.com/u/7019769?v=4" width="100px;" alt=""/><br /><sub><b>Pavlo Manannikov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=manannikov" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/eimanip"><img src="https://avatars0.githubusercontent.com/u/20307301?v=4" width="100px;" alt=""/><br /><sub><b>Eiman</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=eimanip" title="Code">💻</a></td>
</tr>
</table>

View File

@@ -23,7 +23,6 @@
package com.iluwatar.abstractfactory;
import com.iluwatar.abstractfactory.App.FactoryMaker.KingdomType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,84 +40,14 @@ import org.slf4j.LoggerFactory;
* and its implementations ( {@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses
* both concrete implementations to create a king, a castle and an army.
*/
public class App {
public class App implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
private static Logger log = LoggerFactory.getLogger(App.class);
private King king;
private Castle castle;
private Army army;
private final Kingdom kingdom = new Kingdom();
/**
* Creates kingdom.
*/
public void createKingdom(final KingdomFactory factory) {
setKing(factory.createKing());
setCastle(factory.createCastle());
setArmy(factory.createArmy());
}
King getKing(final KingdomFactory factory) {
return factory.createKing();
}
public King getKing() {
return king;
}
private void setKing(final King king) {
this.king = king;
}
Castle getCastle(final KingdomFactory factory) {
return factory.createCastle();
}
public Castle getCastle() {
return castle;
}
private void setCastle(final Castle castle) {
this.castle = castle;
}
Army getArmy(final KingdomFactory factory) {
return factory.createArmy();
}
public Army getArmy() {
return army;
}
private void setArmy(final Army army) {
this.army = army;
}
/**
* The factory of kingdom factories.
*/
public static class FactoryMaker {
/**
* Enumeration for the different types of Kingdoms.
*/
public enum KingdomType {
ELF, ORC
}
/**
* The factory method to create KingdomFactory concrete objects.
*/
public static KingdomFactory makeFactory(KingdomType type) {
switch (type) {
case ELF:
return new ElfKingdomFactory();
case ORC:
return new OrcKingdomFactory();
default:
throw new IllegalArgumentException("KingdomType not supported.");
}
}
public Kingdom getKingdom() {
return kingdom;
}
/**
@@ -127,19 +56,33 @@ public class App {
* @param args command line args
*/
public static void main(String[] args) {
var app = new App();
app.run();
}
LOGGER.info("Elf Kingdom");
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
LOGGER.info(app.getArmy().getDescription());
LOGGER.info(app.getCastle().getDescription());
LOGGER.info(app.getKing().getDescription());
@Override
public void run() {
log.info("Elf Kingdom");
createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
log.info(kingdom.getArmy().getDescription());
log.info(kingdom.getCastle().getDescription());
log.info(kingdom.getKing().getDescription());
LOGGER.info("Orc Kingdom");
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
LOGGER.info(app.getArmy().getDescription());
LOGGER.info(app.getCastle().getDescription());
LOGGER.info(app.getKing().getDescription());
log.info("Orc Kingdom");
createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
log.info(kingdom.getArmy().getDescription());
log.info(kingdom.getCastle().getDescription());
log.info(kingdom.getKing().getDescription());
}
/**
* Creates kingdom.
* @param kingdomType type of Kingdom
*/
public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) {
final KingdomFactory kingdomFactory = Kingdom.FactoryMaker.makeFactory(kingdomType);
kingdom.setKing(kingdomFactory.createKing());
kingdom.setCastle(kingdomFactory.createCastle());
kingdom.setArmy(kingdomFactory.createArmy());
}
}

View File

@@ -0,0 +1,82 @@
/*
* 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.abstractfactory;
public class Kingdom {
private King king;
private Castle castle;
private Army army;
public King getKing() {
return king;
}
public Castle getCastle() {
return castle;
}
public Army getArmy() {
return army;
}
public void setKing(King king) {
this.king = king;
}
public void setCastle(Castle castle) {
this.castle = castle;
}
public void setArmy(Army army) {
this.army = army;
}
/**
* The factory of kingdom factories.
*/
public static class FactoryMaker {
/**
* Enumeration for the different types of Kingdoms.
*/
public enum KingdomType {
ELF, ORC
}
/**
* The factory method to create KingdomFactory concrete objects.
*/
public static KingdomFactory makeFactory(KingdomType type) {
switch (type) {
case ELF:
return new ElfKingdomFactory();
case ORC:
return new OrcKingdomFactory();
default:
throw new IllegalArgumentException("KingdomType not supported.");
}
}
}
}

View File

@@ -23,65 +23,71 @@
package com.iluwatar.abstractfactory;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.iluwatar.abstractfactory.App.FactoryMaker;
import com.iluwatar.abstractfactory.App.FactoryMaker.KingdomType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Test for abstract factory.
*/
public class AbstractFactoryTest {
private final App app = new App();
private KingdomFactory elfFactory;
private KingdomFactory orcFactory;
@BeforeEach
public void setUp() {
elfFactory = FactoryMaker.makeFactory(KingdomType.ELF);
orcFactory = FactoryMaker.makeFactory(KingdomType.ORC);
}
@Test
public void king() {
final var elfKing = app.getKing(elfFactory);
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
final var elfKing = kingdom.getKing();
assertTrue(elfKing instanceof ElfKing);
assertEquals(ElfKing.DESCRIPTION, elfKing.getDescription());
final var orcKing = app.getKing(orcFactory);
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
final var orcKing = kingdom.getKing();
assertTrue(orcKing instanceof OrcKing);
assertEquals(OrcKing.DESCRIPTION, orcKing.getDescription());
}
@Test
public void castle() {
final var elfCastle = app.getCastle(elfFactory);
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
final var elfCastle = kingdom.getCastle();
assertTrue(elfCastle instanceof ElfCastle);
assertEquals(ElfCastle.DESCRIPTION, elfCastle.getDescription());
final var orcCastle = app.getCastle(orcFactory);
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
final var orcCastle = kingdom.getCastle();
assertTrue(orcCastle instanceof OrcCastle);
assertEquals(OrcCastle.DESCRIPTION, orcCastle.getDescription());
}
@Test
public void army() {
final var elfArmy = app.getArmy(elfFactory);
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
final var elfArmy = kingdom.getArmy();
assertTrue(elfArmy instanceof ElfArmy);
assertEquals(ElfArmy.DESCRIPTION, elfArmy.getDescription());
final var orcArmy = app.getArmy(orcFactory);
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
final var orcArmy = kingdom.getArmy();
assertTrue(orcArmy instanceof OrcArmy);
assertEquals(OrcArmy.DESCRIPTION, orcArmy.getDescription());
}
@Test
public void createElfKingdom() {
app.createKingdom(elfFactory);
final var king = app.getKing();
final var castle = app.getCastle();
final var army = app.getArmy();
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
final var king = kingdom.getKing();
final var castle = kingdom.getCastle();
final var army = kingdom.getArmy();
assertTrue(king instanceof ElfKing);
assertEquals(ElfKing.DESCRIPTION, king.getDescription());
assertTrue(castle instanceof ElfCastle);
@@ -92,10 +98,12 @@ public class AbstractFactoryTest {
@Test
public void createOrcKingdom() {
app.createKingdom(orcFactory);
final var king = app.getKing();
final var castle = app.getCastle();
final var army = app.getArmy();
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
final var kingdom = app.getKingdom();
final var king = kingdom.getKing();
final var castle = kingdom.getCastle();
final var army = kingdom.getArmy();
assertTrue(king instanceof OrcKing);
assertEquals(OrcKing.DESCRIPTION, king.getDescription());
assertTrue(castle instanceof OrcCastle);

View File

@@ -19,12 +19,15 @@ cannot bring the whole application down, and we can reconnect to the service as
Real world example
> Imagine a web application that has both local files/images and remote database entries to serve.
> The database might not be responding due to a variety of reasons, so if the application keeps
> trying to read from the database using multiple threads/processes, soon all of them will hang
> causing our entire web application will crash. We should be able to detect this situation and show
> the user an appropriate message so that he/she can explore other parts of the app unaffected by
> the database failure.
> Imagine a web application that has both local files/images and remote services that are used for
> fetching data. These remote services may be either healthy and responsive at times, or may become
> slow and unresponsive at some point of time due to variety of reasons. So if one of the remote
> services is slow or not responding successfully, our application will try to fetch response from
> the remote service using multiple threads/processes, soon all of them will hang (also called
> [thread starvation](https://en.wikipedia.org/wiki/Starvation_(computer_science))) causing our entire web application to crash. We should be able to detect
> this situation and show the user an appropriate message so that he/she can explore other parts of
> the app unaffected by the remote service failure. Meanwhile, the other services that are working
> normally, should keep functioning unaffected by this failure.
In plain words
@@ -52,40 +55,104 @@ In terms of code, the end user application is:
```java
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
var obj = new MonitoringService();
var circuitBreaker = new CircuitBreaker(3000, 1, 2000 * 1000 * 1000);
var serverStartTime = System.nanoTime();
while (true) {
LOGGER.info(obj.localResourceResponse());
LOGGER.info(obj.remoteResourceResponse(circuitBreaker, serverStartTime));
LOGGER.info(circuitBreaker.getState());
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
LOGGER.error(e.getMessage());
}
var delayedService = new DelayedRemoteService(serverStartTime, 5);
var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2,
2000 * 1000 * 1000);
var quickService = new QuickRemoteService();
var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2,
2000 * 1000 * 1000);
//Create an object of monitoring service which makes both local and remote calls
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
quickServiceCircuitBreaker);
//Fetch response from local resource
LOGGER.info(monitoringService.localResourceResponse());
//Fetch response from delayed service 2 times, to meet the failure threshold
LOGGER.info(monitoringService.delayedServiceResponse());
LOGGER.info(monitoringService.delayedServiceResponse());
//Fetch current state of delayed service circuit breaker after crossing failure threshold limit
//which is OPEN now
LOGGER.info(delayedServiceCircuitBreaker.getState());
//Meanwhile, the delayed service is down, fetch response from the healthy quick service
LOGGER.info(monitoringService.quickServiceResponse());
LOGGER.info(quickServiceCircuitBreaker.getState());
//Wait for the delayed service to become responsive
try {
LOGGER.info("Waiting for delayed service to become responsive");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Check the state of delayed circuit breaker, should be HALF_OPEN
LOGGER.info(delayedServiceCircuitBreaker.getState());
//Fetch response from delayed service, which should be healthy by now
LOGGER.info(monitoringService.delayedServiceResponse());
//As successful response is fetched, it should be CLOSED again.
LOGGER.info(delayedServiceCircuitBreaker.getState());
}
}
```
The monitoring service:
``` java
```java
public class MonitoringService {
private final CircuitBreaker delayedService;
private final CircuitBreaker quickService;
public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) {
this.delayedService = delayedService;
this.quickService = quickService;
}
//Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic
public String localResourceResponse() {
return "Local Service is working";
}
public String remoteResourceResponse(CircuitBreaker circuitBreaker, long serverStartTime) {
/**
* Fetch response from the delayed service (with some simulated startup time).
*
* @return response string
*/
public String delayedServiceResponse() {
try {
return circuitBreaker.call("delayedService", serverStartTime);
} catch (Exception e) {
return this.delayedService.attemptRequest();
} catch (RemoteServiceException e) {
return e.getMessage();
}
}
/**
* Fetches response from a healthy service without any failure.
*
* @return response string
*/
public String quickServiceResponse() {
try {
return this.quickService.attemptRequest();
} catch (RemoteServiceException e) {
return e.getMessage();
}
}
@@ -95,72 +162,127 @@ As it can be seen, it does the call to get local resources directly, but it wrap
remote (costly) service in a circuit breaker object, which prevents faults as follows:
```java
public class CircuitBreaker {
public class DefaultCircuitBreaker implements CircuitBreaker {
private final long timeout;
private final long retryTimePeriod;
private final RemoteService service;
long lastFailureTime;
private String lastFailureResponse;
int failureCount;
private final int failureThreshold;
private State state;
private final long futureTime = 1000 * 1000 * 1000 * 1000;
CircuitBreaker(long timeout, int failureThreshold, long retryTimePeriod) {
/**
* Constructor to create an instance of Circuit Breaker.
*
* @param timeout Timeout for the API request. Not necessary for this simple example
* @param failureThreshold Number of failures we receive from the depended service before changing
* state to 'OPEN'
* @param retryTimePeriod Time period after which a new request is made to remote service for
* status check.
*/
DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold,
long retryTimePeriod) {
this.service = serviceToCall;
// We start in a closed state hoping that everything is fine
this.state = State.CLOSED;
this.failureThreshold = failureThreshold;
// Timeout for the API request.
// Used to break the calls made to remote resource if it exceeds the limit
this.timeout = timeout;
this.retryTimePeriod = retryTimePeriod;
//An absurd amount of time in future which basically indicates the last failure never happened
this.lastFailureTime = System.nanoTime() + futureTime;
this.failureCount = 0;
}
private void reset() {
// Reset everything to defaults
@Override
public void recordSuccess() {
this.failureCount = 0;
this.lastFailureTime = System.nanoTime() + futureTime;
this.lastFailureTime = System.nanoTime() + futureTime;
this.state = State.CLOSED;
}
private void recordFailure() {
@Override
public void recordFailure(String response) {
failureCount = failureCount + 1;
this.lastFailureTime = System.nanoTime();
// Cache the failure response for returning on open state
this.lastFailureResponse = response;
}
protected void setState() {
if (failureCount > failureThreshold) {
// Evaluate the current state based on failureThreshold, failureCount and lastFailureTime.
protected void evaluateState() {
if (failureCount >= failureThreshold) { //Then something is wrong with remote service
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
//We have waited long enough and should try checking if service is up
state = State.HALF_OPEN;
} else {
//Service would still probably be down
state = State.OPEN;
}
} else {
//Everything is working fine
state = State.CLOSED;
}
}
@Override
public String getState() {
evaluateState();
return state.name();
}
public void setStateForBypass(State state) {
/**
* Break the circuit beforehand if it is known service is down Or connect the circuit manually if
* service comes online before expected.
*
* @param state State at which circuit is in
*/
@Override
public void setState(State state) {
this.state = state;
switch (state) {
case OPEN:
this.failureCount = failureThreshold;
this.lastFailureTime = System.nanoTime();
break;
case HALF_OPEN:
this.failureCount = failureThreshold;
this.lastFailureTime = System.nanoTime() - retryTimePeriod;
break;
default:
this.failureCount = 0;
}
}
public String call(String serviceToCall, long serverStartTime) throws Exception {
setState();
/**
* Executes service call.
*
* @return Value from the remote resource, stale response or a custom exception
*/
@Override
public String attemptRequest() throws RemoteServiceException {
evaluateState();
if (state == State.OPEN) {
return "This is stale response from API";
// return cached response if the circuit is in OPEN state
return this.lastFailureResponse;
} else {
if (serviceToCall.equals("delayedService")) {
var delayedService = new DelayedService(20);
var response = delayedService.response(serverStartTime);
if (response.split(" ")[3].equals("working")) {
reset();
return response;
} else {
recordFailure();
throw new Exception("Remote service not responding");
}
} else {
throw new Exception("Unknown Service Name");
// Make the API request if the circuit is not OPEN
try {
//In a real application, this would be run in a thread and the timeout
//parameter of the circuit breaker would be utilized to know if service
//is working. Here, we simulate that based on server response itself
var response = service.call();
// Yay!! the API responded fine. Let's reset everything.
recordSuccess();
return response;
} catch (RemoteServiceException ex) {
recordFailure(ex.getMessage());
throw ex;
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -5,32 +5,52 @@ package com.iluwatar.circuitbreaker {
+ App()
+ main(args : String[]) {static}
}
class CircuitBreaker {
interface CircuitBreaker {
+ attemptRequest() : String {abstract}
+ getState() : String {abstract}
+ recordFailure(String) {abstract}
+ recordSuccess() {abstract}
+ setState(State) {abstract}
}
class DefaultCircuitBreaker {
~ failureCount : int
- failureThreshold : int
- futureTime : long
- lastFailureResponse : String
~ lastFailureTime : long
- retryTimePeriod : long
- service : RemoteService
- state : State
- timeout : long
~ CircuitBreaker(timeout : long, failureThreshold : int, retryTimePeriod : long)
+ call(serviceToCall : String, serverStartTime : long) : String
~ DefaultCircuitBreaker(serviceToCall : RemoteService, timeout : long, failureThreshold : int, retryTimePeriod : long)
+ attemptRequest() : String
# evaluateState()
+ getState() : String
- recordFailure()
- reset()
# setState()
+ setStateForBypass(state : State)
+ recordFailure(response : String)
+ recordSuccess()
+ setState(state : State)
}
class DelayedService {
class DelayedRemoteService {
- delay : int
+ DelayedService()
+ DelayedService(delay : int)
+ response(serverStartTime : long) : String
- serverStartTime : long
+ DelayedRemoteService()
+ DelayedRemoteService(serverStartTime : long, delay : int)
+ call() : String
}
class MonitoringService {
+ MonitoringService()
- delayedService : CircuitBreaker
- quickService : CircuitBreaker
+ MonitoringService(delayedService : CircuitBreaker, quickService : CircuitBreaker)
+ delayedServiceResponse() : String
+ localResourceResponse() : String
+ remoteResourceResponse(circuitBreaker : CircuitBreaker, serverStartTime : long) : String
+ quickServiceResponse() : String
}
class QuickRemoteService {
+ QuickRemoteService()
+ call() : String
}
interface RemoteService {
+ call() : String {abstract}
}
enum State {
+ CLOSED {static}
@@ -40,5 +60,10 @@ package com.iluwatar.circuitbreaker {
+ values() : State[] {static}
}
}
CircuitBreaker --> "-state" State
DefaultCircuitBreaker --> "-state" State
MonitoringService --> "-delayedService" CircuitBreaker
DefaultCircuitBreaker --> "-service" RemoteService
DefaultCircuitBreaker ..|> CircuitBreaker
DelayedRemoteService ..|> RemoteService
QuickRemoteService ..|> RemoteService
@enduml

View File

@@ -36,17 +36,18 @@ import org.slf4j.LoggerFactory;
* operational again, so that we can use it
* </p>
* <p>
* In this example, the circuit breaker pattern is demonstrated by using two services: {@link
* MonitoringService} and {@link DelayedService}. The monitoring service is responsible for calling
* two services: a local service and a remote service {@link DelayedService} , and by using the
* circuit breaker construction we ensure that if the call to remote service is going to fail, we
* are going to save our resources and not make the function call at all, by wrapping our call to
* the remote service in the circuit breaker object.
* In this example, the circuit breaker pattern is demonstrated by using three services: {@link
* DelayedRemoteService}, {@link QuickRemoteService} and {@link MonitoringService}. The monitoring
* service is responsible for calling three services: a local service, a quick remove service
* {@link QuickRemoteService} and a delayed remote service {@link DelayedRemoteService} , and by
* using the circuit breaker construction we ensure that if the call to remote service is going to
* fail, we are going to save our resources and not make the function call at all, by wrapping our
* call to the remote services in the {@link DefaultCircuitBreaker} implementation object.
* </p>
* <p>
* This works as follows: The {@link CircuitBreaker} object can be in one of three states:
* <b>Open</b>, <b>Closed</b> and <b>Half-Open</b>, which represents the real world circuits. If the
* state is closed (initial), we assume everything is alright and perform the function call.
* This works as follows: The {@link DefaultCircuitBreaker} object can be in one of three states:
* <b>Open</b>, <b>Closed</b> and <b>Half-Open</b>, which represents the real world circuits. If
* the state is closed (initial), we assume everything is alright and perform the function call.
* However, every time the call fails, we note it and once it crosses a threshold, we set the state
* to Open, preventing any further calls to the remote server. Then, after a certain retry period
* (during which we expect thee service to recover), we make another call to the remote server and
@@ -63,22 +64,50 @@ public class App {
*
* @param args command line args
*/
@SuppressWarnings("squid:S2189")
public static void main(String[] args) {
//Create an object of monitoring service which makes both local and remote calls
var obj = new MonitoringService();
//Set the circuit Breaker parameters
var circuitBreaker = new CircuitBreaker(3000, 1, 2000 * 1000 * 1000);
var serverStartTime = System.nanoTime();
while (true) {
LOGGER.info(obj.localResourceResponse());
LOGGER.info(obj.remoteResourceResponse(circuitBreaker, serverStartTime));
LOGGER.info(circuitBreaker.getState());
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
LOGGER.error(e.getMessage());
}
var delayedService = new DelayedRemoteService(serverStartTime, 5);
var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2,
2000 * 1000 * 1000);
var quickService = new QuickRemoteService();
var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2,
2000 * 1000 * 1000);
//Create an object of monitoring service which makes both local and remote calls
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
quickServiceCircuitBreaker);
//Fetch response from local resource
LOGGER.info(monitoringService.localResourceResponse());
//Fetch response from delayed service 2 times, to meet the failure threshold
LOGGER.info(monitoringService.delayedServiceResponse());
LOGGER.info(monitoringService.delayedServiceResponse());
//Fetch current state of delayed service circuit breaker after crossing failure threshold limit
//which is OPEN now
LOGGER.info(delayedServiceCircuitBreaker.getState());
//Meanwhile, the delayed service is down, fetch response from the healthy quick service
LOGGER.info(monitoringService.quickServiceResponse());
LOGGER.info(quickServiceCircuitBreaker.getState());
//Wait for the delayed service to become responsive
try {
LOGGER.info("Waiting for delayed service to become responsive");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Check the state of delayed circuit breaker, should be HALF_OPEN
LOGGER.info(delayedServiceCircuitBreaker.getState());
//Fetch response from delayed service, which should be healthy by now
LOGGER.info(monitoringService.delayedServiceResponse());
//As successful response is fetched, it should be CLOSED again.
LOGGER.info(delayedServiceCircuitBreaker.getState());
}
}

View File

@@ -24,114 +24,22 @@
package com.iluwatar.circuitbreaker;
/**
* The circuit breaker class with all configurations.
* The Circuit breaker interface.
*/
public class CircuitBreaker {
private final long timeout;
private final long retryTimePeriod;
long lastFailureTime;
int failureCount;
private final int failureThreshold;
private State state;
private final long futureTime = 1000 * 1000 * 1000 * 1000;
public interface CircuitBreaker {
/**
* Constructor to create an instance of Circuit Breaker.
*
* @param timeout Timeout for the API request. Not necessary for this simple example
* @param failureThreshold Number of failures we receive from the depended service before changing
* state to 'OPEN'
* @param retryTimePeriod Time period after which a new request is made to remote service for
* status check.
*/
CircuitBreaker(long timeout, int failureThreshold, long retryTimePeriod) {
// We start in a closed state hoping that everything is fine
this.state = State.CLOSED;
this.failureThreshold = failureThreshold;
// Timeout for the API request.
// Used to break the calls made to remote resource if it exceeds the limit
this.timeout = timeout;
this.retryTimePeriod = retryTimePeriod;
//An absurd amount of time in future which basically indicates the last failure never happened
this.lastFailureTime = System.nanoTime() + futureTime;
this.failureCount = 0;
}
// Success response. Reset everything to defaults
void recordSuccess();
//Reset everything to defaults
private void reset() {
this.failureCount = 0;
this.lastFailureTime = System.nanoTime() + futureTime;
this.state = State.CLOSED;
}
// Failure response. Handle accordingly with response and change state if required.
void recordFailure(String response);
private void recordFailure() {
failureCount = failureCount + 1;
this.lastFailureTime = System.nanoTime();
}
// Get the current state of circuit breaker
String getState();
protected void setState() {
if (failureCount > failureThreshold) { //Then something is wrong with remote service
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
//We have waited long enough and should try checking if service is up
state = State.HALF_OPEN;
} else {
//Service would still probably be down
state = State.OPEN;
}
} else {
//Everything is working fine
state = State.CLOSED;
}
}
// Set the specific state manually.
void setState(State state);
public String getState() {
return state.name();
}
/**
* Break the circuit beforehand if it is known service is down Or connect the circuit manually if
* service comes online before expected.
*
* @param state State at which circuit is in
*/
public void setStateForBypass(State state) {
this.state = state;
}
/**
* Executes service call.
*
* @param serviceToCall The name of the service in String. Can be changed to data URLs in case
* of web applications
* @param serverStartTime Time at which actual server was started which makes calls to this
* service
* @return Value from the remote resource, stale response or a custom exception
*/
public String call(String serviceToCall, long serverStartTime) throws Exception {
setState();
if (state == State.OPEN) {
// return cached response if no the circuit is in OPEN state
return "This is stale response from API";
} else {
// Make the API request if the circuit is not OPEN
if (serviceToCall.equals("delayedService")) {
var delayedService = new DelayedService(20);
var response = delayedService.response(serverStartTime);
//In a real application, this would be run in a thread and the timeout
//parameter of the circuit breaker would be utilized to know if service
//is working. Here, we simulate that based on server response itself
if (response.split(" ")[3].equals("working")) {
// Yay!! the API responded fine. Let's reset everything.
reset();
return response;
} else {
// Uh-oh!! the call still failed. Let's update that in our records.
recordFailure();
throw new Exception("Remote service not responding");
}
} else {
throw new Exception("Unknown Service Name");
}
}
}
}
// Attempt to fetch response from the remote service.
String attemptRequest() throws RemoteServiceException;
}

View File

@@ -0,0 +1,155 @@
/*
* 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.circuitbreaker;
/**
* The delay based Circuit breaker implementation that works in a
* CLOSED->OPEN-(retry_time_period)->HALF_OPEN->CLOSED flow with some retry time period for failed
* services and a failure threshold for service to open circuit.
*/
public class DefaultCircuitBreaker implements CircuitBreaker {
private final long timeout;
private final long retryTimePeriod;
private final RemoteService service;
long lastFailureTime;
private String lastFailureResponse;
int failureCount;
private final int failureThreshold;
private State state;
private final long futureTime = 1000 * 1000 * 1000 * 1000;
/**
* Constructor to create an instance of Circuit Breaker.
*
* @param timeout Timeout for the API request. Not necessary for this simple example
* @param failureThreshold Number of failures we receive from the depended service before changing
* state to 'OPEN'
* @param retryTimePeriod Time period after which a new request is made to remote service for
* status check.
*/
DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold,
long retryTimePeriod) {
this.service = serviceToCall;
// We start in a closed state hoping that everything is fine
this.state = State.CLOSED;
this.failureThreshold = failureThreshold;
// Timeout for the API request.
// Used to break the calls made to remote resource if it exceeds the limit
this.timeout = timeout;
this.retryTimePeriod = retryTimePeriod;
//An absurd amount of time in future which basically indicates the last failure never happened
this.lastFailureTime = System.nanoTime() + futureTime;
this.failureCount = 0;
}
// Reset everything to defaults
@Override
public void recordSuccess() {
this.failureCount = 0;
this.lastFailureTime = System.nanoTime() + futureTime;
this.state = State.CLOSED;
}
@Override
public void recordFailure(String response) {
failureCount = failureCount + 1;
this.lastFailureTime = System.nanoTime();
// Cache the failure response for returning on open state
this.lastFailureResponse = response;
}
// Evaluate the current state based on failureThreshold, failureCount and lastFailureTime.
protected void evaluateState() {
if (failureCount >= failureThreshold) { //Then something is wrong with remote service
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
//We have waited long enough and should try checking if service is up
state = State.HALF_OPEN;
} else {
//Service would still probably be down
state = State.OPEN;
}
} else {
//Everything is working fine
state = State.CLOSED;
}
}
@Override
public String getState() {
evaluateState();
return state.name();
}
/**
* Break the circuit beforehand if it is known service is down Or connect the circuit manually if
* service comes online before expected.
*
* @param state State at which circuit is in
*/
@Override
public void setState(State state) {
this.state = state;
switch (state) {
case OPEN:
this.failureCount = failureThreshold;
this.lastFailureTime = System.nanoTime();
break;
case HALF_OPEN:
this.failureCount = failureThreshold;
this.lastFailureTime = System.nanoTime() - retryTimePeriod;
break;
default:
this.failureCount = 0;
}
}
/**
* Executes service call.
*
* @return Value from the remote resource, stale response or a custom exception
*/
@Override
public String attemptRequest() throws RemoteServiceException {
evaluateState();
if (state == State.OPEN) {
// return cached response if the circuit is in OPEN state
return this.lastFailureResponse;
} else {
// Make the API request if the circuit is not OPEN
try {
//In a real application, this would be run in a thread and the timeout
//parameter of the circuit breaker would be utilized to know if service
//is working. Here, we simulate that based on server response itself
var response = service.call();
// Yay!! the API responded fine. Let's reset everything.
recordSuccess();
return response;
} catch (RemoteServiceException ex) {
recordFailure(ex.getMessage());
throw ex;
}
}
}
}

View File

@@ -27,7 +27,9 @@ package com.iluwatar.circuitbreaker;
* This simulates the remote service It responds only after a certain timeout period (default set to
* 20 seconds).
*/
public class DelayedService {
public class DelayedRemoteService implements RemoteService {
private final long serverStartTime;
private final int delay;
/**
@@ -35,22 +37,23 @@ public class DelayedService {
*
* @param delay the delay after which service would behave properly, in seconds
*/
public DelayedService(int delay) {
public DelayedRemoteService(long serverStartTime, int delay) {
this.serverStartTime = serverStartTime;
this.delay = delay;
}
public DelayedService() {
this.delay = 60;
public DelayedRemoteService() {
this.serverStartTime = System.nanoTime();
this.delay = 20;
}
/**
* Responds based on delay, current time and server start time if the service is down / working.
*
* @param serverStartTime Time at which actual server was started which makes calls to this
* service
* @return The state of the service
*/
public String response(long serverStartTime) {
@Override
public String call() throws RemoteServiceException {
var currentTime = System.nanoTime();
//Since currentTime and serverStartTime are both in nanoseconds, we convert it to
//seconds by diving by 10e9 and ensure floating point division by multiplying it
@@ -58,9 +61,8 @@ public class DelayedService {
//send the reply
if ((currentTime - serverStartTime) * 1.0 / (1000 * 1000 * 1000) < delay) {
//Can use Thread.sleep() here to block and simulate a hung server
return "Delayed service is down";
} else {
return "Delayed service is working";
throw new RemoteServiceException("Delayed service is down");
}
return "Delayed service is working";
}
}

View File

@@ -24,28 +24,47 @@
package com.iluwatar.circuitbreaker;
/**
* The service class which makes local and remote calls Uses {@link CircuitBreaker} object to ensure
* remote calls don't use up resources.
* The service class which makes local and remote calls Uses {@link DefaultCircuitBreaker} object to
* ensure remote calls don't use up resources.
*/
public class MonitoringService {
private final CircuitBreaker delayedService;
private final CircuitBreaker quickService;
public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) {
this.delayedService = delayedService;
this.quickService = quickService;
}
//Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic
public String localResourceResponse() {
return "Local Service is working";
}
/**
* Try to get result from remote server.
* Fetch response from the delayed service (with some simulated startup time).
*
* @param circuitBreaker The circuitBreaker object with all parameters
* @param serverStartTime Time at which actual server was started which makes calls to this
* service
* @return result from the remote response or exception raised by it.
* @return response string
*/
public String remoteResourceResponse(CircuitBreaker circuitBreaker, long serverStartTime) {
public String delayedServiceResponse() {
try {
return circuitBreaker.call("delayedService", serverStartTime);
} catch (Exception e) {
return this.delayedService.attemptRequest();
} catch (RemoteServiceException e) {
return e.getMessage();
}
}
/**
* Fetches response from a healthy service without any failure.
*
* @return response string
*/
public String quickServiceResponse() {
try {
return this.quickService.attemptRequest();
} catch (RemoteServiceException e) {
return e.getMessage();
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.circuitbreaker;
/**
* A quick response remote service, that responds healthy without any delay or failure.
*/
public class QuickRemoteService implements RemoteService {
@Override
public String call() throws RemoteServiceException {
return "Quick Service is working";
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.circuitbreaker;
/**
* The Remote service interface, used by {@link CircuitBreaker} for fetching response from remote
* services.
*/
public interface RemoteService {
//Fetch response from remote service.
String call() throws RemoteServiceException;
}

View File

@@ -0,0 +1,34 @@
/*
* 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.circuitbreaker;
/**
* Exception thrown when {@link RemoteService} does not respond successfully.
*/
public class RemoteServiceException extends Exception {
public RemoteServiceException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,135 @@
/*
* 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.circuitbreaker;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* App Test showing usage of circuit breaker.
*/
public class AppTest {
private static final Logger LOGGER = LoggerFactory.getLogger(AppTest.class);
//Startup delay for delayed service (in seconds)
private static final int STARTUP_DELAY = 4;
//Number of failed requests for circuit breaker to open
private static final int FAILURE_THRESHOLD = 1;
//Time period in seconds for circuit breaker to retry service
private static final int RETRY_PERIOD = 2;
private MonitoringService monitoringService;
private CircuitBreaker delayedServiceCircuitBreaker;
private CircuitBreaker quickServiceCircuitBreaker;
/**
* Setup the circuit breakers and services, where {@link DelayedRemoteService} will be start with
* a delay of 4 seconds and a {@link QuickRemoteService} responding healthy. Both services are
* wrapped in a {@link DefaultCircuitBreaker} implementation with failure threshold of 1 failure
* and retry time period of 2 seconds.
*/
@BeforeEach
public void setupCircuitBreakers() {
var delayedService = new DelayedRemoteService(System.nanoTime(), STARTUP_DELAY);
//Set the circuit Breaker parameters
delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
FAILURE_THRESHOLD,
RETRY_PERIOD * 1000 * 1000 * 1000);
var quickService = new QuickRemoteService();
//Set the circuit Breaker parameters
quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, FAILURE_THRESHOLD,
RETRY_PERIOD * 1000 * 1000 * 1000);
monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
quickServiceCircuitBreaker);
}
@Test
public void testFailure_OpenStateTransition() {
//Calling delayed service, which will be unhealthy till 4 seconds
assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
//As failure threshold is "1", the circuit breaker is changed to OPEN
assertEquals("OPEN", delayedServiceCircuitBreaker.getState());
//As circuit state is OPEN, we expect a quick fallback response from circuit breaker.
assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
//Meanwhile, the quick service is responding and the circuit state is CLOSED
assertEquals("Quick Service is working", monitoringService.quickServiceResponse());
assertEquals("CLOSED", quickServiceCircuitBreaker.getState());
}
@Test
public void testFailure_HalfOpenStateTransition() {
//Calling delayed service, which will be unhealthy till 4 seconds
assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
//As failure threshold is "1", the circuit breaker is changed to OPEN
assertEquals("OPEN", delayedServiceCircuitBreaker.getState());
//Waiting for recovery period of 2 seconds for circuit breaker to retry service.
try {
LOGGER.info("Waiting 2s for delayed service to become responsive");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//After 2 seconds, the circuit breaker should move to "HALF_OPEN" state and retry fetching response from service again
assertEquals("HALF_OPEN", delayedServiceCircuitBreaker.getState());
}
@Test
public void testRecovery_ClosedStateTransition() {
//Calling delayed service, which will be unhealthy till 4 seconds
assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
//As failure threshold is "1", the circuit breaker is changed to OPEN
assertEquals("OPEN", delayedServiceCircuitBreaker.getState());
//Waiting for 4 seconds, which is enough for DelayedService to become healthy and respond successfully.
try {
LOGGER.info("Waiting 4s for delayed service to become responsive");
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//As retry period is 2 seconds (<4 seconds of wait), hence the circuit breaker should be back in HALF_OPEN state.
assertEquals("HALF_OPEN", delayedServiceCircuitBreaker.getState());
//Check the success response from delayed service.
assertEquals("Delayed service is working", monitoringService.delayedServiceResponse());
//As the response is success, the state should be CLOSED
assertEquals("CLOSED", delayedServiceCircuitBreaker.getState());
}
}

View File

@@ -25,56 +25,60 @@ package com.iluwatar.circuitbreaker;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.rmi.Remote;
import org.junit.jupiter.api.Test;
/**
* Circuit Breaker test
*/
public class CircuitBreakerTest {
public class DefaultCircuitBreakerTest {
//long timeout, int failureThreshold, long retryTimePeriod
@Test
public void testSetState() {
var circuitBreaker = new CircuitBreaker(1, 1, 100);
public void testEvaluateState() {
var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 100);
//Right now, failureCount<failureThreshold, so state should be closed
assertEquals(circuitBreaker.getState(), "CLOSED");
circuitBreaker.failureCount = 4;
circuitBreaker.lastFailureTime = System.nanoTime();
circuitBreaker.setState();
circuitBreaker.evaluateState();
//Since failureCount>failureThreshold, and lastFailureTime is nearly equal to current time,
//state should be half-open
assertEquals(circuitBreaker.getState(), "HALF_OPEN");
//Since failureCount>failureThreshold, and lastFailureTime is much lesser current time,
//state should be open
circuitBreaker.lastFailureTime = System.nanoTime() - 1000 * 1000 * 1000 * 1000;
circuitBreaker.setState();
circuitBreaker.evaluateState();
assertEquals(circuitBreaker.getState(), "OPEN");
//Now set it back again to closed to test idempotency
circuitBreaker.failureCount = 0;
circuitBreaker.setState();
circuitBreaker.evaluateState();
assertEquals(circuitBreaker.getState(), "CLOSED");
}
@Test
public void testSetStateForBypass() {
var circuitBreaker = new CircuitBreaker(1, 1, 100);
var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 2000 * 1000 * 1000);
//Right now, failureCount<failureThreshold, so state should be closed
//Bypass it and set it to open
circuitBreaker.setStateForBypass(State.OPEN);
circuitBreaker.setState(State.OPEN);
assertEquals(circuitBreaker.getState(), "OPEN");
}
@Test
public void testApiResponses() {
var circuitBreaker = new CircuitBreaker(1, 1, 100);
try {
//Call with the paramater start_time set to huge amount of time in past so that service
//replies with "Ok". Also, state is CLOSED in start
var serviceStartTime = System.nanoTime() - 60 * 1000 * 1000 * 1000;
var response = circuitBreaker.call("delayedService", serviceStartTime);
assertEquals(response, "Delayed service is working");
} catch (Exception e) {
System.out.println(e.getMessage());
}
public void testApiResponses() throws RemoteServiceException {
RemoteService mockService = new RemoteService() {
@Override
public String call() throws RemoteServiceException {
return "Remote Success";
}
};
var circuitBreaker = new DefaultCircuitBreaker(mockService, 1, 1, 100);
//Call with the paramater start_time set to huge amount of time in past so that service
//replies with "Ok". Also, state is CLOSED in start
var serviceStartTime = System.nanoTime() - 60 * 1000 * 1000 * 1000;
var response = circuitBreaker.attemptRequest();
assertEquals(response, "Remote Success");
}
}

View File

@@ -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.circuitbreaker;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* Monitoring Service test
*/
public class DelayedRemoteServiceTest {
/**
* Testing immediate response of the delayed service.
*
* @throws RemoteServiceException
*/
@Test
public void testDefaultConstructor() throws RemoteServiceException {
Assertions.assertThrows(RemoteServiceException.class, () -> {
var obj = new DelayedRemoteService();
obj.call();
});
}
/**
* Testing server started in past (2 seconds ago) and with a simulated delay of 1 second.
*
* @throws RemoteServiceException
*/
@Test
public void testParameterizedConstructor() throws RemoteServiceException {
var obj = new DelayedRemoteService(System.nanoTime()-2000*1000*1000,1);
assertEquals("Delayed service is working",obj.call());
}
}

View File

@@ -35,28 +35,45 @@ public class MonitoringServiceTest {
//long timeout, int failureThreshold, long retryTimePeriod
@Test
public void testLocalResponse() {
var monitoringService = new MonitoringService();
var monitoringService = new MonitoringService(null,null);
var response = monitoringService.localResourceResponse();
assertEquals(response, "Local Service is working");
}
@Test
public void testRemoteResponse() {
var monitoringService = new MonitoringService();
var circuitBreaker = new CircuitBreaker(1, 1, 100);
public void testDelayedRemoteResponseSuccess() {
var delayedService = new DelayedRemoteService(System.nanoTime()-2*1000*1000*1000, 2);
var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
1,
2 * 1000 * 1000 * 1000);
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null);
//Set time in past to make the server work
var serverStartTime = System.nanoTime() / 10;
var response = monitoringService.remoteResourceResponse(circuitBreaker, serverStartTime);
var response = monitoringService.delayedServiceResponse();
assertEquals(response, "Delayed service is working");
}
@Test
public void testRemoteResponse2() {
var monitoringService = new MonitoringService();
var circuitBreaker = new CircuitBreaker(1, 1, 100);
public void testDelayedRemoteResponseFailure() {
var delayedService = new DelayedRemoteService(System.nanoTime(), 2);
var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
1,
2 * 1000 * 1000 * 1000);
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null);
//Set time as current time as initially server fails
var serverStartTime = System.nanoTime();
var response = monitoringService.remoteResourceResponse(circuitBreaker, serverStartTime);
assertEquals(response, "Remote service not responding");
var response = monitoringService.delayedServiceResponse();
assertEquals(response, "Delayed service is down");
}
@Test
public void testQuickRemoteServiceResponse() {
var delayedService = new QuickRemoteService();
var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
1,
2 * 1000 * 1000 * 1000);
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null);
//Set time as current time as initially server fails
var response = monitoringService.delayedServiceResponse();
assertEquals(response, "Quick Service is working");
}
}

View File

@@ -14,14 +14,14 @@ Action, Transaction
## Intent
Encapsulate a request as an object, thereby letting you parameterize clients with different
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
> 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
@@ -30,8 +30,8 @@ In plain words
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
> 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**
@@ -41,25 +41,21 @@ Here's the sample code with wizard and goblin. Let's start from the `Wizard` cla
```java
public class Wizard {
private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
private final Deque<Command> undoStack = new LinkedList<>();
private final 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 castSpell(Runnable runnable) {
runnable.run();
undoStack.offerLast(runnable);
}
public void undoLastSpell() {
if (!undoStack.isEmpty()) {
var previousSpell = undoStack.pollLast();
redoStack.offerLast(previousSpell);
LOGGER.info("{} undoes {}", this, previousSpell);
previousSpell.undo();
previousSpell.run();
}
}
@@ -67,8 +63,7 @@ public class Wizard {
if (!redoStack.isEmpty()) {
var previousSpell = redoStack.pollLast();
undoStack.offerLast(previousSpell);
LOGGER.info("{} redoes {}", this, previousSpell);
previousSpell.redo();
previousSpell.run();
}
}
@@ -79,84 +74,7 @@ public class Wizard {
}
```
Next we present the spell hierarchy.
```java
public interface Command {
void execute(Target target);
void undo();
void redo();
String toString();
}
public class InvisibilitySpell implements 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 implements 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";
}
}
```
Finally, we have the goblin who's the target of the spells.
Next, we have the goblin who's the target of the spells.
```java
public abstract class Target {
@@ -203,33 +121,73 @@ public class Goblin extends Target {
return "Goblin";
}
public void changeSize() {
var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
setSize(oldSize);
}
public void changeVisibility() {
var visible = getVisibility() == Visibility.INVISIBLE
? Visibility.VISIBLE : Visibility.INVISIBLE;
setVisibility(visible);
}
}
```
Finally we have the wizard in main function who casts spell
```java
public static void main(String[] args) {
var wizard = new Wizard();
var goblin = new Goblin();
// casts shrink/unshrink spell
wizard.castSpell(goblin::changeSize);
// casts visible/invisible spell
wizard.castSpell(goblin::changeVisibility);
// undo and redo casts
wizard.undoLastSpell();
wizard.redoLastSpell();
```
Here's the whole example in action.
```java
var wizard = new Wizard();
var goblin = new Goblin();
goblin.printStatus();
wizard.castSpell(new ShrinkSpell(), goblin);
wizard.castSpell(goblin::changeSize);
goblin.printStatus();
wizard.castSpell(new InvisibilitySpell(), goblin);
wizard.castSpell(goblin::changeVisibility);
goblin.printStatus();
wizard.undoLastSpell();
goblin.printStatus();
wizard.undoLastSpell();
goblin.printStatus();
wizard.redoLastSpell();
goblin.printStatus();
wizard.redoLastSpell();
goblin.printStatus();
```
Here's the program output:
```java
// Goblin, [size=normal] [visibility=visible]
// Wizard casts Shrink spell at Goblin
// Goblin, [size=small] [visibility=visible]
// Wizard casts Invisibility spell at Goblin
// Goblin, [size=small] [visibility=invisible]
// Wizard undoes Invisibility spell
// Goblin, [size=small] [visibility=visible]
Goblin, [size=normal] [visibility=visible]
Goblin, [size=small] [visibility=visible]
Goblin, [size=small] [visibility=invisible]
Goblin, [size=small] [visibility=visible]
Goblin, [size=normal] [visibility=visible]
Goblin, [size=small] [visibility=visible]
Goblin, [size=small] [visibility=invisible]
```
## Class diagram
@@ -240,26 +198,26 @@ Here's the program output:
Use the Command pattern when you want to:
* Parameterize objects by an action to perform. You can express such parameterization in a
procedural language with a callback function, that is, a function that's registered somewhere to be
* Parameterize objects by an action to perform. You can express such parameterization in a
procedural language with a callback function, that is, a function that's registered somewhere to be
called at a later point. Commands are an object-oriented replacement for callbacks.
* Specify, queue, and execute requests at different times. A Command object can have a lifetime
independent of the original request. If the receiver of a request can be represented in an address
space-independent way, then you can transfer a command object for the request to a different process
* Specify, queue, and execute requests at different times. A Command object can have a lifetime
independent of the original request. If the receiver of a request can be represented in an address
space-independent way, then you can transfer a command object for the request to a different process
and fulfill the request there.
* Support undo. The Command's execute operation can store state for reversing its effects in the
command itself. The Command interface must have an added un-execute operation that reverses the
effects of a previous call to execute. The executed commands are stored in a history list.
Unlimited-level undo and redo is achieved by traversing this list backwards and forwards calling
* Support undo. The Command's execute operation can store state for reversing its effects in the
command itself. The Command interface must have an added un-execute operation that reverses the
effects of a previous call to execute. The executed commands are stored in a history list.
Unlimited-level undo and redo is achieved by traversing this list backwards and forwards calling
un-execute and execute, respectively.
* Support logging changes so that they can be reapplied in case of a system crash. By augmenting the
Command interface with load and store operations, you can keep a persistent log of changes.
Recovering from a crash involves reloading logged commands from disk and re-executing them with
* Support logging changes so that they can be reapplied in case of a system crash. By augmenting the
Command interface with load and store operations, you can keep a persistent log of changes.
Recovering from a crash involves reloading logged commands from disk and re-executing them with
the execute operation.
* Structure a system around high-level operations build on primitive operations. Such a structure is
common in information systems that support transactions. A transaction encapsulates a set of changes
to data. The Command pattern offers a way to model transactions. Commands have a common interface,
letting you invoke all transactions the same way. The pattern also makes it easy to extend the
* Structure a system around high-level operations build on primitive operations. Such a structure is
common in information systems that support transactions. A transaction encapsulates a set of changes
to data. The Command pattern offers a way to model transactions. Commands have a common interface,
letting you invoke all transactions the same way. The pattern also makes it easy to extend the
system with new transactions.
## Typical Use Case

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -1,116 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<class id="1" language="java" name="com.iluwatar.command.ShrinkSpell" project="command"
file="/command/src/main/java/com/iluwatar/command/ShrinkSpell.java" binary="false" corner="BOTTOM_RIGHT">
<position height="178" width="141" x="-30" y="681"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.command.Goblin" project="command"
file="/command/src/main/java/com/iluwatar/command/Goblin.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="129" y="1223"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.command.Wizard" project="command"
file="/command/src/main/java/com/iluwatar/command/Wizard.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="129" y="362"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="4" language="java" name="com.iluwatar.command.Command" project="command"
file="/command/src/main/java/com/iluwatar/command/Command.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="129" y="561"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="5" language="java" name="com.iluwatar.command.InvisibilitySpell" project="command"
file="/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java" binary="false" corner="BOTTOM_RIGHT">
<position height="160" width="141" x="151" y="681"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="6" language="java" name="com.iluwatar.command.Target" project="command"
file="/command/src/main/java/com/iluwatar/command/Target.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="129" y="1014"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<association id="7">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="8" name="redoStack">
<position height="20" width="67" x="140" y="451"/>
</attribute>
<multiplicity id="9" minimum="0" maximum="2147483647">
<position height="18" width="25" x="221" y="452"/>
</multiplicity>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="10">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="6"/>
</generalization>
<association id="11">
<end type="SOURCE" refId="1" navigable="false">
<attribute id="12" name="target"/>
<multiplicity id="13" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="6" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="14">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="4"/>
</generalization>
<association id="15">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="16" name="undoStack">
<position height="20" width="70" x="-17" y="451"/>
</attribute>
<multiplicity id="17" minimum="0" maximum="2147483647">
<position height="18" width="25" x="60" y="452"/>
</multiplicity>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="18">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="4"/>
</generalization>
<association id="19">
<end type="SOURCE" refId="5" navigable="false">
<attribute id="20" name="target"/>
<multiplicity id="21" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="6" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<class id="2" language="java" name="com.iluwatar.command.Goblin" project="command"
file="/command/src/main/java/com/iluwatar/command/Goblin.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="129" y="1223"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.command.Wizard" project="command"
file="/command/src/main/java/com/iluwatar/command/Wizard.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="129" y="362"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="6" language="java" name="com.iluwatar.command.Target" project="command"
file="/command/src/main/java/com/iluwatar/command/Target.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="129" y="1014"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<association id="7">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="8" name="redoStack">
<position height="20" width="67" x="140" y="451"/>
</attribute>
<multiplicity id="9" minimum="0" maximum="2147483647">
<position height="18" width="25" x="221" y="452"/>
</multiplicity>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="10">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="6"/>
</generalization>
<association id="11">
<end type="SOURCE" refId="1" navigable="false">
<attribute id="12" name="target"/>
<multiplicity id="13" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="6" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="14">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="4"/>
</generalization>
<association id="15">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="16" name="undoStack">
<position height="20" width="70" x="-17" y="451"/>
</attribute>
<multiplicity id="17" minimum="0" maximum="2147483647">
<position height="18" width="25" x="60" y="452"/>
</multiplicity>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="18">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="4"/>
</generalization>
<association id="19">
<end type="SOURCE" refId="5" navigable="false">
<attribute id="20" name="target"/>
<multiplicity id="21" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="6" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>
</class-diagram>

View File

@@ -4,33 +4,11 @@ package com.iluwatar.command {
+ App()
+ main(args : String[]) {static}
}
interface Command {
+ Command()
+ execute(Target) {abstract}
+ redo() {abstract}
+ toString() : String {abstract}
+ undo() {abstract}
}
class Goblin {
+ Goblin()
+ toString() : String
}
class InvisibilitySpell {
- target : Target
+ InvisibilitySpell()
+ execute(target : Target)
+ redo()
+ toString() : String
+ undo()
}
class ShrinkSpell {
- oldSize : Size
- target : Target
+ ShrinkSpell()
+ execute(target : Target)
+ redo()
+ toString() : String
+ undo()
+ changeSize()
+ changeVisibility()
}
enum Size {
+ NORMAL {static}
@@ -62,22 +40,19 @@ package com.iluwatar.command {
}
class Wizard {
- LOGGER : Logger {static}
- redoStack : Deque<Command>
- undoStack : Deque<Command>
- redoStack : Deque<Runnable>
- undoStack : Deque<Runnable>
+ Wizard()
+ castSpell(command : Command, target : Target)
+ castSpell(Runnable : runnable)
+ redoLastSpell()
+ toString() : String
+ undoLastSpell()
}
}
Target --> "-size" Size
Wizard --> "-undoStack" Command
ShrinkSpell --> "-oldSize" Size
InvisibilitySpell --> "-target" Target
ShrinkSpell --> "-target" Target
Wizard --> "-changeSize" Goblin
Wizard --> "-changeVisibility" Goblin
Target --> "-visibility" Visibility
Goblin --|> Target
InvisibilitySpell ..|> Command
ShrinkSpell ..|> Command
App --> "castSpell" Wizard
@enduml

View File

@@ -30,12 +30,10 @@ package com.iluwatar.command;
*
* <p>Four terms always associated with the command pattern are command, receiver, invoker and
* client. A command object (spell) knows about the receiver (target) and invokes a method of the
* receiver. Values for parameters of the receiver method are stored in the command. The receiver
* then does the work. An invoker object (wizard) knows how to execute a command, and optionally
* does bookkeeping about the command execution. The invoker does not know anything about a concrete
* command, it knows only about command interface. Both an invoker object and several command
* objects are held by a client object (app). The client decides which commands to execute at which
* points. To execute a command, it passes the command object to the invoker object.
* receiver. An invoker object (wizard) receives a reference to the command to be executed and
* optionally does bookkeeping about the command execution. The invoker does not know anything
* about how the command is executed. The client decides which commands to execute at which
* points. To execute a command, it passes a reference of the function to the invoker object.
*
* <p>In other words, in this example the wizard casts spells on the goblin. The wizard keeps track
* of the previous spells cast, so it is easy to undo them. In addition, the wizard keeps track of
@@ -54,10 +52,10 @@ public class App {
goblin.printStatus();
wizard.castSpell(new ShrinkSpell(), goblin);
wizard.castSpell(goblin::changeSize);
goblin.printStatus();
wizard.castSpell(new InvisibilitySpell(), goblin);
wizard.castSpell(goblin::changeVisibility);
goblin.printStatus();
wizard.undoLastSpell();

View File

@@ -37,5 +37,4 @@ public class Goblin extends Target {
public String toString() {
return "Goblin";
}
}

View File

@@ -62,4 +62,21 @@ public abstract class Target {
public void printStatus() {
LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
}
/**
* Changes the size of the target.
*/
public void changeSize() {
var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
setSize(oldSize);
}
/**
* Changes the visibility of the target.
*/
public void changeVisibility() {
var visible = getVisibility() == Visibility.INVISIBLE
? Visibility.VISIBLE : Visibility.INVISIBLE;
setVisibility(visible);
}
}

View File

@@ -25,30 +25,24 @@ package com.iluwatar.command;
import java.util.Deque;
import java.util.LinkedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Wizard is the invoker of the commands.
*/
public class Wizard {
private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
private final Deque<Command> undoStack = new LinkedList<>();
private final Deque<Command> redoStack = new LinkedList<>();
private final Deque<Runnable> undoStack = new LinkedList<>();
private final Deque<Runnable> redoStack = new LinkedList<>();
public Wizard() {
// comment to ignore sonar issue: LEVEL critical
}
/**
* Cast spell.
*/
public void castSpell(Command command, Target target) {
LOGGER.info("{} casts {} at {}", this, command, target);
command.execute(target);
undoStack.offerLast(command);
public void castSpell(Runnable runnable) {
runnable.run();
undoStack.offerLast(runnable);
}
/**
@@ -58,8 +52,7 @@ public class Wizard {
if (!undoStack.isEmpty()) {
var previousSpell = undoStack.pollLast();
redoStack.offerLast(previousSpell);
LOGGER.info("{} undoes {}", this, previousSpell);
previousSpell.undo();
previousSpell.run();
}
}
@@ -70,8 +63,7 @@ public class Wizard {
if (!redoStack.isEmpty()) {
var previousSpell = redoStack.pollLast();
undoStack.offerLast(previousSpell);
LOGGER.info("{} redoes {}", this, previousSpell);
previousSpell.redo();
previousSpell.run();
}
}

View File

@@ -56,10 +56,10 @@ public class CommandTest {
var wizard = new Wizard();
var goblin = new Goblin();
wizard.castSpell(new ShrinkSpell(), goblin);
wizard.castSpell(goblin::changeSize);
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE);
wizard.castSpell(new InvisibilitySpell(), goblin);
wizard.castSpell(goblin::changeVisibility);
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE);
wizard.undoLastSpell();

View File

@@ -45,7 +45,7 @@ public class SpaceStationMir extends GameObject {
@Override
public void collisionResolve(FlamingAsteroid asteroid) {
LOGGER.info(AppConstants.HITS, " {} is damaged! {} is set on fire!", asteroid.getClass()
LOGGER.info(AppConstants.HITS + " {} is damaged! {} is set on fire!", asteroid.getClass()
.getSimpleName(),
this.getClass().getSimpleName(), this.getClass().getSimpleName(), this.getClass()
.getSimpleName());
@@ -55,14 +55,14 @@ public class SpaceStationMir extends GameObject {
@Override
public void collisionResolve(Meteoroid meteoroid) {
LOGGER.info(AppConstants.HITS, " {} is damaged!", meteoroid.getClass().getSimpleName(),
LOGGER.info(AppConstants.HITS + " {} is damaged!", meteoroid.getClass().getSimpleName(),
this.getClass().getSimpleName(), this.getClass().getSimpleName());
setDamaged(true);
}
@Override
public void collisionResolve(SpaceStationMir mir) {
LOGGER.info(AppConstants.HITS, " {} is damaged!", mir.getClass().getSimpleName(),
LOGGER.info(AppConstants.HITS + " {} is damaged!", mir.getClass().getSimpleName(),
this.getClass().getSimpleName(), this.getClass().getSimpleName());
setDamaged(true);
}

View File

@@ -24,11 +24,11 @@ Use the Event Sourcing pattern when
## Real world examples
* [The Lmax Architecture] (https://martinfowler.com/articles/lmax.html)
* [The Lmax Architecture](https://martinfowler.com/articles/lmax.html)
## Credits
* [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)
* [Martin Fowler - Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html)
* [Event Sourcing in Microsoft's documentation](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)

133
factory/README.md Normal file
View File

@@ -0,0 +1,133 @@
---
layout: pattern
title: Factory
folder: factory
permalink: /patterns/factory/
categories: Creational
tags:
- Gang of Four
---
## Also known as
* Simple Factory
* Static Factory Method
## Intent
Providing a static method encapsulated in a class called factory, in order to hide the
implementation logic and makes client code focus on usage rather then initialization new objects.
## Explanation
Real world example
> Lets say we have a web application connected to SQLServer, but now we want to switch to Oracle. To
> do so without modifying existing source code, we need to implements Simple Factory pattern, in
> which a static method can be invoked to create connection to a given database.
Wikipedia says
> Factory is an object for creating other objects formally a factory is a function or method that
> returns objects of a varying prototype or class.
**Programmatic Example**
We have an interface `Car` and two implementations `Ford` and `Ferrari`.
```java
public interface Car {
String getDescription();
}
public class Ford implements Car {
static final String DESCRIPTION = "This is Ford.";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class Ferrari implements Car {
static final String DESCRIPTION = "This is Ferrari.";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
```
Enumeration above represents types of cars that we support (`Ford` and `Ferrari`).
```java
public enum CarType {
FORD(Ford::new),
FERRARI(Ferrari::new);
private final Supplier<Car> constructor;
CarType(Supplier<Car> constructor) {
this.constructor = constructor;
}
public Supplier<Car> getConstructor() {
return this.constructor;
}
}
```
Then we have the static method `getCar` to create car objects encapsulated in the factory class
`CarSimpleFactory`.
```java
public class CarsFactory {
public static Car getCar(CarType type) {
return type.getConstructor().get();
}
}
```
Now on the client code we can create different types of cars using the factory class.
```java
var car1 = CarsFactory.getCar(CarType.FORD);
var car2 = CarsFactory.getCar(CarType.FERRARI);
LOGGER.info(car1.getDescription());
LOGGER.info(car2.getDescription());;
```
Program output:
```java
This is Ford.
This Ferrari.
```
## Class Diagram
![alt text](./etc/factory.urm.png "Factory pattern class diagram")
## Applicability
Use the Simple Factory pattern when you only care about the creation of a object, not how to create
and manage it.
Pros
* Allows keeping all objects creation in one place and avoid of spreading 'new' key value across codebase.
* Allows to writs loosely coupled code. Some of its main advantages include better testability, easy-to-understand code, swappable components, scalability and isolated features.
Cons
* The code becomes more complicated than it should be.
## Related patterns
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/)
* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/)
* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/)

BIN
factory/etc/factory.urm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,35 @@
@startuml
package com.iluwatar.factory {
class App {
- LOGGER : Logger {static}
+ App()
+ main(args : String[]) {static}
}
interface Car {
+ getDescription() : String {abstract}
}
class CarsFactory {
+ CarsFactory()
+ getCar(type : CarType) : Car {static}
}
~enum CarType {
+ FERRARI {static}
+ FORD {static}
+ valueOf(name : String) : CarType {static}
+ values() : CarType[] {static}
}
class Ferrari {
~ DESCRIPTION : String {static}
+ Ferrari()
+ getDescription() : String
}
class Ford {
~ DESCRIPTION : String {static}
+ Ford()
+ getDescription() : String
}
}
CarType ..+ CarsFactory
Ferrari ..|> Car
Ford ..|> Car
@enduml

67
factory/pom.xml Normal file
View File

@@ -0,0 +1,67 @@
<!--
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.24.0-SNAPSHOT</version>
</parent>
<artifactId>factory</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
in parent pom and specifying the class having main method -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.factory.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -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.factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Factory is an object for creating other objects, it providing Providing a static method to
* create and return objects of varying classes, in order to hide the implementation logic
* and makes client code focus on usage rather then objects initialization and management.
*
* <p>In this example the CarFactory is the factory class and it provides a static method to
* create different cars.
*/
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Program main entry point.
*/
public static void main(String[] args) {
var car1 = CarsFactory.getCar(CarType.FORD);
var car2 = CarsFactory.getCar(CarType.FERRARI);
LOGGER.info(car1.getDescription());
LOGGER.info(car2.getDescription());
}
}

View File

@@ -1,37 +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.command;
/**
* Interface for Commands.
*/
public interface Command {
void execute(Target target);
void undo();
void redo();
String toString();
}
/*
* 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.factory;
/**
* Car interface.
*/
public interface Car {
String getDescription();
}

View File

@@ -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.factory;
import java.util.function.Supplier;
public enum CarType {
/**
* Enumeration for different types of cars.
*/
FORD(Ford::new),
FERRARI(Ferrari::new);
private final Supplier<Car> constructor;
CarType(Supplier<Car> constructor) {
this.constructor = constructor;
}
public Supplier<Car> getConstructor() {
return this.constructor;
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.factory;
/**
* Factory of cars.
*/
public class CarsFactory {
/**
* Factory method takes as parameter a car type and initiate the appropriate class.
*/
public static Car getCar(CarType type) {
return type.getConstructor().get();
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.factory;
/**
* Ferrari implementation.
*/
public class Ferrari implements Car {
static final String DESCRIPTION = "This is Ferrari.";
@Override
public String getDescription() {
return DESCRIPTION;
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.factory;
/**
* Ford implementation.
*/
public class Ford implements Car {
static final String DESCRIPTION = "This is Ford.";
@Override
public String getDescription() {
return DESCRIPTION;
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.factory;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class AppTest {
@Test
void shouldExecuteWithoutExceptions() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.factory;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class CarsFactoryTest {
@Test
void shouldReturnFerrariInstance() {
final var ferrari = CarsFactory.getCar(CarType.FERRARI);
assertTrue(ferrari instanceof Ferrari);
}
}

View File

@@ -9,6 +9,10 @@ tags:
- Performance
---
## Also known as
Resource Pool
## Intent
When objects are expensive to create and they are needed only for short periods of time it is

View File

@@ -89,6 +89,7 @@
<module>state</module>
<module>strategy</module>
<module>template-method</module>
<module>version-number</module>
<module>visitor</module>
<module>double-checked-locking</module>
<module>servant</module>
@@ -195,6 +196,8 @@
<module>arrange-act-assert</module>
<module>transaction-script</module>
<module>filterer</module>
<module>factory</module>
<module>separated-interface</module>
</modules>
<repositories>

View File

@@ -9,6 +9,7 @@ tags:
---
## Intent
Private Class Data design pattern seeks to reduce exposure of attributes by limiting their
visibility. It reduces the number of class attributes by encapsulating them in single Data object.
@@ -124,9 +125,11 @@ immutableStew.mix(); // Mixing the immutable stew we find: 2 potatoes, 4 carrot
```
## Class diagram
![alt text](./etc/private-class-data.png "Private Class Data")
## Applicability
Use the Private Class Data pattern when
* You want to prevent write access to class data members
* You want to prevent write access to class data members.

View File

@@ -14,23 +14,25 @@ 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.
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
* 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.
> 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
@@ -38,14 +40,15 @@ In plain words
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.
> 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.
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`.
@@ -195,7 +198,7 @@ public class Promise<T> extends PromiseSupport<T> {
public <V> Promise<V> thenApply(Function<? super T, V> func) {
Promise<V> dest = new Promise<>();
fulfillmentAction = new TransformAction<V>(this, dest, func);
fulfillmentAction = new TransformAction<>(this, dest, func);
return dest;
}
@@ -246,8 +249,8 @@ public class Promise<T> extends PromiseSupport<T> {
}
```
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`.
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(
@@ -280,8 +283,8 @@ Now we can show the full example in action. Here's how to download and count the
## Applicability
Promise pattern is applicable in concurrent programming when some work needs to be done asynchronously
and:
Promise pattern is applicable in concurrent programming when some work needs to be done
asynchronously and:
* Code maintainability and readability suffers due to callback hell.
* You need to compose promises and need better error handling for asynchronous tasks.

View File

@@ -81,7 +81,7 @@ public class App {
* @throws InterruptedException if main thread is interrupted.
* @throws ExecutionException if an execution error occurs.
*/
public static void main(String[] args) throws InterruptedException, ExecutionException {
public static void main(String[] args) throws InterruptedException {
var app = new App();
try {
app.promiseUsage();

View File

@@ -141,7 +141,7 @@ public class Promise<T> extends PromiseSupport<T> {
*/
public <V> Promise<V> thenApply(Function<? super T, V> func) {
Promise<V> dest = new Promise<>();
fulfillmentAction = new TransformAction<V>(this, dest, func);
fulfillmentAction = new TransformAction<>(this, dest, func);
return dest;
}

View File

@@ -26,7 +26,6 @@ package com.iluwatar.promise;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -114,4 +113,4 @@ class PromiseSupport<T> implements Future<T> {
}
throw new ExecutionException(exception);
}
}
}

View File

@@ -10,16 +10,19 @@ 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.
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.
> 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.
In plain words
@@ -27,9 +30,12 @@ In plain words
Wikipedia says
> The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects.
> The prototype pattern is a creational design pattern in software development. It is used when the
> type of objects to create is determined by a prototypical instance, which is cloned to produce new
> objects.
In short, it allows you to create a copy of an existing object and modify it to your needs, instead of going through the trouble of creating an object from scratch and setting it up.
In short, it allows you to create a copy of an existing object and modify it to your needs, instead
of going through the trouble of creating an object from scratch and setting it up.
**Programmatic Example**
@@ -52,7 +58,7 @@ class Sheep implements Cloneable {
}
```
Then it can be cloned like below
Then it can be cloned like below:
```java
var original = new Sheep("Jolly");
@@ -65,15 +71,20 @@ System.out.println(cloned.getName()); // Dolly
```
## Class diagram
![alt text](./etc/prototype.urm.png "Prototype pattern class diagram")
## Applicability
Use the Prototype pattern when a system should be independent of how its products are created, composed and represented; and
* When the classes to instantiate are specified at run-time, for example, by dynamic loading
* To avoid building a class hierarchy of factories that parallels the class hierarchy of products
* When instances of a class can have one of only a few different combinations of state. It may be more convenient to install a corresponding number of prototypes and clone them rather than instantiating the class manually, each time with the appropriate state
* When object creation is expensive compared to cloning
Use the Prototype pattern when a system should be independent of how its products are created,
composed, represented and
* When the classes to instantiate are specified at run-time, for example, by dynamic loading.
* To avoid building a class hierarchy of factories that parallels the class hierarchy of products.
* When instances of a class can have one of only a few different combinations of state. It may be
more convenient to install a corresponding number of prototypes and clone them rather than
instantiating the class manually, each time with the appropriate state.
* When object creation is expensive compared to cloning.
## Real world examples

View File

@@ -10,16 +10,20 @@ tags:
---
## Also known as
Surrogate
## Intent
Provide a surrogate or placeholder for another object to control
access to it.
Provide a surrogate or placeholder for another object to control access to it.
## Explanation
Real world example
> Imagine a tower where the local wizards go to study their spells. The ivory tower can only be accessed through a proxy which ensures that only the first three wizards can enter. Here the proxy represents the functionality of the tower and adds access control to it.
> Imagine a tower where the local wizards go to study their spells. The ivory tower can only be
> accessed through a proxy which ensures that only the first three wizards can enter. Here the proxy
> represents the functionality of the tower and adds access control to it.
In plain words
@@ -27,11 +31,17 @@ In plain words
Wikipedia says
> A proxy, in its most general form, is a class functioning as an interface to something else. A proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked.
> A proxy, in its most general form, is a class functioning as an interface to something else.
> A proxy is a wrapper or agent object that is being called by the client to access the real serving
> object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can
> provide additional logic. In the proxy extra functionality can be provided, for example caching
> when operations on the real object are resource intensive, or checking preconditions before
> operations on the real object are invoked.
**Programmatic Example**
Taking our wizard tower example from above. Firstly we have the wizard tower interface and the ivory tower class
Taking our wizard tower example from above. Firstly we have the `WizardTower` interface and the
`IvoryTower` class.
```java
public interface WizardTower {
@@ -50,7 +60,7 @@ public class IvoryTower implements WizardTower {
}
```
Then a simple wizard class
Then a simple `Wizard` class.
```java
public class Wizard {
@@ -68,7 +78,7 @@ public class Wizard {
}
```
Then we have the proxy to add access control to wizard tower
Then we have the `WizardTowerProxy` to add access control to `WizardTower`.
```java
public class WizardTowerProxy implements WizardTower {
@@ -97,28 +107,41 @@ public class WizardTowerProxy implements WizardTower {
}
```
And here is tower entering scenario
And here is the tower entering scenario.
```java
var proxy = new WizardTowerProxy(new IvoryTower());
proxy.enter(new Wizard("Red wizard")); // Red wizard enters the tower.
proxy.enter(new Wizard("White wizard")); // White wizard enters the tower.
proxy.enter(new Wizard("Black wizard")); // Black wizard enters the tower.
proxy.enter(new Wizard("Green wizard")); // Green wizard is not allowed to enter!
proxy.enter(new Wizard("Brown wizard")); // Brown wizard is not allowed to enter!
proxy.enter(new Wizard("Red wizard"));
proxy.enter(new Wizard("White wizard"));
proxy.enter(new Wizard("Black wizard"));
proxy.enter(new Wizard("Green wizard"));
proxy.enter(new Wizard("Brown wizard"));
```
Program output:
```
Red wizard enters the tower.
White wizard enters the tower.
Black wizard enters the tower.
Green wizard is not allowed to enter!
Brown wizard is not allowed to enter!
```
## Class diagram
![alt text](./etc/proxy.urm.png "Proxy pattern class diagram")
## Applicability
Proxy is applicable whenever there is a need for a more
versatile or sophisticated reference to an object than a simple pointer. Here
are several common situations in which the Proxy pattern is applicable
Proxy is applicable whenever there is a need for a more versatile or sophisticated reference to an
object than a simple pointer. Here are several common situations in which the Proxy pattern is
applicable.
* Remote proxy provides a local representative for an object in a different address space.
* Virtual proxy creates expensive objects on demand.
* Protection proxy controls access to the original object. Protection proxies are useful when objects should have different access rights.
* Protection proxy controls access to the original object. Protection proxies are useful when
objects should have different access rights.
## Typical Use Case
@@ -136,7 +159,8 @@ are several common situations in which the Proxy pattern is applicable
* [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html)
* [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/)
* Mocking frameworks Mockito, Powermock, EasyMock
* Mocking frameworks [Mockito](https://site.mockito.org/),
[Powermock](https://powermock.github.io/), [EasyMock](https://easymock.org/)
## Related patterns

View File

@@ -1,40 +1,40 @@
/*
* 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.proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The object to be proxyed.
*/
public class IvoryTower implements WizardTower {
private static final Logger LOGGER = LoggerFactory.getLogger(IvoryTower.class);
public void enter(Wizard wizard) {
LOGGER.info("{} enters the tower.", wizard);
}
}
/*
* 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.proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The object to be proxied.
*/
public class IvoryTower implements WizardTower {
private static final Logger LOGGER = LoggerFactory.getLogger(IvoryTower.class);
public void enter(Wizard wizard) {
LOGGER.info("{} enters the tower.", wizard);
}
}

View File

@@ -9,26 +9,33 @@ 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.
> 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.
> 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.
> 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.
Let's first look at the person entity that we need to persist.
```java
@Entity
@@ -39,107 +46,23 @@ public class Person {
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);
}
// getters and setters ->
...
}
```
We are using Spring Data to create the repository so it becomes really simple.
We are using Spring Data to create the `PersonRepository` so it becomes really simple.
```java
@Repository
@@ -150,7 +73,7 @@ public interface PersonRepository
}
```
Additionally we define a helper class for specification queries.
Additionally we define a helper class `PersonSpecifications` for specification queries.
```java
public class PersonSpecifications {
@@ -189,7 +112,7 @@ public class PersonSpecifications {
}
```
And here's the repository in action.
And here's the repository example in action.
```java
var peter = new Person("Peter", "Sagan", 17);
@@ -226,30 +149,36 @@ And here's the repository in action.
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]
```
Program output:
```
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
![alt text](./etc/repository.png "Repository")
## Applicability
Use the Repository pattern when
* The number of domain objects is large
* You want to avoid duplication of query code
* You want to keep the database querying code in single place
* You have multiple data sources
* The number of domain objects is large.
* You want to avoid duplication of query code.
* You want to keep the database querying code in single place.
* You have multiple data sources.
## Real world examples

View File

@@ -23,7 +23,6 @@
package com.iluwatar.repository;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import javax.sql.DataSource;
@@ -89,7 +88,7 @@ public class AppConfig {
* Get transaction manager.
*/
@Bean
public JpaTransactionManager transactionManager() throws SQLException {
public JpaTransactionManager transactionManager() {
var transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;

View File

@@ -60,7 +60,7 @@ public class PersonSpecifications {
*/
public static class NameEqualSpec implements Specification<Person> {
public String name;
public final String name;
public NameEqualSpec(String name) {
this.name = name;

View File

@@ -10,39 +10,40 @@ tags:
---
## 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
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
remote resource during peak loads as well as our application as more threads
are waiting for the remote resource's availability? Among the errors returned
by the remote service, which can be safely ignored in order to retry? Is the
operation [idempotent](https://en.wikipedia.org/wiki/Idempotence)?
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).
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 remote resource during peak loads as well as our application as more threads
are waiting for the remote resource's availability? Among the errors returned by the remote service,
which can be safely ignored in order to retry? Is the operation
[idempotent](https://en.wikipedia.org/wiki/Idempotence)?
From a strategic point of view, this would be solved by having requests
be redirected to a separate intermediary system, traditionally an
[ESB](https://en.wikipedia.org/wiki/Enterprise_service_bus), but more recently
a [Service Mesh](https://medium.com/microservices-in-practice/service-mesh-for-microservices-2953109a3c9a).
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).
From a tactical point of view, this would be solved by reusing shared libraries
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*.
From a strategic point of view, this would be solved by having requests redirected to a separate
intermediary system, traditionally an [ESB](https://en.wikipedia.org/wiki/Enterprise_service_bus),
but more recently 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) (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.md`.
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.
> 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
@@ -50,11 +51,14 @@ In plain words
[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.
> 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.
In our hypothetical application, we have a generic interface for all operations on remote
interfaces.
```java
public interface BusinessOperation<T> {
@@ -73,16 +77,14 @@ public final class FindCustomer implements BusinessOperation<String> {
}
```
Our `FindCustomer` implementation can be configured to throw
`BusinessException`s before returning the customer's ID, thereby simulating a
'flaky' service that intermittently fails. Some exceptions, like the
`CustomerNotFoundException`, are deemed to be recoverable after some
hypothetical analysis because the root cause of the error stems from "some
database locking issue". However, the `DatabaseNotAvailableException` is
considered to be a definite showstopper - the application should not attempt
to recover from this error.
Our `FindCustomer` implementation can be configured to throw `BusinessException`s before returning
the customer's ID, thereby simulating a flaky service that intermittently fails. Some exceptions,
like the `CustomerNotFoundException`, are deemed to be recoverable after some hypothetical analysis
because the root cause of the error stems from "some database locking issue". However, the
`DatabaseNotAvailableException` is considered to be a definite showstopper - the application should
not attempt to recover from this error.
We can model a 'recoverable' scenario by instantiating `FindCustomer` like this:
We can model a recoverable scenario by instantiating `FindCustomer` like this:
```java
final var op = new FindCustomer(
@@ -93,15 +95,12 @@ final var op = new FindCustomer(
);
```
In this configuration, `FindCustomer` will throw `CustomerNotFoundException`
three times, after which it will consistently return the customer's ID
(`12345`).
In this configuration, `FindCustomer` will throw `CustomerNotFoundException` three times, after
which it will consistently return the customer's ID (`12345`).
In our hypothetical scenario, our analysts indicate that this operation
typically fails 2-4 times for a given input during peak hours, and that each
worker thread in the database subsystem typically needs 50ms to
"recover from an error". Applying these policies would yield something like
this:
In our hypothetical scenario, our analysts indicate that this operation typically fails 2-4 times
for a given input during peak hours, and that each worker thread in the database subsystem typically
needs 50ms to "recover from an error". Applying these policies would yield something like this:
```java
final var op = new Retry<>(
@@ -117,26 +116,27 @@ final var op = new Retry<>(
);
```
Executing `op` *once* would automatically trigger at most 5 retry attempts,
with a 100 millisecond delay between attempts, ignoring any
`CustomerNotFoundException` thrown while trying. In this particular scenario,
due to the configuration for `FindCustomer`, there will be 1 initial attempt
Executing `op` once would automatically trigger at most 5 retry attempts, with a 100 millisecond
delay between attempts, ignoring any `CustomerNotFoundException` thrown while trying. In this
particular scenario, due to the configuration for `FindCustomer`, there will be 1 initial attempt
and 3 additional retries before finally returning the desired result `12345`.
If our `FindCustomer` operation were instead to throw a fatal
`DatabaseNotFoundException`, which we were instructed not to ignore, but
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.
If our `FindCustomer` operation were instead to throw a fatal `DatabaseNotFoundException`, which we
were instructed not to ignore, but 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.
## Class diagram
![alt text](./etc/retry.png "Retry")
## Applicability
Whenever an application needs to communicate with an external resource, particularly in a cloud environment, and if
the business requirements allow it.
Whenever an application needs to communicate with an external resource, particularly in a cloud
environment, and if the business requirements allow it.
## Consequences
**Pros:**
* Resiliency

View File

@@ -0,0 +1,138 @@
---
layout: pattern
title: Separated Interface
folder: separated-interface
permalink: /patterns/separated-interface/
categories: Structural
tags:
- Decoupling
---
## Intent
Separate the interface definition and implementation in different packages. This allows the client
to be completely unaware of the implementation.
## Explanation
Real world example
> An Invoice generator may be created with ability to use different Tax calculators that may be
> added in the invoice depending upon type of purchase, region etc.
In plain words
> Separated interface pattern encourages to keep the implementations of an interface decoupled from
> the client and its definition, so the client is not dependent on the implementation.
A client code may abstract some specific functionality to an interface, and define the definition of
the interface as an SPI ([Service Programming Interface](https://en.wikipedia.org/wiki/Service_provider_interface)
is an API intended and open to be implemented or extended by a third party). Another package may
implement this interface definition with a concrete logic, which will be injected into the client
code at runtime (with a third class, injecting the implementation in the client) or at compile time
(using Plugin pattern with some configurable file).
**Programmatic Example**
**Client**
`InvoiceGenerator` class accepts the cost of the product and calculates the total
amount payable inclusive of tax.
```java
public class InvoiceGenerator {
private final TaxCalculator taxCalculator;
private final double amount;
public InvoiceGenerator(double amount, TaxCalculator taxCalculator) {
this.amount = amount;
this.taxCalculator = taxCalculator;
}
public double getAmountWithTax() {
return amount + taxCalculator.calculate(amount);
}
}
```
The tax calculation logic is delegated to the `TaxCalculator` interface.
```java
public interface TaxCalculator {
double calculate(double amount);
}
```
**Implementation package**
In another package (which the client is completely unaware of) there exist multiple implementations
of the `TaxCalculator` interface. `ForeignTaxCalculator` is one of them which levies 60% tax
for international products.
```java
public class ForeignTaxCalculator implements TaxCalculator {
public static final double TAX_PERCENTAGE = 60;
@Override
public double calculate(double amount) {
return amount * TAX_PERCENTAGE / 100.0;
}
}
```
Another is `DomesticTaxCalculator` which levies 20% tax for international products.
```java
public class DomesticTaxCalculator implements TaxCalculator {
public static final double TAX_PERCENTAGE = 20;
@Override
public double calculate(double amount) {
return amount * TAX_PERCENTAGE / 100.0;
}
}
```
These both implementations are instantiated and injected in the client class by the ```App.java```
class.
```java
var internationalProductInvoice = new InvoiceGenerator(PRODUCT_COST, new ForeignTaxCalculator());
LOGGER.info("Foreign Tax applied: {}", "" + internationalProductInvoice.getAmountWithTax());
var domesticProductInvoice = new InvoiceGenerator(PRODUCT_COST, new DomesticTaxCalculator());
LOGGER.info("Domestic Tax applied: {}", "" + domesticProductInvoice.getAmountWithTax());
```
## Class diagram
![alt text](./etc/class_diagram.png "Separated Interface")
## Applicability
Use the Separated interface pattern when
* You are developing a framework package, and your framework needs to call some application code through interfaces.
* You have separate packages implementing the functionalities which may be plugged in your client code at runtime or compile-time.
* Your code resides in a layer that is not allowed to call the interface implementation layer by rule. For example, a domain layer needs to call a data mapper.
## Tutorial
* [Separated Interface Tutorial](https://www.youtube.com/watch?v=d3k-hOA7k2Y)
## Credits
* [Martin Fowler](https://www.martinfowler.com/eaaCatalog/separatedInterface.html)
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321127420&linkId=e08dfb7f2cf6153542ef1b5a00b10abc)

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,36 @@
@startuml
package com.iluwatar.separatedinterface {
class App {
- LOGGER : Logger {static}
+ PRODUCT_COST : double {static}
+ App()
+ main(args : String[]) {static}
}
}
package com.iluwatar.separatedinterface.taxes {
class DomesticTaxCalculator {
+ TAX_PERCENTAGE : double {static}
+ DomesticTaxCalculator()
+ calculate(amount : double) : double
}
class ForeignTaxCalculator {
+ TAX_PERCENTAGE : double {static}
+ ForeignTaxCalculator()
+ calculate(amount : double) : double
}
}
package com.iluwatar.separatedinterface.invoice {
class InvoiceGenerator {
- amount : double
- taxCalculator : TaxCalculator
+ InvoiceGenerator(amount : double, taxCalculator : TaxCalculator)
+ getAmountWithTax() : double
}
interface TaxCalculator {
+ calculate(double) : double {abstract}
}
}
InvoiceGenerator --> "-taxCalculator" TaxCalculator
DomesticTaxCalculator ..|> TaxCalculator
ForeignTaxCalculator ..|> TaxCalculator
@enduml

View File

@@ -0,0 +1,71 @@
<?xml version="1.0"?>
<!--
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 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.24.0-SNAPSHOT</version>
</parent>
<artifactId>separated-interface</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</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.separatedinterface.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,62 @@
/*
* 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.separatedinterface;
import com.iluwatar.separatedinterface.invoice.InvoiceGenerator;
import com.iluwatar.separatedinterface.taxes.DomesticTaxCalculator;
import com.iluwatar.separatedinterface.taxes.ForeignTaxCalculator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>The Separated Interface pattern encourages to separate the interface definition and
* implementation in different packages. This allows the client to be completely unaware of the
* implementation.</p>
*
* <p>In this class the {@link InvoiceGenerator} class is injected with different instances of
* {@link com.iluwatar.separatedinterface.invoice.TaxCalculator} implementations located in separate
* packages, to receive different responses for both of the implementations.</p>
*/
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
public static final double PRODUCT_COST = 50.0;
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
//Create the invoice generator with product cost as 50 and foreign product tax
var internationalProductInvoice = new InvoiceGenerator(PRODUCT_COST,
new ForeignTaxCalculator());
LOGGER.info("Foreign Tax applied: {}", "" + internationalProductInvoice.getAmountWithTax());
//Create the invoice generator with product cost as 50 and domestic product tax
var domesticProductInvoice = new InvoiceGenerator(PRODUCT_COST, new DomesticTaxCalculator());
LOGGER.info("Domestic Tax applied: {}", "" + domesticProductInvoice.getAmountWithTax());
}
}

View File

@@ -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.separatedinterface.invoice;
/**
* InvoiceGenerator class generates an invoice, accepting the product cost and calculating the total
* price payable inclusive tax (calculated by {@link TaxCalculator}).
*/
public class InvoiceGenerator {
/**
* The TaxCalculator interface to calculate the payable tax.
*/
private final TaxCalculator taxCalculator;
/**
* The base product amount without tax.
*/
private final double amount;
public InvoiceGenerator(double amount, TaxCalculator taxCalculator) {
this.amount = amount;
this.taxCalculator = taxCalculator;
}
public double getAmountWithTax() {
return amount + taxCalculator.calculate(amount);
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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.separatedinterface.invoice;
public interface TaxCalculator {
double calculate(double amount);
}

View File

@@ -21,37 +21,20 @@
* THE SOFTWARE.
*/
package com.iluwatar.command;
package com.iluwatar.separatedinterface.taxes;
import com.iluwatar.separatedinterface.invoice.TaxCalculator;
/**
* InvisibilitySpell is a concrete command.
* TaxCalculator for Domestic goods with 20% tax.
*/
public class InvisibilitySpell implements Command {
public class DomesticTaxCalculator implements TaxCalculator {
private Target target;
public static final double TAX_PERCENTAGE = 20;
@Override
public void execute(Target target) {
target.setVisibility(Visibility.INVISIBLE);
this.target = target;
public double calculate(double amount) {
return amount * TAX_PERCENTAGE / 100.0;
}
@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";
}
}

View File

@@ -21,39 +21,20 @@
* THE SOFTWARE.
*/
package com.iluwatar.command;
package com.iluwatar.separatedinterface.taxes;
import com.iluwatar.separatedinterface.invoice.TaxCalculator;
/**
* ShrinkSpell is a concrete command.
* TaxCalculator for foreign goods with 60% tax.
*/
public class ShrinkSpell implements Command {
public class ForeignTaxCalculator implements TaxCalculator {
private Size oldSize;
private Target target;
public static final double TAX_PERCENTAGE = 60;
@Override
public void execute(Target target) {
oldSize = target.getSize();
target.setSize(Size.SMALL);
this.target = target;
public double calculate(double amount) {
return amount * TAX_PERCENTAGE / 100.0;
}
@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";
}
}

View File

@@ -21,21 +21,21 @@
* THE SOFTWARE.
*/
package com.iluwatar.circuitbreaker;
import static org.junit.jupiter.api.Assertions.assertEquals;
package com.iluwatar.separatedinterface;
import org.junit.jupiter.api.Test;
/**
* Monitoring Service test
*/
public class DelayedServiceTest {
import com.iluwatar.separatedinterface.App;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
/**
* Application test.
*/
class AppTest {
//Improves code coverage
@Test
public void testDefaultConstructor() {
var obj = new DelayedService();
assertEquals(obj.response(System.nanoTime()), "Delayed service is down");
void shouldExecuteWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.separatedinterface.invoice;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
public class InvoiceGeneratorTest {
private InvoiceGenerator target;
@Test
public void testGenerateTax() {
var productCost = 50.0;
var tax = 10.0;
TaxCalculator taxCalculatorMock = mock(TaxCalculator.class);
doReturn(tax).when(taxCalculatorMock).calculate(productCost);
target = new InvoiceGenerator(productCost, taxCalculatorMock);
Assertions.assertEquals(target.getAmountWithTax(), productCost + tax);
verify(taxCalculatorMock, times(1)).calculate(productCost);
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.separatedinterface.taxes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class DomesticTaxCalculatorTest {
private DomesticTaxCalculator target;
@Test
public void testTaxCalculation(){
target = new DomesticTaxCalculator();
var tax=target.calculate(100.0);
Assertions.assertEquals(tax,20.0);
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.separatedinterface.taxes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class ForeignTaxCalculatorTest {
private ForeignTaxCalculator target;
@Test
public void testTaxCalculation(){
target = new ForeignTaxCalculator();
var tax=target.calculate(100.0);
Assertions.assertEquals(tax,60.0);
}
}

View File

@@ -9,14 +9,16 @@ tags:
---
## Intent
Ensure a class only has one instance, and provide a global point of
access to it.
Ensure a class only has one instance, and provide a global point of access to it.
## Explanation
Real world example
> There can only be one ivory tower where the wizards study their magic. The same enchanted ivory tower is always used by the wizards. Ivory tower here is singleton.
> There can only be one ivory tower where the wizards study their magic. The same enchanted ivory
> tower is always used by the wizards. Ivory tower here is singleton.
In plain words
@@ -24,7 +26,9 @@ In plain words
Wikipedia says
> In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system.
> In software engineering, the singleton pattern is a software design pattern that restricts the
> instantiation of a class to one object. This is useful when exactly one object is needed to
> coordinate actions across the system.
**Programmatic Example**
@@ -38,7 +42,7 @@ public enum EnumIvoryTower {
}
```
Then in order to use
Then in order to use:
```java
var enumIvoryTower1 = EnumIvoryTower.INSTANCE;
@@ -47,9 +51,11 @@ assertEquals(enumIvoryTower1, enumIvoryTower2); // true
```
## Class diagram
![alt text](./etc/singleton.urm.png "Singleton pattern class diagram")
## Applicability
Use the Singleton pattern when
* There must be exactly one instance of a class, and it must be accessible to clients from a well-known access point

View File

@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
* <p>One of the risks of this pattern is that bugs resulting from setting a singleton up in a
* distributed environment can be tricky to debug, since it will work fine if you debug with a
* single classloader. Additionally, these problems can crop up a while after the implementation of
* a singleton, since they may start out synchronous and only become async with time, so you it may
* a singleton, since they may start out synchronous and only become async with time, so it may
* not be clear why you are seeing certain changes in behaviour.</p>
*
* <p>There are many ways to implement the Singleton. The first one is the eagerly initialized

View File

@@ -10,12 +10,17 @@ tags:
---
## Intent
As explained in the book [Game Programming Patterns](http://gameprogrammingpatterns.com/spatial-partition.html) by Bob Nystrom, spatial partition pattern helps to
> efficiently locate objects by storing them in a data structure organized by their positions.
As explained in the book [Game Programming Patterns](http://gameprogrammingpatterns.com/spatial-partition.html)
by Bob Nystrom, spatial partition pattern helps to efficiently locate objects by storing them in a
data structure organized by their positions.
## Explanation
Say, you are building a war game with hundreds, or maybe even thousands of players, who are clashing on the battle field. Each player's position is getting updated every frame. The simple way to handle all interactions taking place on the field is to check each player's position against every other player's position:
Say, you are building a war game with hundreds, or maybe even thousands of players, who are clashing
on the battle field. Each player's position is getting updated every frame. The simple way to handle
all interactions taking place on the field is to check each player's position against every other
player's position:
```java
public void handleMeLee(Unit units[], int numUnits) {
@@ -32,21 +37,33 @@ public void handleMeLee(Unit units[], int numUnits) {
}
```
This will include a lot of unnecessary checks between players which are too far apart to have any influence on each other. The nested loops gives this operation an O(n^2) complexity, which has to be performed every frame since many of the objects on the field may be moving each frame.
The idea behind the Spatial Partition design pattern is to enable quick location of objects using a data structure that is organised by their positions, so when performing an operation like the one above, every object's position need not be checked against all other objects' positions. The data structure can be used to store moving and static objects, though in order to keep track of the moving objects, their positions will have to be reset each time they move. This would mean having to create a new instance of the data structure each time an object moves, which would use up additional memory. The common data structures used for this design pattern are:
This will include a lot of unnecessary checks between players which are too far apart to have any
influence on each other. The nested loops gives this operation an O(n^2) complexity, which has to be
performed every frame since many of the objects on the field may be moving each frame. The idea
behind the Spatial Partition design pattern is to enable quick location of objects using a data
structure that is organised by their positions, so when performing an operation like the one above,
every object's position need not be checked against all other objects' positions. The data structure
can be used to store moving and static objects, though in order to keep track of the moving objects,
their positions will have to be reset each time they move. This would mean having to create a new
instance of the data structure each time an object moves, which would use up additional memory. The
common data structures used for this design pattern are:
* Grid
* Quad tree
* k-d tree
* K-d tree
* BSP
* Boundary volume hierarchy
In our implementation, we use the Quadtree data structure which will reduce the time complexity of finding the objects within a certain range from O(n^2) to O(nlogn), decreasing the computations required significantly in case of large number of objects.
In our implementation, we use the Quadtree data structure which will reduce the time complexity of
finding the objects within a certain range from O(n^2) to O(nlogn), decreasing the computations
required significantly in case of large number of objects.
## Class diagram
![alt text](./etc/spatial-partition.urm.png "Spatial Partition pattern class diagram")
## Applicability
This pattern can be used:
* When you need to keep track of a large number of objects' positions, which are getting updated every frame.

View File

@@ -24,7 +24,6 @@
package com.iluwatar.spatialpartition;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
/**

View File

@@ -9,36 +9,46 @@ tags:
---
## Also known as
Filter, Criteria
## Intent
Specification pattern separates the statement of how to match a
candidate, from the candidate object that it is matched against. As well as its
usefulness in selection, it is also valuable for validation and for building to
order.
Specification pattern separates the statement of how to match a candidate, from the candidate object
that it is matched against. As well as its usefulness in selection, it is also valuable for
validation and for building to order.
## Explanation
Real world example
> There is a pool of different creatures and we often need to select some subset of them.
> We can write our search specification such as "creatures that can fly", "creatures heavier than 500 kilograms", or as a combination of other search specifications, and then give it to the party that will perform the filtering.
> There is a pool of different creatures and we often need to select some subset of them. We can
> write our search specification such as "creatures that can fly", "creatures heavier than 500
> kilograms", or as a combination of other search specifications, and then give it to the party that
> will perform the filtering.
In Plain Words
> Specification pattern allows us to separate the search criteria from the object that performs the search.
> Specification pattern allows us to separate the search criteria from the object that performs the
> search.
Wikipedia says
> In computer programming, the specification pattern is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic.
> In computer programming, the specification pattern is a particular software design pattern,
> whereby business rules can be recombined by chaining the business rules together using boolean
> logic.
**Programmatic Example**
If we look at our creature pool example from above, we have a set of creatures with certain properties.
Those properties can be part of a pre-defined, limited set (represented here by the enums Size, Movement and Color); but they can also be continuous values (e.g. the mass of a Creature).
In this case, it is more appropriate to use what we call "parameterized specification", where the property value can be given as an argument when the Creature is instantiated, allowing for more flexibility.
A third option is to combine pre-defined and/or parameterized properties using boolean logic, allowing for near-endless selection possibilities (this is called "composite specification", see below).
The pros and cons of each approach are detailed in the table at the end of this document.
If we look at our creature pool example from above, we have a set of creatures with certain
properties. Those properties can be part of a pre-defined, limited set (represented here by the
enums Size, Movement and Color); but they can also be continuous values (e.g. the mass of a
Creature). In this case, it is more appropriate to use what we call "parameterized specification",
where the property value can be given as an argument when the Creature is instantiated, allowing for
more flexibility. A third option is to combine pre-defined and/or parameterized properties using
boolean logic, allowing for near-endless selection possibilities (this is called "composite
specification", see below). The pros and cons of each approach are detailed in the table at the end
of this document.
```java
public interface Creature {
@@ -50,7 +60,7 @@ public interface Creature {
}
```
And ``Dragon`` implementation looks like this.
And `Dragon` implementation looks like this.
```java
public class Dragon extends AbstractCreature {
@@ -61,7 +71,8 @@ public class Dragon extends AbstractCreature {
}
```
Now that we want to select some subset of them, we use selectors. To select creatures that fly, we should use ``MovementSelector``.
Now that we want to select some subset of them, we use selectors. To select creatures that fly, we
should use `MovementSelector`.
```java
public class MovementSelector extends AbstractSelector<Creature> {
@@ -79,7 +90,8 @@ public class MovementSelector extends AbstractSelector<Creature> {
}
```
On the other hand, when selecting creatures heavier than a chosen amount, we use ``MassGreaterThanSelector``.
On the other hand, when selecting creatures heavier than a chosen amount, we use
`MassGreaterThanSelector`.
```java
public class MassGreaterThanSelector extends AbstractSelector<Creature> {
@@ -111,7 +123,8 @@ But we could also use our parameterized selector like this:
.collect(Collectors.toList());
```
Our third option is to combine multiple selectors together. Performing a search for special creatures (defined as red, flying, and not small) could be done as follows:
Our third option is to combine multiple selectors together. Performing a search for special
creatures (defined as red, flying, and not small) could be done as follows:
```java
var specialCreaturesSelector =
@@ -123,8 +136,9 @@ Our third option is to combine multiple selectors together. Performing a search
**More on Composite Specification**
In Composite Specification, we will create custom instances of ``AbstractSelector`` by combining other selectors (called "leaves") using the three basic logical operators.
These are implemented in ``ConjunctionSelector``, ``DisjunctionSelector`` and ``NegationSelector``.
In Composite Specification, we will create custom instances of `AbstractSelector` by combining
other selectors (called "leaves") using the three basic logical operators. These are implemented in
`ConjunctionSelector`, `DisjunctionSelector` and `NegationSelector`.
```java
public abstract class AbstractSelector<T> implements Predicate<T> {
@@ -163,12 +177,14 @@ public class ConjunctionSelector<T> extends AbstractSelector<T> {
}
```
All that is left to do is now to create leaf selectors (be it hard-coded or parameterized ones) that are as generic as possible,
and we will be able to instantiate the ``AbstractSelector`` class by combining any amount of selectors, as exemplified above.
We should be careful though, as it is easy to make a mistake when combining many logical operators; in particular, we should pay attention to the priority of the operations.\
In general, Composite Specification is a great way to write more reusable code, as there is no need to create a Selector class for each filtering operation.
Instead, we just create an instance of ``AbstractSelector`` "on the spot", using tour generic "leaf" selectors and some basic boolean logic.
All that is left to do is now to create leaf selectors (be it hard-coded or parameterized ones) that
are as generic as possible, and we will be able to instantiate the ``AbstractSelector`` class by
combining any amount of selectors, as exemplified above. We should be careful though, as it is easy
to make a mistake when combining many logical operators; in particular, we should pay attention to
the priority of the operations. In general, Composite Specification is a great way to write more
reusable code, as there is no need to create a Selector class for each filtering operation. Instead,
we just create an instance of ``AbstractSelector`` "on the spot", using tour generic "leaf"
selectors and some basic boolean logic.
**Comparison of the different approaches**
@@ -181,9 +197,11 @@ Instead, we just create an instance of ``AbstractSelector`` "on the spot", using
| | | + Supports logical operations | - You still need to create the base classes used as leaves |
## Class diagram
![alt text](./etc/specification.png "Specification")
## Applicability
Use the Specification pattern when
* You need to select a subset of objects based on some criteria, and to refresh the selection at various times.

View File

@@ -9,15 +9,21 @@ tags:
---
## Also known as
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.
> 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
@@ -25,7 +31,10 @@ In plain words
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.
> 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**
@@ -126,17 +135,23 @@ And here is the full example how the mammoth behaves over time.
mammoth.observe();
mammoth.timePasses();
mammoth.observe();
// The mammoth gets angry!
// The mammoth is furious!
// The mammoth calms down.
// The mammoth is calm and peaceful.
```
Program output:
```java
The mammoth gets angry!
The mammoth is furious!
The mammoth calms down.
The mammoth is calm and peaceful.
```
## Class diagram
![alt text](./etc/state_1.png "State")
![alt text](./etc/state_urm.png "State")
## Applicability
Use the State pattern in either of the following cases
* An object's behavior depends on its state, and it must change its behavior at run-time depending on that state

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<class id="1" language="java" name="com.iluwatar.state.AngryState" project="state"
file="/state/src/main/java/com/iluwatar/state/AngryState.java" binary="false" corner="BOTTOM_RIGHT">
<position height="124" width="157" x="98" y="310"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.state.PeacefulState" project="state"
file="/state/src/main/java/com/iluwatar/state/PeacefulState.java" binary="false" corner="BOTTOM_RIGHT">
<position height="124" width="173" x="295" y="310"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="3" language="java" name="com.iluwatar.state.State" project="state"
file="/state/src/main/java/com/iluwatar/state/State.java" binary="false" corner="BOTTOM_RIGHT">
<position height="106" width="136" x="138" y="674"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="4" language="java" name="com.iluwatar.state.Mammoth" project="state"
file="/state/src/main/java/com/iluwatar/state/Mammoth.java" binary="false" corner="BOTTOM_RIGHT">
<position height="160" width="177" x="138" y="474"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<realization id="5">
<bendpoint x="107" y="474"/>
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="3"/>
</realization>
<association id="6">
<end type="SOURCE" refId="4" navigable="false">
<attribute id="7" name="state"/>
<multiplicity id="8" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="3" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="9">
<bendpoint x="345" y="474"/>
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="3"/>
</realization>
<association id="10">
<end type="SOURCE" refId="1" navigable="false">
<attribute id="11" name="mammoth"/>
<multiplicity id="12" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="13">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="14" name="mammoth"/>
<multiplicity id="15" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

BIN
state/etc/state_urm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -9,17 +9,20 @@ tags:
---
## Also known as
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.
> Slaying dragons is a dangerous job. With experience it becomes easier. Veteran
> dragonslayers have developed different fighting strategies against different types of dragons.
In plain words
@@ -27,7 +30,8 @@ In plain words
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.
> 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**
@@ -71,7 +75,8 @@ public class SpellStrategy implements DragonSlayingStrategy {
}
```
And here is the mighty dragonslayer who is able to pick his fighting strategy based on the opponent.
And here is the mighty dragonslayer, who is able to pick his fighting strategy based on the
opponent.
```java
public class DragonSlayer {
@@ -92,7 +97,7 @@ public class DragonSlayer {
}
```
Finally here's dragonslayer in action.
Finally here's the dragonslayer in action.
```java
LOGGER.info("Green dragon spotted ahead!");
@@ -104,19 +109,25 @@ Finally here's dragonslayer in action.
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!
```
Program output:
```
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
![alt text](./etc/strategy_1.png "Strategy")
![alt text](./etc/strategy_urm.png "Strategy")
## Applicability
Use the Strategy pattern when
* Many related classes differ only in their behavior. Strategies provide a way to configure a class either one of many behaviors

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,75 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<interface id="1" language="java" name="com.iluwatar.strategy.DragonSlayingStrategy" project="strategy"
file="/strategy/src/main/java/com/iluwatar/strategy/DragonSlayingStrategy.java" binary="false" corner="BOTTOM_RIGHT">
<position height="88" width="186" x="525" y="428"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="2" language="java" name="com.iluwatar.strategy.ProjectileStrategy" project="strategy"
file="/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java" binary="false" corner="BOTTOM_RIGHT">
<position height="106" width="151" x="218" y="264"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.strategy.SpellStrategy" project="strategy"
file="/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java" binary="false" corner="BOTTOM_RIGHT">
<position height="106" width="125" x="409" y="264"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="4" language="java" name="com.iluwatar.strategy.DragonSlayer" project="strategy"
file="/strategy/src/main/java/com/iluwatar/strategy/DragonSlayer.java" binary="false" corner="BOTTOM_RIGHT">
<position height="124" width="284" x="574" y="264"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="5" language="java" name="com.iluwatar.strategy.MeleeStrategy" project="strategy"
file="/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java" binary="false" corner="BOTTOM_RIGHT">
<position height="106" width="127" x="898" y="264"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<realization id="6">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="1"/>
</realization>
<realization id="7">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="1"/>
</realization>
<realization id="8">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="1"/>
</realization>
<association id="9">
<end type="SOURCE" refId="4" navigable="false">
<attribute id="10" name="strategy"/>
<multiplicity id="11" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -9,21 +9,30 @@ 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.
## 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.
> 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.
> 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.
> 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**
@@ -120,9 +129,11 @@ And finally we show how the halfling thief utilizes the different stealing metho
```
## Class diagram
![alt text](./etc/template-method_1.png "Template Method")
![alt text](./etc/template_method_urm.png "Template Method")
## Applicability
The Template Method pattern should be used
* To implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<class id="1" language="java" name="com.iluwatar.templatemethod.SubtleMethod" project="template-method"
file="/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="142" width="174" x="-90" y="683"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.templatemethod.StealingMethod" project="template-method"
file="/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="159" width="177" x="17" y="484"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.templatemethod.HalflingThief" project="template-method"
file="/template-method/src/main/java/com/iluwatar/templatemethod/HalflingThief.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="124" width="235" x="17" y="320"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="4" language="java" name="com.iluwatar.templatemethod.HitAndRunMethod" project="template-method"
file="/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="142" width="174" x="124" y="683"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<association id="5">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="6" name="method"/>
<multiplicity id="7" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="2" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="8">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="2"/>
</generalization>
<generalization id="9">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="2"/>
</generalization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -9,16 +9,19 @@ tags:
---
## Intent
It is often the case that tasks to be executed are short-lived and
the number of tasks is large. Creating a new thread for each task would make
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.
It is often the case that tasks to be executed are short-lived and the number of tasks is large.
Creating a new thread for each task would make 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.
> 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
@@ -26,11 +29,18 @@ In plain words
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.
> 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.
Let's first look at our task hierarchy. We have a base class and then concrete `CoffeeMakingTask`
and `PotatoPeelingTask`.
```java
public abstract class Task {
@@ -88,8 +98,8 @@ public class PotatoPeelingTask extends Task {
}
```
Next we present a runnable Worker class that the thread pool will utilize to handle all the potato peeling and coffee
making.
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 {
@@ -156,9 +166,11 @@ Now we are ready to show the full example in action.
```
## Class diagram
![alt text](./etc/thread-pool.png "Thread Pool")
![alt text](./etc/thread_pool_urm.png "Thread Pool")
## Applicability
Use the Thread Pool pattern when
* You have a large number of short-lived tasks to be executed in parallel

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<class id="1" language="java" name="com.iluwatar.Task" project="thread-pool"
file="/thread-pool/src/main/java/com/iluwatar/Task.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="343" y="579"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.Worker" project="thread-pool"
file="/thread-pool/src/main/java/com/iluwatar/Worker.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="344" y="389"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.CoffeeMakingTask" project="thread-pool"
file="/thread-pool/src/main/java/com/iluwatar/CoffeeMakingTask.java" binary="false" corner="BOTTOM_RIGHT">
<position height="124" width="156" x="194" y="717"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="4" language="java" name="com.iluwatar.PotatoPeelingTask" project="thread-pool"
file="/thread-pool/src/main/java/com/iluwatar/PotatoPeelingTask.java" binary="false" corner="BOTTOM_RIGHT">
<position height="124" width="169" x="390" y="717"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<association id="5">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="6" name="task"/>
<multiplicity id="7" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="8">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="1"/>
</generalization>
<generalization id="9">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="1"/>
</generalization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -10,12 +10,15 @@ tags:
---
## 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.
> 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
@@ -23,7 +26,9 @@ In plain words
[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.
> 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**
@@ -77,7 +82,8 @@ public final class CallsCount {
}
```
Next we introduce the service that the tenants are calling. To track the call count we use the throttler timer.
Next we introduce the service that the tenants are calling. To track the call count we use the
throttler timer.
```java
public interface Throttler {
@@ -134,7 +140,8 @@ class B2BService {
}
```
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.
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) {
@@ -171,9 +178,11 @@ Now we are ready to see the full example in action. Tenant Adidas is rate-limite
## Class diagram
![alt text](./etc/throttling-pattern.png "Throttling pattern class diagram")
![alt text](./etc/throttling_urm.png "Throttling pattern class diagram")
## Applicability
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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -1,88 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.2.2" 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.throttling.CallsCount" project="throttling"
file="/throttling/src/main/java/com/iluwatar/throttling/CallsCount.java" binary="false" corner="BOTTOM_RIGHT">
<position height="211" width="256" x="656" y="228"/>
<display autosize="false" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.throttling.Tenant" project="throttling"
file="/throttling/src/main/java/com/iluwatar/throttling/Tenant.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="465" y="524"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.throttling.B2BService" project="throttling"
file="/throttling/src/main/java/com/iluwatar/throttling/B2BService.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="464" y="192"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="4" language="java" name="com.iluwatar.throttling.timer.Throttler" project="throttling"
file="/throttling/src/main/java/com/iluwatar/throttling/timer/Throttler.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="167" y="174"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="5" language="java" name="com.iluwatar.throttling.timer.ThrottleTimerImpl" project="throttling"
file="/throttling/src/main/java/com/iluwatar/throttling/timer/ThrottleTimerImpl.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="166" y="396"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<association id="6">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="7" name="callsCount"/>
<multiplicity id="8" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<dependency id="9">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="4"/>
</dependency>
<dependency id="10">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="2"/>
</dependency>
<association id="11">
<end type="SOURCE" refId="5" navigable="false">
<attribute id="12" name="callsCount"/>
<multiplicity id="13" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<dependency id="14">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="1"/>
</dependency>
<realization id="15">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="4"/>
</realization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Some files were not shown because too many files have changed in this diff Show More