Compare commits

..

168 Commits
1.7.0 ... 1.9.0

Author SHA1 Message Date
e4458c1b17 Achieved milestone 1.9.0 2016-01-01 20:37:50 +02:00
30e09a8dd9 Merge pull request #331 from fluxw42/master
Add unit tests for remaining patterns
2016-01-01 18:24:58 +02:00
f987c7253b Merge pull request #330 from mikulucky/master
Delegation Pattern
2016-01-01 16:49:01 +02:00
ad714294dd Clean up javaDocs on Printer.java #324 2015-12-30 20:08:52 +00:00
623c2081cd Update App.java to have more information on the pattern #324 2015-12-30 20:07:42 +00:00
542a832a66 Added tests for twin pattern 2015-12-30 20:55:22 +01:00
444eb07e26 Added tests for tolerant-reader pattern 2015-12-30 20:55:22 +01:00
fd8c05846f Added tests for thread-pool pattern
Fixed concurrency problem in id generation of Task
2015-12-30 20:55:22 +01:00
47709e24b9 Added tests for template-method pattern 2015-12-30 20:55:22 +01:00
09d3a82884 Added tests for visitor pattern 2015-12-30 20:55:22 +01:00
997bfba3b2 Added tests for strategy pattern 2015-12-30 20:55:22 +01:00
6326c1742d Added tests for step-builder pattern 2015-12-30 20:55:22 +01:00
2e2707862a Fixed checkstyle code quality issues 2015-12-30 20:55:21 +01:00
1d9aff4071 Added tests for state pattern 2015-12-30 20:55:21 +01:00
5611f26c77 Added tests for specification pattern 2015-12-30 20:55:21 +01:00
d0cdf84936 Added tests for singleton pattern 2015-12-30 20:55:21 +01:00
a375b2d28b Added tests for service-locator pattern
Fix NPE when requested service is unknown
2015-12-30 20:55:21 +01:00
fcfdbe71f5 Added tests for service-layer pattern 2015-12-30 20:55:21 +01:00
52c483f1d0 Added tests for servant pattern 2015-12-30 20:46:40 +01:00
c72faeb67e Added tests for resource-acquisition-is-initialization pattern 2015-12-30 20:46:40 +01:00
299d612b9b Added tests for proxy pattern 2015-12-30 20:46:40 +01:00
9d4c3154b1 Added tests for prototype pattern 2015-12-30 20:46:40 +01:00
a3b1265921 Added tests for property pattern 2015-12-30 20:46:40 +01:00
dca68511e8 Added tests for producer-consumer pattern 2015-12-30 20:46:40 +01:00
42a1dc69ca Added tests for private-class-data pattern 2015-12-30 20:46:40 +01:00
47e1cd710c Added tests for poison-pull pattern 2015-12-30 20:46:40 +01:00
b3d1c2b2ed Added tests for generic observer pattern 2015-12-30 20:46:40 +01:00
3e20a2afa8 Added tests for observer pattern 2015-12-30 20:46:39 +01:00
4c938ab8a5 Added tests for object-pool pattern 2015-12-30 20:46:39 +01:00
8f6f171a3f Added tests for null-object pattern 2015-12-30 20:46:39 +01:00
b4dcec45ef Added tests for multiton pattern 2015-12-30 20:46:39 +01:00
dec5ff22fc Update index.md #324 2015-12-30 18:26:32 +00:00
7b323e9cb4 Updated Class Diagram #324 2015-12-30 17:55:42 +00:00
a0af0a1a76 Merge pull request #325 from DevFactory/utility-classes-should-not-have-public-constructors-fix-3
Utility classes should not have public constructors
2015-12-30 19:55:25 +02:00
7c6e1fc3d8 Merge pull request #323 from DevFactory/code-quality-fix-5
Code quality fixes
2015-12-30 19:47:19 +02:00
45cecf68c4 Updated Class Diagram #324 2015-12-30 17:34:50 +00:00
d7580f5530 Review Comments #324 2015-12-30 16:56:47 +00:00
58b8a06561 Merge branch 'master' of https://github.com/iluwatar/java-design-patterns
* 'master' of https://github.com/iluwatar/java-design-patterns:
  Define checkstyle suppression filter location in maven plugin configuration
2015-12-30 16:17:07 +00:00
befe509582 Define checkstyle suppression filter location in maven plugin configuration 2015-12-30 17:58:03 +02:00
17b889c31d Update to match checkStyle rules added for test packages #324 2015-12-29 22:07:52 +00:00
52192de909 Merge commit '8b020837eaacc9ed4497f3c2461984935314bfb8' into add-delegation-pattern
* commit '8b020837eaacc9ed4497f3c2461984935314bfb8':
  Checkstyle corrections
  Checkstyle configuration to look into test classes too
  Rephrase readme title
  Corrected difficulty for Visitor #213
  Add performance tag to relevant patterns #213
  Categorize and tag all patterns #213
  Update CONTRIBUTING.MD
  Update CONTRIBUTING.MD
  add-contributing
2015-12-29 22:00:56 +00:00
8b020837ea Checkstyle corrections 2015-12-29 21:34:27 +02:00
b369812511 Checkstyle configuration to look into test classes too 2015-12-29 13:27:49 +02:00
52d6e20ad9 Readd File to Index#324 2015-12-28 22:13:05 +00:00
749880e3b9 Messed up indexes #324 2015-12-28 22:12:11 +00:00
41593774c6 CheckStyle reporting strange error about classname, suspect caching, forcing a clean build #324 2015-12-28 21:56:41 +00:00
0bc722f797 Fix CheckStyle #324 2015-12-28 20:34:28 +00:00
fcadb223ce Merge branch 'add-delegation-pattern'
* add-delegation-pattern:
  Add java documentation #324
  Populate the index.md for the delegate module #324
  Move App.java to correct Package #324
  Move App.java to correct Package #324
  Generic For AbstractPrinterController #324
  Generate UML for delegation pattern
  Add simple tests for delegate pattern #324
  Make AppTest.java match other patterns and update AppTest.java to match other patterns #324
  3am Code is starting to show. Get this build working. #324
  Created a unit test for build, added junit to pom for delegation maven module. #324
  Add template index.md for population later #324
  Added skeleton code for delegation pattern #324
  Create maven module for delegation pattern #324
2015-12-28 20:01:14 +00:00
c842f88eb7 Add java documentation #324 2015-12-28 19:23:00 +00:00
6d516d5124 Populate the index.md for the delegate module #324 2015-12-28 18:16:58 +00:00
9544dd321a Rephrase readme title 2015-12-28 20:09:53 +02:00
d623e9c8fd Corrected difficulty for Visitor #213 2015-12-28 19:52:10 +02:00
e27de33f75 Add performance tag to relevant patterns #213 2015-12-28 16:07:43 +02:00
7ac7e3b097 Categorize and tag all patterns #213 2015-12-28 15:52:44 +02:00
fefb51c46e Merge pull request #329 from zafarella/patch-1
add-contributing
2015-12-28 09:34:14 +02:00
58547fae8f Update CONTRIBUTING.MD
real fix :)
2015-12-27 22:44:15 -05:00
30676f8d4f Update CONTRIBUTING.MD
fix the typo
2015-12-27 22:42:42 -05:00
483db04cef Move App.java to correct Package #324 2015-12-27 20:12:04 +00:00
fac10dc454 Move App.java to correct Package #324 2015-12-27 20:10:48 +00:00
982f9f5e31 Generic For AbstractPrinterController #324 2015-12-27 20:09:33 +00:00
74d1823078 Merge pull request #1 from fluxw42/add-delegation-pattern
Generate UML for delegation pattern
2015-12-27 20:03:42 +00:00
f59f18091e Remove Coverity scan #327 2015-12-27 21:23:09 +02:00
5f033be54f Fix PMD violations #327 2015-12-27 21:21:57 +02:00
df911baf36 Added maven pmd plugin and configured it to fail build when violations are found #327 2015-12-27 21:21:31 +02:00
32e7181478 Generate UML for delegation pattern 2015-12-27 18:13:34 +01:00
c8a750df49 Fixing squid:S1118 - Utility classes should not have public constructors 2015-12-27 21:17:13 +05:00
191078735f Fixing squid:S1698 - Objects should be compared with equals() and squid:HiddenFieldCheck - Local variables should not shadow class fields 2015-12-27 21:12:35 +05:00
fa6a6006c3 add-contributing
so github will show link whenever PR are made
2015-12-27 11:03:21 -05:00
a49dbefb56 Add simple tests for delegate pattern #324 2015-12-27 14:10:49 +00:00
fb0617e9c5 Make AppTest.java match other patterns and update AppTest.java to match other patterns #324 2015-12-27 13:59:50 +00:00
84fd2348ea 3am Code is starting to show. Get this build working. #324 2015-12-26 23:02:45 +00:00
f0ff8ad4b3 Created a unit test for build, added junit to pom for delegation maven module. #324 2015-12-26 22:55:12 +00:00
c3184255bf Add template index.md for population later #324 2015-12-26 22:29:45 +00:00
bdacfe30c1 Added skeleton code for delegation pattern #324 2015-12-26 22:20:53 +00:00
30363cbb7f Create maven module for delegation pattern #324 2015-12-26 22:05:12 +00:00
3d8c64d76d Merge pull request #322 from DevFactory/dead-stores-fix-1
Fixing squid:S1854 - Dead stores should be removed
2015-12-26 13:38:16 +02:00
78fcd63271 Merge branch 'fluxw42-master' 2015-12-26 13:07:37 +02:00
30bc25b5bf Merge branch 'master' of https://github.com/fluxw42/java-design-patterns into fluxw42-master
Conflicts:
	monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java
2015-12-26 12:36:38 +02:00
6b1356a160 Add Gitter link to README.md 2015-12-26 12:12:33 +02:00
cec9a99410 Adjust checkstyle rules. Make checkstyle fail the build when violations are found. Correct all current checkstyle violations. 2015-12-25 23:49:28 +02:00
9fbb085985 Checkstyle fails the build when violations are detected 2015-12-23 13:20:39 +02:00
6e496e7c86 Fixing squid:S1854 - Dead stores should be removed 2015-12-23 13:41:38 +05:00
e3e0e32e92 Fixed failing unit test
LoadBalancer has several static fields, this could cause problems since
JUnit tests are executed concurrently.
2015-12-21 14:35:36 +01:00
531158c836 Added tests for monostate pattern 2015-12-21 13:29:25 +01:00
6fe01e73b2 Add additional tests for model-view-presenter pattern 2015-12-21 12:17:20 +01:00
69c9374669 Added tests for model-view-controller pattern 2015-12-20 14:31:36 +01:00
a57a71b09c Added tests for memento pattern 2015-12-20 10:39:02 +01:00
719f80a0d6 Added tests for mediator pattern 2015-12-20 00:00:07 +01:00
2c82bd9450 Added tests for lazy-loading pattern 2015-12-19 21:44:50 +01:00
5948a82cf2 Added tests for layers pattern 2015-12-19 21:44:50 +01:00
a0151826ad Changed page-index to 1 for a better structured navbar 2015-12-19 17:35:49 +01:00
33fe90d177 Merge pull request #317 from fluxw42/master
Added some more JUnit tests
2015-12-15 18:48:31 +02:00
25cacdbbc9 Added tests for iterator pattern 2015-12-14 15:14:48 +01:00
323e4c8751 Added tests for interpreter pattern 2015-12-14 12:46:07 +01:00
de78490d29 Added tests for intercepting-filter pattern 2015-12-13 15:18:13 +01:00
9059d2b96c Added proper tests for front-controller pattern 2015-12-13 13:58:39 +01:00
dbca06a9e7 Added proper tests for half-sync-half-async 2015-12-13 13:04:17 +01:00
ca14e8ddad Add proper tests for flyweight pattern 2015-12-13 11:42:25 +01:00
3dc370e2d1 Add proper tests for flux pattern 2015-12-12 22:43:47 +01:00
4181514c65 Add proper tests for fluent-interface pattern and fixed a little bug 2015-12-12 20:16:10 +01:00
df69a8f986 Add proper tests for execute-around pattern 2015-12-12 20:14:59 +01:00
885d5bb7dd Add proper unit tests for event-aggregator pattern 2015-12-12 20:13:25 +01:00
1b74e0ff67 Merge pull request #316 from fluxw42/master
Added more JUnit tests
2015-12-12 09:44:41 +02:00
94f80d1868 Add proper unit tests for composite pattern 2015-12-11 20:24:56 +01:00
c837ffe234 Add proper unit tests for double-dispatch pattern 2015-12-11 20:24:56 +01:00
2edc1898b1 Add proper unit tests for double-checked-locking pattern 2015-12-11 20:24:56 +01:00
29fc56002a Add proper unit tests for dependency-injection pattern 2015-12-11 20:24:56 +01:00
0643289c74 Dependency org.mockito:mockito-core should have scope 'test' 2015-12-11 20:24:56 +01:00
2c0a5e8703 Add proper unit tests for facade pattern 2015-12-11 20:24:56 +01:00
a3372febd0 Merge pull request #315 from Deses/master
Just  Just a quick fix for the Front-controller pattern.
2015-12-11 20:46:18 +02:00
7df65adbfc Just Just a quick fix for the Front-controller pattern. 2015-12-11 13:54:32 +01:00
d7dd8d42af Merge pull request #312 from fluxw42/master
Add proper unit tests for chain, bridge and builder pattern #293
2015-12-09 20:23:51 +02:00
4e7a8fdaca Merge pull request #314 from iluwatar/fstrategy
Functional approach to Strategy pattern #310
2015-12-08 23:27:01 +02:00
6e59d9554f Functional approach to Strategy pattern #310 2015-12-08 23:25:05 +02:00
e85308fbc4 Merge pull request #309 from JuhoKang/adapterdev
Attempt to solve issue #292
2015-12-08 20:53:15 +02:00
10a94cc91c Add proper unit tests for decorator pattern 2015-12-07 20:37:27 +01:00
e2d8079b36 Fixed code to follow coding conventions 2015-12-07 10:35:32 +09:00
8367c83aca Add proper unit tests for bridge pattern #293 2015-12-06 23:48:48 +01:00
875e5b872c Add proper unit tests for builder pattern #293 2015-12-06 23:14:07 +01:00
1fa617d08d Add proper unit tests for chain pattern #293 2015-12-06 22:58:16 +01:00
6c1f025d0f Apply code formatting rules to async-method-invocation example tests 2015-12-06 21:42:06 +02:00
601964a2d1 Merge pull request #311 from fluxw42/master
Add proper unit tests for async-method-invocation #293
2015-12-06 21:33:03 +02:00
1884df525b Add proper unit tests for async-method-invocation #293 2015-12-06 13:43:01 +01:00
507b89d5e4 Apply project coding conventions to Repository example 2015-12-05 21:26:30 +02:00
02d6754804 Merge pull request #308 from neonds/spring-annotation-config
Annotation Config was added.
2015-12-05 21:12:12 +02:00
e709a196bd Set version number for next development iteration 2015-12-02 23:26:07 +02:00
f018d13c39 Set version number for monthly release 2015-12-02 23:21:44 +02:00
33b41f872e The new java files was not added.. #292 2015-12-02 18:45:08 +09:00
119d264779 issue #292 2015-12-01 16:00:31 +09:00
9a85dfe9df Annotation Config was added. Now AppConfig contains a Main Method (same logic in App.java) to execute 2015-11-27 21:23:55 -06:00
5bec63659f Merge pull request #305 from hoswey/master
Fix #216 Repository vs DAO
2015-11-28 00:09:27 +02:00
9d4fff6029 Fix #216 Repository vs DAO 2015-11-27 11:22:33 +08:00
092d48d150 Merge pull request #294 from DevFactory/release1
Unit tests for adapter, business-delegate, factory-method and command modules
2015-11-22 08:44:13 +02:00
6b99f2669e Added capability for test coverage report generation and steps to do so. 2015-11-22 05:47:36 +05:30
ced317bc9d Added UnitTest cases for factory method. 2015-11-22 05:46:14 +05:30
e5614e5a20 Added UnitTest cases for command. 2015-11-22 05:45:45 +05:30
012b638023 Added UnitTest cases for business delegate. 2015-11-22 05:45:14 +05:30
b577890db4 Added UnitTest cases for adapter. 2015-11-22 05:44:45 +05:30
8ba0192864 Add EIP tag to relevant patterns 2015-11-21 16:34:59 +02:00
899b9617c9 Merge pull request #295 from iluwatar/JaXt0r-publish-subscribe
Jaxt0r publish subscribe
2015-11-21 16:10:34 +02:00
d0fca6d318 Improve the example and documentation 2015-11-21 16:05:55 +02:00
7885380633 Apply Google coding conventions 2015-11-21 15:12:31 +02:00
230c02fb24 Fix spelling 2015-11-21 15:09:23 +02:00
b97e9207a7 Update version number for Publish-Subscribe 2015-11-21 15:03:27 +02:00
8519e13de6 Merge branch 'publish-subscribe' of https://github.com/JaXt0r/java-design-patterns into JaXt0r-publish-subscribe
Conflicts:
	pom.xml
2015-11-21 14:55:02 +02:00
ba3f583467 Merge pull request #290 from hoswey/master
implements Twin design pattern #63
2015-11-16 21:09:41 +02:00
142274f3f7 implements Twin design pattern #63, add credit and rephrase the comments 2015-11-16 10:01:03 +08:00
5b06c52fff Merge pull request #291 from tainarareis/abstract-factory
Issue #286: Added main method in Abstract Factory Example
2015-11-14 21:33:48 +02:00
033f8e740d Merge pull request #287 from l-cortese/master
log4j.xml relocated and excluded from JAR
2015-11-14 21:19:50 +02:00
34528173b7 Issue #286: Added main method in Abstract Factory Example 2015-11-14 16:48:19 -02:00
fdbfa9e8ee implements Twin design pattern #63 2015-11-14 17:38:35 +08:00
bf65168776 corrected typos in the comments 2015-11-13 22:35:30 +01:00
043a610754 log4j.xml relocated due to visibility issues and excluded from generated
JAR. pom.xml formatted according to coding conventions
2015-11-13 18:34:10 +01:00
27199325ec Merge pull request #284 from hoswey/master
correct the package name for producer-consumer test folder
2015-11-07 11:48:51 +02:00
960b568fbb correct the package name for producer-consumer test folder 2015-11-07 15:51:16 +08:00
c02e65fa3a Added alias names for GOF patterns #267 2015-11-04 21:13:32 +02:00
fe63c9cec4 Merge pull request #281 from ankurkaushal/master
Reformat according to google style guide
2015-11-02 21:39:17 +02:00
7e4f04602e Merge pull request #282 from l-cortese/master
Corrected Freddy's last name
2015-11-02 21:36:20 +02:00
306b1f3d31 Reformat rest of the design patterns - Issue #224 2015-11-01 21:29:13 -05:00
449340bd2b Reformat business-delegate, callback, chain, command, composite, dao, decorator & dependency-injection patterns. 2015-11-01 18:48:43 -05:00
3af06a3a3a Reformat builder pattern - issue #224 2015-11-01 18:03:29 -05:00
e7b6542134 Reformat bridge design pattern - Issue #224 2015-11-01 17:43:54 -05:00
16a8c85af6 Reformat Async Method Invocation - Issue #224 2015-11-01 17:33:25 -05:00
95c16200e7 Reformat Adapter Pattern - Issue #224 2015-11-01 17:26:57 -05:00
c0c21ebd91 Reformat Abstract Factory - Issue #224 2015-11-01 17:18:39 -05:00
25c1f87d46 Corrected Freddy's second name 2015-11-01 23:15:57 +01:00
c6ca80b25f Bump version number for the next development iteration 2015-11-01 22:22:31 +02:00
ac5c1967df #161 - Publish Subscribe Channel realisation.Heavily based on iluwatars Message Channel implementation (#143). 2015-10-25 07:11:34 +01:00
818 changed files with 20731 additions and 9523 deletions

13
CODE_COVERAGE.md Normal file
View File

@ -0,0 +1,13 @@
# Code Coverage Report generation
To generate the code coverage report, execute the following command:
> mvn clean verify
This will generate code coverage report in each of the modules. In order to view the same, open the following file in your browser.
> target/site/jacoco/index.html
Please note that the above folder is created under each of the modules. For example:
* adapter/target/site/jacoco/index.html
* busniess-delegate/target/site/jacoco/index.html

4
CONTRIBUTING.MD Normal file
View File

@ -0,0 +1,4 @@
This is great you have something to contribute!
Before going any further please read the [wiki](https://github.com/iluwatar/java-design-patterns/wiki)
with conventions and rules we used for this project.

View File

@ -2,11 +2,10 @@
that smart and dearly wants an empty line before a heading to be able to
display it as such, e.g. website) -->
# Design pattern samples in Java
# Design patterns implemented in Java
[![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master)](https://travis-ci.org/iluwatar/java-design-patterns)
[![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5634/badge.svg)](https://scan.coverity.com/projects/5634)
[![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)
# Introduction
@ -40,7 +39,7 @@ patterns by any of the following approaches
# How to contribute
If you are willing to contribute to the project you will find the relevant information in our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki).
If you are willing to contribute to the project you will find the relevant information in our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki). We will help you and answer your questions in the [Gitter chatroom](https://gitter.im/iluwatar/java-design-patterns).
# Credits

View File

@ -7,8 +7,11 @@ categories: Creational
tags:
- Java
- Gang Of Four
- Difficulty-Intermediate
---
**Also known as:** Kit
**Intent:** Provide an interface for creating families of related or dependent
objects without specifying their concrete classes.

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.7.0</version>
<version>1.9.0</version>
</parent>
<artifactId>abstract-factory</artifactId>
<dependencies>

View File

@ -1,82 +1,105 @@
package com.iluwatar.abstractfactory;
/**
*
* The Abstract Factory pattern provides a way to encapsulate a group of individual
* factories that have a common theme without specifying their concrete classes. In
* normal usage, the client software creates a concrete implementation of the abstract
* factory and then uses the generic interface of the factory to create the concrete
* objects that are part of the theme. The client does not know (or care) which
* concrete objects it gets from each of these internal factories, since it uses only
* the generic interfaces of their products. This pattern separates the details of
* implementation of a set of objects from their general usage and relies on object
* composition, as object creation is implemented in methods exposed in the factory
* interface.
* The Abstract Factory pattern provides a way to encapsulate a group of individual factories that have a common theme
* without specifying their concrete classes. In normal usage, the client software creates a concrete implementation of
* the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part
* of the theme. The client does not know (or care) which concrete objects it gets from each of these internal
* factories, since it uses only the generic interfaces of their products. This pattern separates the details of
* implementation of a set of objects from their general usage and relies on object composition, as object creation is
* implemented in methods exposed in the factory interface.
* <p>
* The essence of the Abstract Factory pattern is a factory interface
* ({@link KingdomFactory}) and its implementations ({@link ElfKingdomFactory},
* {@link OrcKingdomFactory}). The example uses both concrete implementations to
* create a king, a castle and an army.
* The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) 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 {
private King king;
private Castle castle;
private Army army;
private King king;
private Castle castle;
private Army army;
/**
* Creates kingdom
*/
public void createKingdom(final KingdomFactory factory) {
setKing(factory.createKing());
setCastle(factory.createCastle());
setArmy(factory.createArmy());
}
ElfKingdomFactory getElfKingdomFactory() {
return new ElfKingdomFactory();
}
OrcKingdomFactory getOrcKingdomFactory() {
return new OrcKingdomFactory();
}
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;
}
/**
* Program entry point
*
* @param args
* command line args
*/
public static void main(String[] args) {
App app = new App();
System.out.println("Elf Kingdom");
KingdomFactory elfKingdomFactory;
elfKingdomFactory = app.getElfKingdomFactory();
app.createKingdom(elfKingdomFactory);
System.out.println(app.getArmy().getDescription());
System.out.println(app.getCastle().getDescription());
System.out.println(app.getKing().getDescription());
System.out.println("\nOrc Kingdom");
KingdomFactory orcKingdomFactory;
orcKingdomFactory = app.getOrcKingdomFactory();
app.createKingdom(orcKingdomFactory);
System.out.println(app.getArmy().getDescription());
System.out.println(app.getCastle().getDescription());
System.out.println(app.getKing().getDescription());
}
/**
* Creates kingdom
* @param factory
*/
public void createKingdom(final KingdomFactory factory) {
setKing(factory.createKing());
setCastle(factory.createCastle());
setArmy(factory.createArmy());
}
ElfKingdomFactory getElfKingdomFactory() {
return new ElfKingdomFactory();
}
OrcKingdomFactory getOrcKingdomFactory() {
return new OrcKingdomFactory();
}
King getKing(final KingdomFactory factory) {
return factory.createKing();
}
Castle getCastle(final KingdomFactory factory) {
return factory.createCastle();
}
Army getArmy(final KingdomFactory factory) {
return factory.createArmy();
}
public King getKing() {
return king;
}
private void setKing(final King king) {
this.king = king;
}
public Castle getCastle() {
return castle;
}
private void setCastle(final Castle castle) {
this.castle = castle;
}
public Army getArmy() {
return army;
}
private void setArmy(final Army army) {
this.army = army;
}
}

View File

@ -7,5 +7,5 @@ package com.iluwatar.abstractfactory;
*/
public interface Army {
String getDescription();
String getDescription();
}

View File

@ -7,5 +7,5 @@ package com.iluwatar.abstractfactory;
*/
public interface Castle {
String getDescription();
String getDescription();
}

View File

@ -7,10 +7,10 @@ package com.iluwatar.abstractfactory;
*/
public class ElfArmy implements Army {
static final String DESCRIPTION = "This is the Elven Army!";
static final String DESCRIPTION = "This is the Elven Army!";
@Override
public String getDescription() {
return DESCRIPTION;
}
@Override
public String getDescription() {
return DESCRIPTION;
}
}

View File

@ -7,10 +7,10 @@ package com.iluwatar.abstractfactory;
*/
public class ElfCastle implements Castle {
static final String DESCRIPTION = "This is the Elven castle!";
static final String DESCRIPTION = "This is the Elven castle!";
@Override
public String getDescription() {
return DESCRIPTION;
}
@Override
public String getDescription() {
return DESCRIPTION;
}
}

View File

@ -7,10 +7,10 @@ package com.iluwatar.abstractfactory;
*/
public class ElfKing implements King {
static final String DESCRIPTION = "This is the Elven king!";
@Override
public String getDescription() {
return DESCRIPTION;
}
static final String DESCRIPTION = "This is the Elven king!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}

View File

@ -7,16 +7,16 @@ package com.iluwatar.abstractfactory;
*/
public class ElfKingdomFactory implements KingdomFactory {
public Castle createCastle() {
return new ElfCastle();
}
public Castle createCastle() {
return new ElfCastle();
}
public King createKing() {
return new ElfKing();
}
public King createKing() {
return new ElfKing();
}
public Army createArmy() {
return new ElfArmy();
}
public Army createArmy() {
return new ElfArmy();
}
}

View File

@ -7,5 +7,5 @@ package com.iluwatar.abstractfactory;
*/
public interface King {
String getDescription();
String getDescription();
}

View File

@ -7,10 +7,10 @@ package com.iluwatar.abstractfactory;
*/
public interface KingdomFactory {
Castle createCastle();
Castle createCastle();
King createKing();
King createKing();
Army createArmy();
Army createArmy();
}

View File

@ -7,10 +7,10 @@ package com.iluwatar.abstractfactory;
*/
public class OrcArmy implements Army {
static final String DESCRIPTION = "This is the Orc Army!";
static final String DESCRIPTION = "This is the Orc Army!";
@Override
public String getDescription() {
return DESCRIPTION;
}
@Override
public String getDescription() {
return DESCRIPTION;
}
}

View File

@ -7,10 +7,10 @@ package com.iluwatar.abstractfactory;
*/
public class OrcCastle implements Castle {
static final String DESCRIPTION = "This is the Orc castle!";
static final String DESCRIPTION = "This is the Orc castle!";
@Override
public String getDescription() {
return DESCRIPTION;
}
@Override
public String getDescription() {
return DESCRIPTION;
}
}

View File

@ -7,10 +7,10 @@ package com.iluwatar.abstractfactory;
*/
public class OrcKing implements King {
static final String DESCRIPTION = "This is the Orc king!";
@Override
public String getDescription() {
return DESCRIPTION;
}
static final String DESCRIPTION = "This is the Orc king!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}

View File

@ -7,16 +7,15 @@ package com.iluwatar.abstractfactory;
*/
public class OrcKingdomFactory implements KingdomFactory {
public Castle createCastle() {
return new OrcCastle();
}
public Castle createCastle() {
return new OrcCastle();
}
public King createKing() {
return new OrcKing();
}
public Army createArmy() {
return new OrcArmy();
}
public King createKing() {
return new OrcKing();
}
public Army createArmy() {
return new OrcArmy();
}
}

View File

@ -1,4 +1,5 @@
package com.iluwatar.abstractfactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@ -7,71 +8,71 @@ import org.junit.Test;
public class AppTest {
private App app = new App();
private KingdomFactory elfFactory;
private KingdomFactory orcFactory;
@Before
public void setUp() {
elfFactory = app.getElfKingdomFactory();
orcFactory = app.getOrcKingdomFactory();
}
@Test
public void king() {
final King elfKing = app.getKing(elfFactory);
assertTrue(elfKing instanceof ElfKing);
assertEquals(ElfKing.DESCRIPTION, elfKing.getDescription());
final King orcKing = app.getKing(orcFactory);
assertTrue(orcKing instanceof OrcKing);
assertEquals(OrcKing.DESCRIPTION, orcKing.getDescription());
}
@Test
public void castle() {
final Castle elfCastle = app.getCastle(elfFactory);
assertTrue(elfCastle instanceof ElfCastle);
assertEquals(ElfCastle.DESCRIPTION, elfCastle.getDescription());
final Castle orcCastle = app.getCastle(orcFactory);
assertTrue(orcCastle instanceof OrcCastle);
assertEquals(OrcCastle.DESCRIPTION, orcCastle.getDescription());
}
@Test
public void army() {
final Army elfArmy = app.getArmy(elfFactory);
assertTrue(elfArmy instanceof ElfArmy);
assertEquals(ElfArmy.DESCRIPTION, elfArmy.getDescription());
final Army orcArmy = app.getArmy(orcFactory);
assertTrue(orcArmy instanceof OrcArmy);
assertEquals(OrcArmy.DESCRIPTION, orcArmy.getDescription());
}
@Test
public void createElfKingdom() {
app.createKingdom(elfFactory);
final King king = app.getKing();
final Castle castle = app.getCastle();
final Army army = app.getArmy();
assertTrue(king instanceof ElfKing);
assertEquals(ElfKing.DESCRIPTION, king.getDescription());
assertTrue(castle instanceof ElfCastle);
assertEquals(ElfCastle.DESCRIPTION, castle.getDescription());
assertTrue(army instanceof ElfArmy);
assertEquals(ElfArmy.DESCRIPTION, army.getDescription());
}
@Test
public void createOrcKingdom() {
app.createKingdom(orcFactory);
final King king = app.getKing();
final Castle castle = app.getCastle();
final Army army = app.getArmy();
assertTrue(king instanceof OrcKing);
assertEquals(OrcKing.DESCRIPTION, king.getDescription());
assertTrue(castle instanceof OrcCastle);
assertEquals(OrcCastle.DESCRIPTION, castle.getDescription());
assertTrue(army instanceof OrcArmy);
assertEquals(OrcArmy.DESCRIPTION, army.getDescription());
}
private App app = new App();
private KingdomFactory elfFactory;
private KingdomFactory orcFactory;
@Before
public void setUp() {
elfFactory = app.getElfKingdomFactory();
orcFactory = app.getOrcKingdomFactory();
}
@Test
public void king() {
final King elfKing = app.getKing(elfFactory);
assertTrue(elfKing instanceof ElfKing);
assertEquals(ElfKing.DESCRIPTION, elfKing.getDescription());
final King orcKing = app.getKing(orcFactory);
assertTrue(orcKing instanceof OrcKing);
assertEquals(OrcKing.DESCRIPTION, orcKing.getDescription());
}
@Test
public void castle() {
final Castle elfCastle = app.getCastle(elfFactory);
assertTrue(elfCastle instanceof ElfCastle);
assertEquals(ElfCastle.DESCRIPTION, elfCastle.getDescription());
final Castle orcCastle = app.getCastle(orcFactory);
assertTrue(orcCastle instanceof OrcCastle);
assertEquals(OrcCastle.DESCRIPTION, orcCastle.getDescription());
}
@Test
public void army() {
final Army elfArmy = app.getArmy(elfFactory);
assertTrue(elfArmy instanceof ElfArmy);
assertEquals(ElfArmy.DESCRIPTION, elfArmy.getDescription());
final Army orcArmy = app.getArmy(orcFactory);
assertTrue(orcArmy instanceof OrcArmy);
assertEquals(OrcArmy.DESCRIPTION, orcArmy.getDescription());
}
@Test
public void createElfKingdom() {
app.createKingdom(elfFactory);
final King king = app.getKing();
final Castle castle = app.getCastle();
final Army army = app.getArmy();
assertTrue(king instanceof ElfKing);
assertEquals(ElfKing.DESCRIPTION, king.getDescription());
assertTrue(castle instanceof ElfCastle);
assertEquals(ElfCastle.DESCRIPTION, castle.getDescription());
assertTrue(army instanceof ElfArmy);
assertEquals(ElfArmy.DESCRIPTION, army.getDescription());
}
@Test
public void createOrcKingdom() {
app.createKingdom(orcFactory);
final King king = app.getKing();
final Castle castle = app.getCastle();
final Army army = app.getArmy();
assertTrue(king instanceof OrcKing);
assertEquals(OrcKing.DESCRIPTION, king.getDescription());
assertTrue(castle instanceof OrcCastle);
assertEquals(OrcCastle.DESCRIPTION, castle.getDescription());
assertTrue(army instanceof OrcArmy);
assertEquals(OrcArmy.DESCRIPTION, army.getDescription());
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,61 +1,61 @@
<?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.adapter.GnomeEngineeringManager" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java" binary="false" corner="BOTTOM_RIGHT">
<position height="106" width="224" x="110" y="210"/>
<class-diagram version="1.1.9" 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.adapter.FishingBoat" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="656" y="355"/>
<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="2" language="java" name="com.iluwatar.adapter.Engineer" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/Engineer.java" binary="false" corner="BOTTOM_RIGHT">
<position height="88" width="141" x="110" y="356"/>
<class id="2" language="java" name="com.iluwatar.adapter.Captain" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/Captain.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="228" y="185"/>
<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.adapter.BattleFishingBoat" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="463" y="357"/>
<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.adapter.BattleShip" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/BattleShip.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="466" y="170"/>
<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="3" language="java" name="com.iluwatar.adapter.GnomeEngineer" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java" binary="false" corner="BOTTOM_RIGHT">
<position height="106" width="141" x="374" y="210"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="4" language="java" name="com.iluwatar.adapter.GoblinGlider" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java" binary="false" corner="BOTTOM_RIGHT">
<position height="142" width="130" x="374" y="356"/>
<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">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="2"/>
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="4"/>
</realization>
<realization id="6">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="2"/>
</realization>
<association id="7">
<end type="SOURCE" refId="1" navigable="false">
<attribute id="8" name="engineer"/>
<multiplicity id="9" minimum="0" maximum="1"/>
<association id="6">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="7" name="boat"/>
<multiplicity id="8" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="2" navigable="true"/>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="9">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="4"/>
</realization>
<association id="10">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="11" name="glider"/>
<end type="SOURCE" refId="2" navigable="false">
<attribute id="11" name="battleship"/>
<multiplicity id="12" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="4" navigable="true"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@ -7,13 +7,16 @@ categories: Structural
tags:
- Java
- Gang Of Four
- Difficulty-Beginner
---
**Also known as:** Wrapper
**Intent:** Convert the interface of a class into another interface the clients
expect. Adapter lets classes work together that couldn't otherwise because of
incompatible interfaces.
![alt text](./etc/adapter_1.png "Adapter")
![alt text](./etc/adapter.png "Adapter")
**Applicability:** Use the Adapter pattern when

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.7.0</version>
<version>1.9.0</version>
</parent>
<artifactId>adapter</artifactId>
<dependencies>
@ -14,5 +14,10 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,31 +1,38 @@
package com.iluwatar.adapter;
/**
* An adapter helps two incompatible interfaces to work together. This is the real world definition
* for an adapter. Interfaces may be incompatible but the inner functionality should suit the need.
* The Adapter design pattern allows otherwise incompatible classes to work together by converting
* the interface of one class into an interface expected by the clients.
*
* An adapter helps two incompatible interfaces to work together. This is the real
* world definition for an adapter. Interfaces may be incompatible but the inner
* functionality should suit the need. The Adapter design pattern allows otherwise
* incompatible classes to work together by converting the interface of one class
* into an interface expected by the clients.
* <p>
* There are two variations of the Adapter pattern: The class adapter implements
* the adaptee's interface whereas the object adapter uses composition to
* contain the adaptee in the adapter object. This example uses the object
* adapter approach.
* There are two variations of the Adapter pattern: The class adapter implements the adaptee's
* interface whereas the object adapter uses composition to contain the adaptee in the adapter
* object. This example uses the object adapter approach.
*
* <p>
* The Adapter ({@link GnomeEngineer}) converts the interface of the target class
* ({@link GoblinGlider}) into a suitable one expected by the client
* ({@link GnomeEngineeringManager}).
*
* The Adapter ({@link BattleFishingBoat}) converts the interface of the adaptee class (
* {@link FishingBoat}) into a suitable one expected by the client ( {@link BattleShip} ).
*
* <p>
* The story of this implementation is this. <br>
* Pirates are coming! we need a {@link BattleShip} to fight! We have a {@link FishingBoat} and our
* captain. We have no time to make up a new ship! we need to reuse this {@link FishingBoat}. The
* captain needs a battleship which can fire and move. The spec is in {@link BattleShip}. We will
* use the Adapter pattern to reuse {@link FishingBoat}.
*
*/
public class App {
/**
* Program entry point
* @param args command line args
*/
public static void main(String[] args) {
Engineer manager = new GnomeEngineeringManager();
manager.operateDevice();
}
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
Captain captain = new Captain(new BattleFishingBoat());
captain.move();
captain.fire();
}
}

View File

@ -0,0 +1,29 @@
package com.iluwatar.adapter;
/**
*
* Adapter class. Adapts the interface of the device ({@link FishingBoat}) into {@link BattleShip}
* interface expected by the client ({@link Captain}). <br>
* In this case we added a new function fire to suit the interface. We are reusing the
* {@link FishingBoat} without changing itself. The Adapter class can just map the functions of the
* Adaptee or add, delete features of the Adaptee.
*
*/
public class BattleFishingBoat implements BattleShip {
private FishingBoat boat;
public BattleFishingBoat() {
boat = new FishingBoat();
}
@Override
public void fire() {
System.out.println("fire!");
}
@Override
public void move() {
boat.sail();
}
}

View File

@ -0,0 +1,14 @@
package com.iluwatar.adapter;
/**
* The interface expected by the client.<br>
* A Battleship can fire and move.
*
*/
public interface BattleShip {
void fire();
void move();
}

View File

@ -0,0 +1,33 @@
package com.iluwatar.adapter;
/**
* The Captain uses {@link BattleShip} to fight. <br>
* This is the client in the pattern.
*/
public class Captain implements BattleShip {
private BattleShip battleship;
public Captain() {
}
public Captain(BattleShip battleship) {
this.battleship = battleship;
}
public void setBattleship(BattleShip battleship) {
this.battleship = battleship;
}
@Override
public void fire() {
battleship.fire();
}
@Override
public void move() {
battleship.move();
}
}

View File

@ -1,12 +0,0 @@
package com.iluwatar.adapter;
/**
*
* Engineers can operate devices.
*
*/
public interface Engineer {
void operateDevice();
}

View File

@ -0,0 +1,18 @@
package com.iluwatar.adapter;
/**
*
* Device class (adaptee in the pattern). We want to reuse this class
*
*/
public class FishingBoat {
public void sail() {
System.out.println("The Boat is moving to that place");
}
public void fish() {
System.out.println("fishing ...");
}
}

View File

@ -1,24 +0,0 @@
package com.iluwatar.adapter;
/**
*
* Adapter class. Adapts the interface of the device ({@link GoblinGlider}) into
* {@link Engineer} interface expected by the client ({@link GnomeEngineeringManager}).
*
*/
public class GnomeEngineer implements Engineer {
private GoblinGlider glider;
public GnomeEngineer() {
glider = new GoblinGlider();
}
@Override
public void operateDevice() {
glider.attachGlider();
glider.gainSpeed();
glider.takeOff();
}
}

View File

@ -1,20 +0,0 @@
package com.iluwatar.adapter;
/**
*
* GnomeEngineering manager uses {@link Engineer} to operate devices.
*
*/
public class GnomeEngineeringManager implements Engineer {
private Engineer engineer;
public GnomeEngineeringManager() {
engineer = new GnomeEngineer();
}
@Override
public void operateDevice() {
engineer.operateDevice();
}
}

View File

@ -1,21 +0,0 @@
package com.iluwatar.adapter;
/**
*
* Device class (adaptee in the pattern).
*
*/
public class GoblinGlider {
public void attachGlider() {
System.out.println("Glider attached.");
}
public void gainSpeed() {
System.out.println("Gaining speed.");
}
public void takeOff() {
System.out.println("Lift-off!");
}
}

View File

@ -0,0 +1,61 @@
package com.iluwatar.adapter;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
* Test class
*
*/
public class AdapterPatternTest {
private Map<String, Object> beans;
private static final String BATTLESHIP_BEAN = "engineer";
private static final String CAPTAIN_BEAN = "captain";
/**
* This method runs before the test execution and sets the bean objects in the beans Map.
*/
@Before
public void setup() {
beans = new HashMap<>();
BattleFishingBoat battleFishingBoat = spy(new BattleFishingBoat());
beans.put(BATTLESHIP_BEAN, battleFishingBoat);
Captain captain = new Captain();
captain.setBattleship((BattleFishingBoat) beans.get(BATTLESHIP_BEAN));
beans.put(CAPTAIN_BEAN, captain);
}
/**
* This test asserts that when we use the move() method on a captain bean(client), it is
* internally calling move method on the battleship object. The Adapter ({@link BattleFishingBoat}
* ) converts the interface of the target class ( {@link FishingBoat}) into a suitable one
* expected by the client ({@link Captain} ).
*/
@Test
public void testAdapter() {
BattleShip captain = (BattleShip) beans.get(CAPTAIN_BEAN);
// when captain moves
captain.move();
// the captain internally calls the battleship object to move
BattleShip battleship = (BattleShip) beans.get(BATTLESHIP_BEAN);
verify(battleship).move();
// same with above with firing
captain.fire();
verify(battleship).fire();
}
}

View File

@ -1,19 +0,0 @@
package com.iluwatar.adapter;
import org.junit.Test;
import com.iluwatar.adapter.App;
/**
*
* Application test
*
*/
public class AppTest {
@Test
public void test() {
String[] args = {};
App.main(args);
}
}

View File

@ -4,7 +4,10 @@ title: Async Method Invocation
folder: async-method-invocation
permalink: /patterns/async-method-invocation/
categories: Concurrency
tags: Java
tags:
- Java
- Difficulty-Intermediate
- Functional
---
**Intent:** Asynchronous method invocation is pattern where the calling thread

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.7.0</version>
<version>1.9.0</version>
</parent>
<artifactId>async-method-invocation</artifactId>
<dependencies>
@ -14,5 +14,10 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -5,22 +5,22 @@ import java.util.concurrent.Callable;
/**
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated value,
* <code>AsyncCallback</code> which can be provided to be executed on task completion and
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
* <code>AsyncCallback</code> which can be provided to be executed on task completion and <code>AsyncExecutor</code>
* that manages the execution of the async tasks.
* <p>
* The main method shows example flow of async invocations. The main thread starts multiple tasks with
* variable durations and then continues its own work. When the main thread has done it's job it collects
* the results of the async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are
* executed immediately when the tasks complete.
* The main method shows example flow of async invocations. The main thread starts multiple tasks with variable
* durations and then continues its own work. When the main thread has done it's job it collects the results of the
* async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are executed immediately when the
* tasks complete.
* <p>
* Noteworthy difference of thread usage between the async results and callbacks is that the async results
* are collected in the main thread but the callbacks are executed within the worker threads. This should be
* noted when working with thread pools.
* Noteworthy difference of thread usage between the async results and callbacks is that the async results are collected
* in the main thread but the callbacks are executed within the worker threads. This should be noted when working with
* thread pools.
* <p>
* Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture
* and ExecutorService are the real world implementations of this pattern. But due to the nature of parallel
* programming, the implementations are not trivial. This example does not take all possible scenarios into
* account but rather provides a simple version that helps to understand the pattern.
* Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture and
* ExecutorService are the real world implementations of this pattern. But due to the nature of parallel programming,
* the implementations are not trivial. This example does not take all possible scenarios into account but rather
* provides a simple version that helps to understand the pattern.
*
* @see AsyncResult
* @see AsyncCallback
@ -32,66 +32,72 @@ import java.util.concurrent.Callable;
*/
public class App {
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
AsyncExecutor executor = new ThreadAsyncExecutor();
/**
* Program entry point
*/
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
AsyncExecutor executor = new ThreadAsyncExecutor();
// start few async tasks with varying processing times, two last with callback handlers
AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
AsyncResult<Integer> asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
AsyncResult<String> asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
// start few async tasks with varying processing times, two last with callback handlers
AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
AsyncResult<Integer> asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
AsyncResult<String> asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
// emulate processing in the current thread while async tasks are running in their own threads
Thread.sleep(350); // Oh boy I'm working hard here
log("Some hard work done");
// emulate processing in the current thread while async tasks are running in their own threads
Thread.sleep(350); // Oh boy I'm working hard here
log("Some hard work done");
// wait for completion of the tasks
Integer result1 = executor.endProcess(asyncResult1);
String result2 = executor.endProcess(asyncResult2);
Long result3 = executor.endProcess(asyncResult3);
asyncResult4.await();
asyncResult5.await();
// wait for completion of the tasks
Integer result1 = executor.endProcess(asyncResult1);
String result2 = executor.endProcess(asyncResult2);
Long result3 = executor.endProcess(asyncResult3);
asyncResult4.await();
asyncResult5.await();
// log the results of the tasks, callbacks log immediately when complete
log("Result 1: " + result1);
log("Result 2: " + result2);
log("Result 3: " + result3);
}
// log the results of the tasks, callbacks log immediately when complete
log("Result 1: " + result1);
log("Result 2: " + result2);
log("Result 3: " + result3);
}
/**
* Creates a callable that lazily evaluates to given value with artificial delay.
*
* @param value value to evaluate
* @param delayMillis artificial delay in milliseconds
* @return new callable for lazy evaluation
*/
private static <T> Callable<T> lazyval(T value, long delayMillis) {
return () -> {
Thread.sleep(delayMillis);
log("Task completed with: " + value);
return value;
};
}
/**
* Creates a callable that lazily evaluates to given value with artificial delay.
*
* @param value
* value to evaluate
* @param delayMillis
* artificial delay in milliseconds
* @return new callable for lazy evaluation
*/
private static <T> Callable<T> lazyval(T value, long delayMillis) {
return () -> {
Thread.sleep(delayMillis);
log("Task completed with: " + value);
return value;
};
}
/**
* Creates a simple callback that logs the complete status of the async result.
*
* @param name callback name
* @return new async callback
*/
private static <T> AsyncCallback<T> callback(String name) {
return (value, ex) -> {
if (ex.isPresent()) {
log(name + " failed: " + ex.map(Exception::getMessage).orElse(""));
} else {
log(name + ": " + value);
}
};
}
/**
* Creates a simple callback that logs the complete status of the async result.
*
* @param name
* callback name
* @return new async callback
*/
private static <T> AsyncCallback<T> callback(String name) {
return (value, ex) -> {
if (ex.isPresent()) {
log(name + " failed: " + ex.map(Exception::getMessage).orElse(""));
} else {
log(name + ": " + value);
}
};
}
private static void log(String msg) {
System.out.println(String.format("[%1$-10s] - %2$s", Thread.currentThread().getName(), msg));
}
private static void log(String msg) {
System.out.println(String.format("[%1$-10s] - %2$s", Thread.currentThread().getName(), msg));
}
}

View File

@ -11,12 +11,11 @@ import java.util.Optional;
*/
public interface AsyncCallback<T> {
/**
* Complete handler which is executed when async task is completed or fails execution.
*
* @param value the evaluated value from async task, undefined when execution fails
* @param ex empty value if execution succeeds, some exception if executions fails
*/
void onComplete(T value, Optional<Exception> ex);
/**
* Complete handler which is executed when async task is completed or fails execution.
*
* @param value the evaluated value from async task, undefined when execution fails
* @param ex empty value if execution succeeds, some exception if executions fails
*/
void onComplete(T value, Optional<Exception> ex);
}

View File

@ -10,33 +10,32 @@ import java.util.concurrent.ExecutionException;
*/
public interface AsyncExecutor {
/**
* Starts processing of an async task. Returns immediately with async result.
*
* @param task task to be executed asynchronously
* @return async result for the task
*/
<T> AsyncResult<T> startProcess(Callable<T> task);
/**
* Starts processing of an async task. Returns immediately with async result.
*
* @param task task to be executed asynchronously
* @return async result for the task
*/
<T> AsyncResult<T> startProcess(Callable<T> task);
/**
* Starts processing of an async task. Returns immediately with async result. Executes callback
* when the task is completed.
*
* @param task task to be executed asynchronously
* @param callback callback to be executed on task completion
* @return async result for the task
*/
<T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback);
/**
* Ends processing of an async task. Blocks the current thread if necessary and returns the
* evaluated value of the completed task.
*
* @param asyncResult async result of a task
* @return evaluated value of the completed task
* @throws ExecutionException if execution has failed, containing the root cause
* @throws InterruptedException if the execution is interrupted
*/
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
/**
* Starts processing of an async task. Returns immediately with async result. Executes callback
* when the task is completed.
*
* @param task task to be executed asynchronously
* @param callback callback to be executed on task completion
* @return async result for the task
*/
<T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback);
/**
* Ends processing of an async task. Blocks the current thread if necessary and returns the
* evaluated value of the completed task.
*
* @param asyncResult async result of a task
* @return evaluated value of the completed task
* @throws ExecutionException if execution has failed, containing the root cause
* @throws InterruptedException if the execution is interrupted
*/
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
}

View File

@ -5,31 +5,29 @@ import java.util.concurrent.ExecutionException;
/**
*
* AsyncResult interface
*
* @param <T>
*/
public interface AsyncResult<T> {
/**
* Status of the async task execution.
*
* @return <code>true</code> if execution is completed or failed
*/
boolean isCompleted();
/**
* Status of the async task execution.
*
* @return <code>true</code> if execution is completed or failed
*/
boolean isCompleted();
/**
* Gets the value of completed async task.
*
* @return evaluated value or throws ExecutionException if execution has failed
* @throws ExecutionException if execution has failed, containing the root cause
* @throws IllegalStateException if execution is not completed
*/
T getValue() throws ExecutionException;
/**
* Gets the value of completed async task.
*
* @return evaluated value or throws ExecutionException if execution has failed
* @throws ExecutionException if execution has failed, containing the root cause
* @throws IllegalStateException if execution is not completed
*/
T getValue() throws ExecutionException;
/**
* Blocks the current thread until the async task is completed.
*
* @throws InterruptedException if the execution is interrupted
*/
void await() throws InterruptedException;
/**
* Blocks the current thread until the async task is completed.
*
* @throws InterruptedException if the execution is interrupted
*/
void await() throws InterruptedException;
}

View File

@ -12,116 +12,117 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public class ThreadAsyncExecutor implements AsyncExecutor {
/** Index for thread naming */
private final AtomicInteger idx = new AtomicInteger(0);
/** Index for thread naming */
private final AtomicInteger idx = new AtomicInteger(0);
@Override
public <T> AsyncResult<T> startProcess(Callable<T> task) {
return startProcess(task, null);
}
@Override
public <T> AsyncResult<T> startProcess(Callable<T> task) {
return startProcess(task, null);
}
@Override
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
CompletableResult<T> result = new CompletableResult<>(callback);
new Thread(() -> {
try {
result.setValue(task.call());
} catch (Exception ex) {
result.setException(ex);
}
}, "executor-" + idx.incrementAndGet()).start();
return result;
}
@Override
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
CompletableResult<T> result = new CompletableResult<>(callback);
new Thread(() -> {
try {
result.setValue(task.call());
} catch (Exception ex) {
result.setException(ex);
}
} , "executor-" + idx.incrementAndGet()).start();
return result;
}
@Override
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException {
if (asyncResult.isCompleted()) {
return asyncResult.getValue();
} else {
asyncResult.await();
return asyncResult.getValue();
}
}
@Override
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException {
if (asyncResult.isCompleted()) {
return asyncResult.getValue();
} else {
asyncResult.await();
return asyncResult.getValue();
}
}
/**
* Simple implementation of async result that allows completing it successfully with a value
* or exceptionally with an exception. A really simplified version from its real life cousins
* FutureTask and CompletableFuture.
*
* @see java.util.concurrent.FutureTask
* @see java.util.concurrent.CompletableFuture
*/
private static class CompletableResult<T> implements AsyncResult<T> {
/**
* Simple implementation of async result that allows completing it successfully with a value or exceptionally with an
* exception. A really simplified version from its real life cousins FutureTask and CompletableFuture.
*
* @see java.util.concurrent.FutureTask
* @see java.util.concurrent.CompletableFuture
*/
private static class CompletableResult<T> implements AsyncResult<T> {
static final int RUNNING = 1;
static final int FAILED = 2;
static final int COMPLETED = 3;
static final int RUNNING = 1;
static final int FAILED = 2;
static final int COMPLETED = 3;
final Object lock;
final Optional<AsyncCallback<T>> callback;
final Object lock;
final Optional<AsyncCallback<T>> callback;
volatile int state = RUNNING;
T value;
Exception exception;
volatile int state = RUNNING;
T value;
Exception exception;
CompletableResult(AsyncCallback<T> callback) {
this.lock = new Object();
this.callback = Optional.ofNullable(callback);
}
CompletableResult(AsyncCallback<T> callback) {
this.lock = new Object();
this.callback = Optional.ofNullable(callback);
}
/**
* Sets the value from successful execution and executes callback if available. Notifies
* any thread waiting for completion.
*
* @param value value of the evaluated task
*/
void setValue(T value) {
this.value = value;
this.state = COMPLETED;
this.callback.ifPresent(ac -> ac.onComplete(value, Optional.<Exception>empty()));
synchronized (lock) {
lock.notifyAll();
}
}
/**
* Sets the value from successful execution and executes callback if available. Notifies any thread waiting for
* completion.
*
* @param value
* value of the evaluated task
*/
void setValue(T value) {
this.value = value;
this.state = COMPLETED;
this.callback.ifPresent(ac -> ac.onComplete(value, Optional.<Exception>empty()));
synchronized (lock) {
lock.notifyAll();
}
}
/**
* Sets the exception from failed execution and executes callback if available. Notifies
* any thread waiting for completion.
*
* @param exception exception of the failed task
*/
void setException(Exception exception) {
this.exception = exception;
this.state = FAILED;
this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception)));
synchronized (lock) {
lock.notifyAll();
}
}
/**
* Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for
* completion.
*
* @param exception
* exception of the failed task
*/
void setException(Exception exception) {
this.exception = exception;
this.state = FAILED;
this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception)));
synchronized (lock) {
lock.notifyAll();
}
}
@Override
public boolean isCompleted() {
return (state > RUNNING);
}
@Override
public boolean isCompleted() {
return state > RUNNING;
}
@Override
public T getValue() throws ExecutionException {
if (state == COMPLETED) {
return value;
} else if (state == FAILED) {
throw new ExecutionException(exception);
} else {
throw new IllegalStateException("Execution not completed yet");
}
}
@Override
public T getValue() throws ExecutionException {
if (state == COMPLETED) {
return value;
} else if (state == FAILED) {
throw new ExecutionException(exception);
} else {
throw new IllegalStateException("Execution not completed yet");
}
}
@Override
public void await() throws InterruptedException {
synchronized (lock) {
if (!isCompleted()) {
lock.wait();
}
}
}
}
@Override
public void await() throws InterruptedException {
synchronized (lock) {
if (!isCompleted()) {
lock.wait();
}
}
}
}
}

View File

@ -9,10 +9,9 @@ import org.junit.Test;
*/
public class AppTest {
@Test
public void test() throws Exception {
String[] args = {};
App.main(args);
}
@Test
public void test() throws Exception {
String[] args = {};
App.main(args);
}
}

View File

@ -0,0 +1,290 @@
package com.iluwatar.async.method.invocation;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Matchers;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
* Date: 12/6/15 - 10:49 AM
*
* @author Jeroen Meulemeester
*/
public class ThreadAsyncExecutorTest {
/**
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)}
*/
@Test(timeout = 3000)
public void testSuccessfulTaskWithoutCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final Object result = new Object();
final Callable<Object> task = mock(Callable.class);
when(task.call()).thenReturn(result);
final AsyncResult<Object> asyncResult = executor.startProcess(task);
assertNotNull(asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
// Our task should only execute once ...
verify(task, times(1)).call();
// ... and the result should be exactly the same object
assertSame(result, asyncResult.getValue());
}
/**
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)}
*/
@Test(timeout = 3000)
public void testSuccessfulTaskWithCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final Object result = new Object();
final Callable<Object> task = mock(Callable.class);
when(task.call()).thenReturn(result);
final AsyncCallback callback = mock(AsyncCallback.class);
final AsyncResult<Object> asyncResult = executor.startProcess(task, callback);
assertNotNull(asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
// Our task should only execute once ...
verify(task, times(1)).call();
// ... same for the callback, we expect our object
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
verify(callback, times(1)).onComplete(eq(result), optionalCaptor.capture());
final Optional<Exception> optionalException = optionalCaptor.getValue();
assertNotNull(optionalException);
assertFalse(optionalException.isPresent());
// ... and the result should be exactly the same object
assertSame(result, asyncResult.getValue());
}
/**
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while
* to execute
*/
@Test(timeout = 5000)
public void testLongRunningTaskWithoutCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final Object result = new Object();
final Callable<Object> task = mock(Callable.class);
when(task.call()).thenAnswer(i -> {
Thread.sleep(1500);
return result;
});
final AsyncResult<Object> asyncResult = executor.startProcess(task);
assertNotNull(asyncResult);
assertFalse(asyncResult.isCompleted());
try {
asyncResult.getValue();
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
} catch (IllegalStateException e) {
assertNotNull(e.getMessage());
}
// Our task should only execute once, but it can take a while ...
verify(task, timeout(3000).times(1)).call();
// Prevent timing issues, and wait until the result is available
asyncResult.await();
assertTrue(asyncResult.isCompleted());
verifyNoMoreInteractions(task);
// ... and the result should be exactly the same object
assertSame(result, asyncResult.getValue());
}
/**
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when a task
* takes a while to execute
*/
@Test(timeout = 5000)
public void testLongRunningTaskWithCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final Object result = new Object();
final Callable<Object> task = mock(Callable.class);
when(task.call()).thenAnswer(i -> {
Thread.sleep(1500);
return result;
});
final AsyncCallback<Object> callback = mock(AsyncCallback.class);
final AsyncResult<Object> asyncResult = executor.startProcess(task, callback);
assertNotNull(asyncResult);
assertFalse(asyncResult.isCompleted());
verifyZeroInteractions(callback);
try {
asyncResult.getValue();
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
} catch (IllegalStateException e) {
assertNotNull(e.getMessage());
}
// Our task should only execute once, but it can take a while ...
verify(task, timeout(3000).times(1)).call();
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
verify(callback, timeout(3000).times(1)).onComplete(eq(result), optionalCaptor.capture());
final Optional<Exception> optionalException = optionalCaptor.getValue();
assertNotNull(optionalException);
assertFalse(optionalException.isPresent());
// Prevent timing issues, and wait until the result is available
asyncResult.await();
assertTrue(asyncResult.isCompleted());
verifyNoMoreInteractions(task, callback);
// ... and the result should be exactly the same object
assertSame(result, asyncResult.getValue());
}
/**
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while
* to execute, while waiting on the result using {@link ThreadAsyncExecutor#endProcess(AsyncResult)}
*/
@Test(timeout = 5000)
public void testEndProcess() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final Object result = new Object();
final Callable<Object> task = mock(Callable.class);
when(task.call()).thenAnswer(i -> {
Thread.sleep(1500);
return result;
});
final AsyncResult<Object> asyncResult = executor.startProcess(task);
assertNotNull(asyncResult);
assertFalse(asyncResult.isCompleted());
try {
asyncResult.getValue();
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
} catch (IllegalStateException e) {
assertNotNull(e.getMessage());
}
assertSame(result, executor.endProcess(asyncResult));
verify(task, times(1)).call();
assertTrue(asyncResult.isCompleted());
// Calling end process a second time while already finished should give the same result
assertSame(result, executor.endProcess(asyncResult));
verifyNoMoreInteractions(task);
}
/**
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when the callable is 'null'
*/
@Test(timeout = 3000)
public void testNullTask() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final AsyncResult<Object> asyncResult = executor.startProcess(null);
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
try {
asyncResult.getValue();
fail("Expected ExecutionException with NPE as cause");
} catch (final ExecutionException e) {
assertNotNull(e.getMessage());
assertNotNull(e.getCause());
assertEquals(NullPointerException.class, e.getCause().getClass());
}
}
/**
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when the
* callable is 'null', but the asynchronous callback is provided
*/
@Test(timeout = 3000)
public void testNullTaskWithCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final AsyncCallback<Object> callback = mock(AsyncCallback.class);
final AsyncResult<Object> asyncResult = executor.startProcess(null, callback);
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
verify(callback, times(1)).onComplete(Matchers.isNull(), optionalCaptor.capture());
final Optional<Exception> optionalException = optionalCaptor.getValue();
assertNotNull(optionalException);
assertTrue(optionalException.isPresent());
final Exception exception = optionalException.get();
assertNotNull(exception);
assertEquals(NullPointerException.class, exception.getClass());
try {
asyncResult.getValue();
fail("Expected ExecutionException with NPE as cause");
} catch (final ExecutionException e) {
assertNotNull(e.getMessage());
assertNotNull(e.getCause());
assertEquals(NullPointerException.class, e.getCause().getClass());
}
}
/**
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when both
* the callable and the asynchronous callback are 'null'
*/
@Test(timeout = 3000)
public void testNullTaskWithNullCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final AsyncResult<Object> asyncResult = executor.startProcess(null, null);
assertNotNull("The AsyncResult should not be 'null', even though the task and callback were 'null'.", asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
try {
asyncResult.getValue();
fail("Expected ExecutionException with NPE as cause");
} catch (final ExecutionException e) {
assertNotNull(e.getMessage());
assertNotNull(e.getCause());
assertEquals(NullPointerException.class, e.getCause().getClass());
}
}
}

View File

@ -7,8 +7,11 @@ categories: Structural
tags:
- Java
- Gang Of Four
- Difficulty-Intermediate
---
**Also known as:** Handle/Body
**Intent:** Decouple an abstraction from its implementation so that the two can
vary independently.

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.7.0</version>
<version>1.9.0</version>
</parent>
<artifactId>bridge</artifactId>
<dependencies>
@ -14,5 +14,10 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -2,41 +2,38 @@ package com.iluwatar.bridge;
/**
*
* The Bridge pattern can also be thought of as two layers of abstraction. With Bridge,
* you can decouple an abstraction from its implementation so that the two can vary independently.
* The Bridge pattern can also be thought of as two layers of abstraction. With Bridge, you can
* decouple an abstraction from its implementation so that the two can vary independently.
* <p>
* In Bridge pattern both abstraction ({@link MagicWeapon}) and implementation
* ({@link MagicWeaponImpl}) have their own class hierarchies. The interface of the
* implementations can be changed without affecting the clients.
* In Bridge pattern both abstraction ({@link MagicWeapon}) and implementation (
* {@link MagicWeaponImpl}) have their own class hierarchies. The interface of the implementations
* can be changed without affecting the clients.
*
*/
public class App {
/**
* Program entry point
* @param args command line args
*/
public static void main(String[] args) {
BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(
new Excalibur());
blindingMagicWeapon.wield();
blindingMagicWeapon.blind();
blindingMagicWeapon.swing();
blindingMagicWeapon.unwield();
/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {
BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(new Excalibur());
blindingMagicWeapon.wield();
blindingMagicWeapon.blind();
blindingMagicWeapon.swing();
blindingMagicWeapon.unwield();
FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(
new Mjollnir());
flyingMagicWeapon.wield();
flyingMagicWeapon.fly();
flyingMagicWeapon.swing();
flyingMagicWeapon.unwield();
FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(new Mjollnir());
flyingMagicWeapon.wield();
flyingMagicWeapon.fly();
flyingMagicWeapon.swing();
flyingMagicWeapon.unwield();
SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(
new Stormbringer());
soulEatingMagicWeapon.wield();
soulEatingMagicWeapon.swing();
soulEatingMagicWeapon.eatSoul();
soulEatingMagicWeapon.unwield();
}
SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(new Stormbringer());
soulEatingMagicWeapon.wield();
soulEatingMagicWeapon.swing();
soulEatingMagicWeapon.eatSoul();
soulEatingMagicWeapon.unwield();
}
}

View File

@ -7,32 +7,31 @@ package com.iluwatar.bridge;
*/
public class BlindingMagicWeapon extends MagicWeapon {
public BlindingMagicWeapon(BlindingMagicWeaponImpl imp) {
super(imp);
}
public BlindingMagicWeapon(BlindingMagicWeaponImpl imp) {
super(imp);
}
@Override
public BlindingMagicWeaponImpl getImp() {
return (BlindingMagicWeaponImpl) imp;
}
@Override
public BlindingMagicWeaponImpl getImp() {
return (BlindingMagicWeaponImpl) imp;
}
@Override
public void wield() {
getImp().wieldImp();
}
@Override
public void wield() {
getImp().wieldImp();
}
@Override
public void swing() {
getImp().swingImp();
}
@Override
public void swing() {
getImp().swingImp();
}
@Override
public void unwield() {
getImp().unwieldImp();
}
public void blind() {
getImp().blindImp();
}
@Override
public void unwield() {
getImp().unwieldImp();
}
public void blind() {
getImp().blindImp();
}
}

View File

@ -7,6 +7,6 @@ package com.iluwatar.bridge;
*/
public abstract class BlindingMagicWeaponImpl extends MagicWeaponImpl {
public abstract void blindImp();
public abstract void blindImp();
}

View File

@ -7,25 +7,23 @@ package com.iluwatar.bridge;
*/
public class Excalibur extends BlindingMagicWeaponImpl {
@Override
public void wieldImp() {
System.out.println("wielding Excalibur");
}
@Override
public void wieldImp() {
System.out.println("wielding Excalibur");
}
@Override
public void swingImp() {
System.out.println("swinging Excalibur");
}
@Override
public void swingImp() {
System.out.println("swinging Excalibur");
}
@Override
public void unwieldImp() {
System.out.println("unwielding Excalibur");
}
@Override
public void blindImp() {
System.out
.println("bright light streams from Excalibur blinding the enemy");
}
@Override
public void unwieldImp() {
System.out.println("unwielding Excalibur");
}
@Override
public void blindImp() {
System.out.println("bright light streams from Excalibur blinding the enemy");
}
}

View File

@ -7,31 +7,31 @@ package com.iluwatar.bridge;
*/
public class FlyingMagicWeapon extends MagicWeapon {
public FlyingMagicWeapon(FlyingMagicWeaponImpl imp) {
super(imp);
}
public FlyingMagicWeapon(FlyingMagicWeaponImpl imp) {
super(imp);
}
public FlyingMagicWeaponImpl getImp() {
return (FlyingMagicWeaponImpl) imp;
}
public FlyingMagicWeaponImpl getImp() {
return (FlyingMagicWeaponImpl) imp;
}
@Override
public void wield() {
getImp().wieldImp();
}
@Override
public void wield() {
getImp().wieldImp();
}
@Override
public void swing() {
getImp().swingImp();
}
@Override
public void swing() {
getImp().swingImp();
}
@Override
public void unwield() {
getImp().unwieldImp();
}
@Override
public void unwield() {
getImp().unwieldImp();
}
public void fly() {
getImp().flyImp();
}
public void fly() {
getImp().flyImp();
}
}

View File

@ -7,6 +7,6 @@ package com.iluwatar.bridge;
*/
public abstract class FlyingMagicWeaponImpl extends MagicWeaponImpl {
public abstract void flyImp();
public abstract void flyImp();
}

View File

@ -7,20 +7,19 @@ package com.iluwatar.bridge;
*/
public abstract class MagicWeapon {
protected MagicWeaponImpl imp;
protected MagicWeaponImpl imp;
public MagicWeapon(MagicWeaponImpl imp) {
this.imp = imp;
}
public MagicWeapon(MagicWeaponImpl imp) {
this.imp = imp;
}
public abstract void wield();
public abstract void wield();
public abstract void swing();
public abstract void swing();
public abstract void unwield();
public MagicWeaponImpl getImp() {
return imp;
}
public abstract void unwield();
public MagicWeaponImpl getImp() {
return imp;
}
}

View File

@ -7,10 +7,10 @@ package com.iluwatar.bridge;
*/
public abstract class MagicWeaponImpl {
public abstract void wieldImp();
public abstract void wieldImp();
public abstract void swingImp();
public abstract void swingImp();
public abstract void unwieldImp();
public abstract void unwieldImp();
}

View File

@ -7,25 +7,23 @@ package com.iluwatar.bridge;
*/
public class Mjollnir extends FlyingMagicWeaponImpl {
@Override
public void wieldImp() {
System.out.println("wielding Mjollnir");
}
@Override
public void wieldImp() {
System.out.println("wielding Mjollnir");
}
@Override
public void swingImp() {
System.out.println("swinging Mjollnir");
}
@Override
public void swingImp() {
System.out.println("swinging Mjollnir");
}
@Override
public void unwieldImp() {
System.out.println("unwielding Mjollnir");
}
@Override
public void flyImp() {
System.out
.println("Mjollnir hits the enemy in the air and returns back to the owner's hand");
}
@Override
public void unwieldImp() {
System.out.println("unwielding Mjollnir");
}
@Override
public void flyImp() {
System.out.println("Mjollnir hits the enemy in the air and returns back to the owner's hand");
}
}

View File

@ -7,32 +7,32 @@ package com.iluwatar.bridge;
*/
public class SoulEatingMagicWeapon extends MagicWeapon {
public SoulEatingMagicWeapon(SoulEatingMagicWeaponImpl imp) {
super(imp);
}
public SoulEatingMagicWeapon(SoulEatingMagicWeaponImpl imp) {
super(imp);
}
@Override
public SoulEatingMagicWeaponImpl getImp() {
return (SoulEatingMagicWeaponImpl) imp;
}
@Override
public SoulEatingMagicWeaponImpl getImp() {
return (SoulEatingMagicWeaponImpl) imp;
}
@Override
public void wield() {
getImp().wieldImp();
}
@Override
public void wield() {
getImp().wieldImp();
}
@Override
public void swing() {
getImp().swingImp();
}
@Override
public void swing() {
getImp().swingImp();
}
@Override
public void unwield() {
getImp().unwieldImp();
}
@Override
public void unwield() {
getImp().unwieldImp();
}
public void eatSoul() {
getImp().eatSoulImp();
}
public void eatSoul() {
getImp().eatSoulImp();
}
}

View File

@ -7,6 +7,6 @@ package com.iluwatar.bridge;
*/
public abstract class SoulEatingMagicWeaponImpl extends MagicWeaponImpl {
public abstract void eatSoulImp();
public abstract void eatSoulImp();
}

View File

@ -7,24 +7,23 @@ package com.iluwatar.bridge;
*/
public class Stormbringer extends SoulEatingMagicWeaponImpl {
@Override
public void wieldImp() {
System.out.println("wielding Stormbringer");
}
@Override
public void wieldImp() {
System.out.println("wielding Stormbringer");
}
@Override
public void swingImp() {
System.out.println("swinging Stormbringer");
}
@Override
public void swingImp() {
System.out.println("swinging Stormbringer");
}
@Override
public void unwieldImp() {
System.out.println("unwielding Stormbringer");
}
@Override
public void eatSoulImp() {
System.out.println("Stormbringer devours the enemy's soul");
}
@Override
public void unwieldImp() {
System.out.println("unwielding Stormbringer");
}
@Override
public void eatSoulImp() {
System.out.println("Stormbringer devours the enemy's soul");
}
}

View File

@ -2,8 +2,6 @@ package com.iluwatar.bridge;
import org.junit.Test;
import com.iluwatar.bridge.App;
/**
*
* Application test
@ -11,9 +9,9 @@ import com.iluwatar.bridge.App;
*/
public class AppTest {
@Test
public void test() {
String[] args = {};
App.main(args);
}
@Test
public void test() {
String[] args = {};
App.main(args);
}
}

View File

@ -0,0 +1,33 @@
package com.iluwatar.bridge;
import org.junit.Test;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
* Date: 12/6/15 - 11:15 PM
*
* @author Jeroen Meulemeester
*/
public class BlindingMagicWeaponTest extends MagicWeaponTest {
/**
* Invoke all possible actions on the weapon and check if the actions are executed on the actual
* underlying weapon implementation.
*/
@Test
public void testExcalibur() throws Exception {
final Excalibur excalibur = spy(new Excalibur());
final BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(excalibur);
testBasicWeaponActions(blindingMagicWeapon, excalibur);
blindingMagicWeapon.blind();
verify(excalibur, times(1)).blindImp();
verifyNoMoreInteractions(excalibur);
}
}

View File

@ -0,0 +1,33 @@
package com.iluwatar.bridge;
import org.junit.Test;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
* Date: 12/6/15 - 11:26 PM
*
* @author Jeroen Meulemeester
*/
public class FlyingMagicWeaponTest extends MagicWeaponTest {
/**
* Invoke all possible actions on the weapon and check if the actions are executed on the actual
* underlying weapon implementation.
*/
@Test
public void testMjollnir() throws Exception {
final Mjollnir mjollnir = spy(new Mjollnir());
final FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(mjollnir);
testBasicWeaponActions(flyingMagicWeapon, mjollnir);
flyingMagicWeapon.fly();
verify(mjollnir, times(1)).flyImp();
verifyNoMoreInteractions(mjollnir);
}
}

View File

@ -0,0 +1,42 @@
package com.iluwatar.bridge;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
* Date: 12/6/15 - 11:28 PM
*
* @author Jeroen Meulemeester
*/
public abstract class MagicWeaponTest {
/**
* Invoke the basic actions of the given weapon, and test if the underlying weapon implementation
* is invoked
*
* @param weaponImpl The spied weapon implementation where actions are bridged to
* @param weapon The weapon, handled by the app
*/
protected final void testBasicWeaponActions(final MagicWeapon weapon,
final MagicWeaponImpl weaponImpl) {
assertNotNull(weapon);
assertNotNull(weaponImpl);
assertNotNull(weapon.getImp());
weapon.swing();
verify(weaponImpl, times(1)).swingImp();
verifyNoMoreInteractions(weaponImpl);
weapon.wield();
verify(weaponImpl, times(1)).wieldImp();
verifyNoMoreInteractions(weaponImpl);
weapon.unwield();
verify(weaponImpl, times(1)).unwieldImp();
verifyNoMoreInteractions(weaponImpl);
}
}

View File

@ -0,0 +1,33 @@
package com.iluwatar.bridge;
import org.junit.Test;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
* Date: 12/6/15 - 11:43 PM
*
* @author Jeroen Meulemeester
*/
public class SoulEatingMagicWeaponTest extends MagicWeaponTest {
/**
* Invoke all possible actions on the weapon and check if the actions are executed on the actual
* underlying weapon implementation.
*/
@Test
public void testStormBringer() throws Exception {
final Stormbringer stormbringer = spy(new Stormbringer());
final SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(stormbringer);
testBasicWeaponActions(soulEatingMagicWeapon, stormbringer);
soulEatingMagicWeapon.eatSoul();
verify(stormbringer, times(1)).eatSoulImp();
verifyNoMoreInteractions(stormbringer);
}
}

View File

@ -7,6 +7,7 @@ categories: Creational
tags:
- Java
- Gang Of Four
- Difficulty-Intermediate
---
**Intent:** Separate the construction of a complex object from its

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.7.0</version>
<version>1.9.0</version>
</parent>
<artifactId>builder</artifactId>
<dependencies>

View File

@ -1,55 +1,55 @@
package com.iluwatar.builder;
import com.iluwatar. builder.Hero.HeroBuilder;
import com.iluwatar.builder.Hero.HeroBuilder;
/**
*
* The intention of the Builder pattern is to find a solution to the telescoping
* constructor anti-pattern. The telescoping constructor anti-pattern occurs when the
* increase of object constructor parameter combination leads to an exponential list
* of constructors. Instead of using numerous constructors, the builder pattern uses
* another object, a builder, that receives each initialization parameter step by step
* and then returns the resulting constructed object at once.
* The intention of the Builder pattern is to find a solution to the telescoping constructor
* anti-pattern. The telescoping constructor anti-pattern occurs when the increase of object
* constructor parameter combination leads to an exponential list of constructors. Instead of using
* numerous constructors, the builder pattern uses another object, a builder, that receives each
* initialization parameter step by step and then returns the resulting constructed object at once.
* <p>
* The Builder pattern has another benefit. It can be used for objects that contain
* flat data (html code, SQL query, X.509 certificate...), that is to say, data that
* can't be easily edited. This type of data cannot be edited step by step and must
* be edited at once. The best way to construct such an object is to use a builder
* class.
* The Builder pattern has another benefit. It can be used for objects that contain flat data (html
* code, SQL query, X.509 certificate...), that is to say, data that can't be easily edited. This
* type of data cannot be edited step by step and must be edited at once. The best way to construct
* such an object is to use a builder class.
* <p>
* In this example we have the Builder pattern variation as described by Joshua Bloch in
* Effective Java 2nd Edition.
* In this example we have the Builder pattern variation as described by Joshua Bloch in Effective
* Java 2nd Edition.
* <p>
* We want to build {@link Hero} objects, but its construction is complex because of the
* many parameters needed. To aid the user we introduce {@link HeroBuilder} class.
* {@link HeroBuilder} takes the minimum parameters to build {@link Hero} object in its
* constructor. After that additional configuration for the {@link Hero} object can be
* done using the fluent {@link HeroBuilder} interface. When configuration is ready the
* build method is called to receive the final {@link Hero} object.
* We want to build {@link Hero} objects, but its construction is complex because of the many
* parameters needed. To aid the user we introduce {@link HeroBuilder} class. {@link HeroBuilder}
* takes the minimum parameters to build {@link Hero} object in its constructor. After that
* additional configuration for the {@link Hero} object can be done using the fluent
* {@link HeroBuilder} interface. When configuration is ready the build method is called to receive
* the final {@link Hero} object.
*
*/
public class App {
/**
* Program entry point
* @param args command line args
*/
public static void main(String[] args) {
/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {
Hero mage = new HeroBuilder(Profession.MAGE, "Riobard")
.withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER)
.build();
System.out.println(mage);
Hero mage =
new HeroBuilder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK)
.withWeapon(Weapon.DAGGER).build();
System.out.println(mage);
Hero warrior = new HeroBuilder(Profession.WARRIOR, "Amberjill")
.withHairColor(HairColor.BLOND)
.withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL)
.withWeapon(Weapon.SWORD).build();
System.out.println(warrior);
Hero warrior =
new HeroBuilder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND)
.withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD)
.build();
System.out.println(warrior);
Hero thief = new HeroBuilder(Profession.THIEF, "Desmond")
.withHairType(HairType.BALD).withWeapon(Weapon.BOW).build();
System.out.println(thief);
Hero thief =
new HeroBuilder(Profession.THIEF, "Desmond").withHairType(HairType.BALD)
.withWeapon(Weapon.BOW).build();
System.out.println(thief);
}
}
}

View File

@ -7,16 +7,16 @@ package com.iluwatar.builder;
*/
public enum Armor {
CLOTHES("clothes"), LEATHER("leather"), CHAIN_MAIL("chain mail"), PLATE_MAIL("plate mail");
CLOTHES("clothes"), LEATHER("leather"), CHAIN_MAIL("chain mail"), PLATE_MAIL("plate mail");
private String title;
private String title;
Armor(String title) {
this.title = title;
}
Armor(String title) {
this.title = title;
}
@Override
public String toString() {
return title;
}
@Override
public String toString() {
return title;
}
}

View File

@ -7,11 +7,11 @@ package com.iluwatar.builder;
*/
public enum HairColor {
WHITE, BLOND, RED, BROWN, BLACK;
WHITE, BLOND, RED, BROWN, BLACK;
@Override
public String toString() {
return name().toLowerCase();
}
@Override
public String toString() {
return name().toLowerCase();
}
}

View File

@ -7,16 +7,17 @@ package com.iluwatar.builder;
*/
public enum HairType {
BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY("long curly");
BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY(
"long curly");
private String title;
private String title;
HairType(String title) {
this.title = title;
}
HairType(String title) {
this.title = title;
}
@Override
public String toString() {
return title;
}
@Override
public String toString() {
return title;
}
}

View File

@ -7,123 +7,125 @@ package com.iluwatar.builder;
*/
public class Hero {
private final Profession profession;
private final String name;
private final HairType hairType;
private final HairColor hairColor;
private final Armor armor;
private final Weapon weapon;
private final Profession profession;
private final String name;
private final HairType hairType;
private final HairColor hairColor;
private final Armor armor;
private final Weapon weapon;
public Profession getProfession() {
return profession;
}
public Profession getProfession() {
return profession;
}
public String getName() {
return name;
}
public String getName() {
return name;
}
public HairType getHairType() {
return hairType;
}
public HairType getHairType() {
return hairType;
}
public HairColor getHairColor() {
return hairColor;
}
public HairColor getHairColor() {
return hairColor;
}
public Armor getArmor() {
return armor;
}
public Armor getArmor() {
return armor;
}
public Weapon getWeapon() {
return weapon;
}
public Weapon getWeapon() {
return weapon;
}
@Override
public String toString() {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("This is a ");
sb.append(profession);
sb.append(" named ");
sb.append(name);
if (hairColor != null || hairType != null) {
sb.append(" with ");
if (hairColor != null) {
sb.append(hairColor);
sb.append(" ");
}
if (hairType != null) {
sb.append(hairType);
sb.append(" ");
}
sb.append(hairType != HairType.BALD ? "hair" : "head");
}
if (armor != null) {
sb.append(" wearing ");
sb.append(armor);
}
if (weapon != null) {
sb.append(" and wielding a ");
sb.append(weapon);
}
sb.append(".");
return sb.toString();
}
StringBuilder sb = new StringBuilder();
sb.append("This is a ");
sb.append(profession);
sb.append(" named ");
sb.append(name);
if (hairColor != null || hairType != null) {
sb.append(" with ");
if (hairColor != null) {
sb.append(hairColor);
sb.append(" ");
}
if (hairType != null) {
sb.append(hairType);
sb.append(" ");
}
sb.append(hairType != HairType.BALD ? "hair" : "head");
}
if (armor != null) {
sb.append(" wearing ");
sb.append(armor);
}
if (weapon != null) {
sb.append(" and wielding a ");
sb.append(weapon);
}
sb.append(".");
return sb.toString();
}
private Hero(HeroBuilder builder) {
this.profession = builder.profession;
this.name = builder.name;
this.hairColor = builder.hairColor;
this.hairType = builder.hairType;
this.weapon = builder.weapon;
this.armor = builder.armor;
}
private Hero(HeroBuilder builder) {
this.profession = builder.profession;
this.name = builder.name;
this.hairColor = builder.hairColor;
this.hairType = builder.hairType;
this.weapon = builder.weapon;
this.armor = builder.armor;
}
/**
*
* The builder class.
*
*/
public static class HeroBuilder {
/**
*
* The builder class.
*
*/
public static class HeroBuilder {
private final Profession profession;
private final String name;
private HairType hairType;
private HairColor hairColor;
private Armor armor;
private Weapon weapon;
private final Profession profession;
private final String name;
private HairType hairType;
private HairColor hairColor;
private Armor armor;
private Weapon weapon;
public HeroBuilder(Profession profession, String name) {
if (profession == null || name == null) {
throw new IllegalArgumentException(
"profession and name can not be null");
}
this.profession = profession;
this.name = name;
}
/**
* Constructor
*/
public HeroBuilder(Profession profession, String name) {
if (profession == null || name == null) {
throw new IllegalArgumentException("profession and name can not be null");
}
this.profession = profession;
this.name = name;
}
public HeroBuilder withHairType(HairType hairType) {
this.hairType = hairType;
return this;
}
public HeroBuilder withHairType(HairType hairType) {
this.hairType = hairType;
return this;
}
public HeroBuilder withHairColor(HairColor hairColor) {
this.hairColor = hairColor;
return this;
}
public HeroBuilder withHairColor(HairColor hairColor) {
this.hairColor = hairColor;
return this;
}
public HeroBuilder withArmor(Armor armor) {
this.armor = armor;
return this;
}
public HeroBuilder withArmor(Armor armor) {
this.armor = armor;
return this;
}
public HeroBuilder withWeapon(Weapon weapon) {
this.weapon = weapon;
return this;
}
public HeroBuilder withWeapon(Weapon weapon) {
this.weapon = weapon;
return this;
}
public Hero build() {
return new Hero(this);
}
}
public Hero build() {
return new Hero(this);
}
}
}

View File

@ -7,11 +7,10 @@ package com.iluwatar.builder;
*/
public enum Profession {
WARRIOR, THIEF, MAGE, PRIEST;
@Override
public String toString() {
return name().toLowerCase();
}
WARRIOR, THIEF, MAGE, PRIEST;
@Override
public String toString() {
return name().toLowerCase();
}
}

View File

@ -7,11 +7,10 @@ package com.iluwatar.builder;
*/
public enum Weapon {
DAGGER, SWORD, AXE, WARHAMMER, BOW;
@Override
public String toString() {
return name().toLowerCase();
}
DAGGER, SWORD, AXE, WARHAMMER, BOW;
@Override
public String toString() {
return name().toLowerCase();
}
}

View File

@ -2,8 +2,6 @@ package com.iluwatar.builder;
import org.junit.Test;
import com.iluwatar. builder.App;
/**
*
* Application test
@ -11,9 +9,9 @@ import com.iluwatar. builder.App;
*/
public class AppTest {
@Test
public void test() {
String[] args = {};
App.main(args);
}
@Test
public void test() {
String[] args = {};
App.main(args);
}
}

View File

@ -0,0 +1,56 @@
package com.iluwatar.builder;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Date: 12/6/15 - 11:01 PM
*
* @author Jeroen Meulemeester
*/
public class HeroTest {
/**
* Test if we get the expected exception when trying to create a hero without a profession
*/
@Test(expected = IllegalArgumentException.class)
public void testMissingProfession() throws Exception {
new Hero.HeroBuilder(null, "Sir without a job");
}
/**
* Test if we get the expected exception when trying to create a hero without a name
*/
@Test(expected = IllegalArgumentException.class)
public void testMissingName() throws Exception {
new Hero.HeroBuilder(Profession.THIEF, null);
}
/**
* Test if the hero build by the builder has the correct attributes, as requested
*/
@Test
public void testBuildHero() throws Exception {
final String heroName = "Sir Lancelot";
final Hero hero = new Hero.HeroBuilder(Profession.WARRIOR, heroName)
.withArmor(Armor.CHAIN_MAIL)
.withWeapon(Weapon.SWORD)
.withHairType(HairType.LONG_CURLY)
.withHairColor(HairColor.BLOND)
.build();
assertNotNull(hero);
assertNotNull(hero.toString());
assertEquals(Profession.WARRIOR, hero.getProfession());
assertEquals(heroName, hero.getName());
assertEquals(Armor.CHAIN_MAIL, hero.getArmor());
assertEquals(Weapon.SWORD, hero.getWeapon());
assertEquals(HairType.LONG_CURLY, hero.getHairType());
assertEquals(HairColor.BLOND, hero.getHairColor());
}
}

View File

@ -4,7 +4,9 @@ title: Business Delegate
folder: business-delegate
permalink: /patterns/business-delegate/
categories: Business Tier
tags: Java
tags:
- Java
- Difficulty-Intermediate
---
**Intent:** The Business Delegate pattern adds an abstraction layer between

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.7.0</version>
<version>1.9.0</version>
</parent>
<artifactId>business-delegate</artifactId>
<dependencies>
@ -15,5 +15,10 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,35 +1,40 @@
package com.iluwatar.business.delegate;
/**
* The Business Delegate pattern adds an abstraction layer between the presentation and business
* tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate
* encapsulates knowledge about how to locate, connect to, and interact with the business objects
* that make up the application.
*
* The Business Delegate pattern adds an abstraction layer between the presentation and business tiers.
* By using the pattern we gain loose coupling between the tiers. The Business Delegate encapsulates
* knowledge about how to locate, connect to, and interact with the business objects that make up
* the application.
* <p>
* Some of the services the Business Delegate uses are instantiated directly, and some can be retrieved
* through service lookups. The Business Delegate itself may contain business logic too potentially tying
* together multiple service calls, exception handling, retrying etc.
* <p>
* In this example the client ({@link Client}) utilizes a business delegate ({@link BusinessDelegate}) to execute a task.
* The Business Delegate then selects the appropriate service and makes the service call.
*
* <p>Some of the services the Business Delegate uses are instantiated directly, and some can be
* retrieved through service lookups. The Business Delegate itself may contain business logic too
* potentially tying together multiple service calls, exception handling, retrying etc.
*
* <p>In this example the client ({@link Client}) utilizes a business delegate (
* {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
* service and makes the service call.
*/
public class App {
/**
* Program entry point
* @param args command line args
*/
public static void main(String[] args) {
BusinessDelegate businessDelegate = new BusinessDelegate();
businessDelegate.setServiceType(ServiceType.EJB);
Client client = new Client(businessDelegate);
client.doTask();
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
businessDelegate.setServiceType(ServiceType.JMS);
client.doTask();
}
BusinessDelegate businessDelegate = new BusinessDelegate();
BusinessLookup businessLookup = new BusinessLookup();
businessLookup.setEjbService(new EjbService());
businessLookup.setJmsService(new JmsService());
businessDelegate.setLookupService(businessLookup);
businessDelegate.setServiceType(ServiceType.EJB);
Client client = new Client(businessDelegate);
client.doTask();
businessDelegate.setServiceType(ServiceType.JMS);
client.doTask();
}
}

View File

@ -1,22 +1,24 @@
package com.iluwatar.business.delegate;
/**
*
* BusinessDelegate separates the presentation and business tiers
*
*/
public class BusinessDelegate {
private BusinessLookup lookupService = new BusinessLookup();
private BusinessService businessService;
private ServiceType serviceType;
public void setServiceType(ServiceType serviceType) {
this.serviceType = serviceType;
}
private BusinessLookup lookupService;
private BusinessService businessService;
private ServiceType serviceType;
public void doTask() {
businessService = lookupService.getBusinessService(serviceType);
businessService.doProcessing();
}
public void setLookupService(BusinessLookup businessLookup) {
this.lookupService = businessLookup;
}
public void setServiceType(ServiceType serviceType) {
this.serviceType = serviceType;
}
public void doTask() {
businessService = lookupService.getBusinessService(serviceType);
businessService.doProcessing();
}
}

View File

@ -1,17 +1,31 @@
package com.iluwatar.business.delegate;
/**
*
* Class for performing service lookups
*
* Class for performing service lookups.
*/
public class BusinessLookup {
public BusinessService getBusinessService(ServiceType serviceType) {
if (serviceType.equals(ServiceType.EJB)) {
return new EjbService();
} else {
return new JmsService();
}
}
private EjbService ejbService;
private JmsService jmsService;
/**
* @param serviceType Type of service instance to be returned.
* @return Service instance.
*/
public BusinessService getBusinessService(ServiceType serviceType) {
if (serviceType.equals(ServiceType.EJB)) {
return ejbService;
} else {
return jmsService;
}
}
public void setJmsService(JmsService jmsService) {
this.jmsService = jmsService;
}
public void setEjbService(EjbService ejbService) {
this.ejbService = ejbService;
}
}

View File

@ -7,5 +7,5 @@ package com.iluwatar.business.delegate;
*/
public interface BusinessService {
void doProcessing();
void doProcessing();
}

View File

@ -7,13 +7,13 @@ package com.iluwatar.business.delegate;
*/
public class Client {
private BusinessDelegate businessDelegate;
private BusinessDelegate businessDelegate;
public Client(BusinessDelegate businessDelegate) {
this.businessDelegate = businessDelegate;
}
public Client(BusinessDelegate businessDelegate) {
this.businessDelegate = businessDelegate;
}
public void doTask() {
businessDelegate.doTask();
}
public void doTask() {
businessDelegate.doTask();
}
}

View File

@ -7,8 +7,8 @@ package com.iluwatar.business.delegate;
*/
public class EjbService implements BusinessService {
@Override
public void doProcessing() {
System.out.println("EjbService is now processing");
}
@Override
public void doProcessing() {
System.out.println("EjbService is now processing");
}
}

View File

@ -7,8 +7,8 @@ package com.iluwatar.business.delegate;
*/
public class JmsService implements BusinessService {
@Override
public void doProcessing() {
System.out.println("JmsService is now processing");
}
@Override
public void doProcessing() {
System.out.println("JmsService is now processing");
}
}

View File

@ -6,6 +6,6 @@ package com.iluwatar.business.delegate;
*
*/
public enum ServiceType {
EJB, JMS;
EJB, JMS;
}

View File

@ -1,19 +0,0 @@
package com.iluwatar.business.delegate;
import org.junit.Test;
import com.iluwatar.business.delegate.App;
/**
*
* Application test
*
*/
public class AppTest {
@Test
public void test() {
String[] args = {};
App.main(args);
}
}

View File

@ -0,0 +1,78 @@
package com.iluwatar.business.delegate;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.junit.Before;
import org.junit.Test;
/**
* The Business Delegate pattern adds an abstraction layer between the presentation and business
* tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate
* encapsulates knowledge about how to locate, connect to, and interact with the business objects
* that make up the application.
*
* <p>Some of the services the Business Delegate uses are instantiated directly, and some can be
* retrieved through service lookups. The Business Delegate itself may contain business logic too
* potentially tying together multiple service calls, exception handling, retrying etc.
*/
public class BusinessDelegateTest {
private EjbService ejbService;
private JmsService jmsService;
private BusinessLookup businessLookup;
private BusinessDelegate businessDelegate;
/**
* This method sets up the instance variables of this test class. It is executed before the
* execution of every test.
*/
@Before
public void setup() {
ejbService = spy(new EjbService());
jmsService = spy(new JmsService());
businessLookup = spy(new BusinessLookup());
businessLookup.setEjbService(ejbService);
businessLookup.setJmsService(jmsService);
businessDelegate = spy(new BusinessDelegate());
businessDelegate.setLookupService(businessLookup);
}
/**
* In this example the client ({@link Client}) utilizes a business delegate (
* {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
* service and makes the service call.
*/
@Test
public void testBusinessDelegate() {
// setup a client object
Client client = new Client(businessDelegate);
// set the service type
businessDelegate.setServiceType(ServiceType.EJB);
// action
client.doTask();
// verifying that the businessDelegate was used by client during doTask() method.
verify(businessDelegate).doTask();
verify(ejbService).doProcessing();
// set the service type
businessDelegate.setServiceType(ServiceType.JMS);
// action
client.doTask();
// verifying that the businessDelegate was used by client during doTask() method.
verify(businessDelegate, times(2)).doTask();
verify(jmsService).doProcessing();
}
}

View File

@ -6,6 +6,8 @@ permalink: /patterns/caching/
categories: Other
tags:
- Java
- Difficulty-Intermediate
- Performance
---
**Intent:** To avoid expensive re-acquisition of resources by not releasing

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.7.0</version>
<version>1.9.0</version>
</parent>
<artifactId>caching</artifactId>
<dependencies>

View File

@ -21,7 +21,7 @@ package com.iluwatar.caching;
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts
* a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The three
* strategies are individually tested. The testing of the cache is restricted towards saving and
* querying of user accounts from the underlying data store ( {@link DBManager}). The main class (
* querying of user accounts from the underlying data store ( {@link DbManager}). The main class (
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
* whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
* ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
@ -43,7 +43,7 @@ public class App {
* @param args command line args
*/
public static void main(String[] args) {
AppManager.initDB(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
// and the App class to avoid Maven compilation errors. Set flag to
// true to run the tests with MongoDB (provided that MongoDB is
// installed and socket connection is open).
@ -65,8 +65,8 @@ public class App {
AppManager.save(userAccount1);
System.out.println(AppManager.printCacheContent());
userAccount1 = AppManager.find("001");
userAccount1 = AppManager.find("001");
AppManager.find("001");
AppManager.find("001");
}
/**
@ -80,15 +80,15 @@ public class App {
AppManager.save(userAccount2);
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
AppManager.find("002");
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
userAccount2.setUserName("Jane G.");
AppManager.save(userAccount2);
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
AppManager.find("002");
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
AppManager.find("002");
}
/**
@ -106,12 +106,12 @@ public class App {
AppManager.save(userAccount4);
AppManager.save(userAccount5);
System.out.println(AppManager.printCacheContent());
userAccount3 = AppManager.find("003");
AppManager.find("003");
System.out.println(AppManager.printCacheContent());
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
AppManager.save(userAccount6);
System.out.println(AppManager.printCacheContent());
userAccount4 = AppManager.find("004");
AppManager.find("004");
System.out.println(AppManager.printCacheContent());
}
}

View File

@ -15,24 +15,30 @@ public class AppManager {
private static CachingPolicy cachingPolicy;
private AppManager() {
}
/**
*
* Developer/Tester is able to choose whether the application should use MongoDB as its underlying
* data storage or a simple Java data structure to (temporarily) store the data/objects during
* runtime.
*/
public static void initDB(boolean useMongoDB) {
if (useMongoDB) {
public static void initDb(boolean useMongoDb) {
if (useMongoDb) {
try {
DBManager.connect();
DbManager.connect();
} catch (ParseException e) {
e.printStackTrace();
}
} else {
DBManager.createVirtualDB();
DbManager.createVirtualDb();
}
}
/**
* Initialize caching policy
*/
public static void initCachingPolicy(CachingPolicy policy) {
cachingPolicy = policy;
if (cachingPolicy == CachingPolicy.BEHIND) {
@ -50,15 +56,21 @@ public class AppManager {
CacheStore.initCapacity(capacity);
}
public static UserAccount find(String userID) {
/**
* Find user account
*/
public static UserAccount find(String userId) {
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
return CacheStore.readThrough(userID);
return CacheStore.readThrough(userId);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
return CacheStore.readThroughWithWriteBackPolicy(userID);
return CacheStore.readThroughWithWriteBackPolicy(userId);
}
return null;
}
/**
* Save user account
*/
public static void save(UserAccount userAccount) {
if (cachingPolicy == CachingPolicy.THROUGH) {
CacheStore.writeThrough(userAccount);

View File

@ -9,73 +9,99 @@ import java.util.ArrayList;
*/
public class CacheStore {
static LRUCache cache = null;
static LruCache cache = null;
public static void initCapacity(int capacity) {
if (null == cache)
cache = new LRUCache(capacity);
else
cache.setCapacity(capacity);
private CacheStore() {
}
public static UserAccount readThrough(String userID) {
if (cache.contains(userID)) {
/**
* Init cache capacity
*/
public static void initCapacity(int capacity) {
if (null == cache) {
cache = new LruCache(capacity);
} else {
cache.setCapacity(capacity);
}
}
/**
* Get user account using read-through cache
*/
public static UserAccount readThrough(String userId) {
if (cache.contains(userId)) {
System.out.println("# Cache Hit!");
return cache.get(userID);
return cache.get(userId);
}
System.out.println("# Cache Miss!");
UserAccount userAccount = DBManager.readFromDB(userID);
cache.set(userID, userAccount);
UserAccount userAccount = DbManager.readFromDb(userId);
cache.set(userId, userAccount);
return userAccount;
}
/**
* Get user account using write-through cache
*/
public static void writeThrough(UserAccount userAccount) {
if (cache.contains(userAccount.getUserID())) {
DBManager.updateDB(userAccount);
if (cache.contains(userAccount.getUserId())) {
DbManager.updateDb(userAccount);
} else {
DBManager.writeToDB(userAccount);
DbManager.writeToDb(userAccount);
}
cache.set(userAccount.getUserID(), userAccount);
cache.set(userAccount.getUserId(), userAccount);
}
/**
* Get user account using write-around cache
*/
public static void writeAround(UserAccount userAccount) {
if (cache.contains(userAccount.getUserID())) {
DBManager.updateDB(userAccount);
cache.invalidate(userAccount.getUserID()); // Cache data has been updated -- remove older
if (cache.contains(userAccount.getUserId())) {
DbManager.updateDb(userAccount);
cache.invalidate(userAccount.getUserId()); // Cache data has been updated -- remove older
// version from cache.
} else {
DBManager.writeToDB(userAccount);
DbManager.writeToDb(userAccount);
}
}
public static UserAccount readThroughWithWriteBackPolicy(String userID) {
if (cache.contains(userID)) {
/**
* Get user account using read-through cache with write-back policy
*/
public static UserAccount readThroughWithWriteBackPolicy(String userId) {
if (cache.contains(userId)) {
System.out.println("# Cache Hit!");
return cache.get(userID);
return cache.get(userId);
}
System.out.println("# Cache Miss!");
UserAccount userAccount = DBManager.readFromDB(userID);
UserAccount userAccount = DbManager.readFromDb(userId);
if (cache.isFull()) {
System.out.println("# Cache is FULL! Writing LRU data to DB...");
UserAccount toBeWrittenToDB = cache.getLRUData();
DBManager.upsertDB(toBeWrittenToDB);
UserAccount toBeWrittenToDb = cache.getLruData();
DbManager.upsertDb(toBeWrittenToDb);
}
cache.set(userID, userAccount);
cache.set(userId, userAccount);
return userAccount;
}
/**
* Set user account
*/
public static void writeBehind(UserAccount userAccount) {
if (cache.isFull() && !cache.contains(userAccount.getUserID())) {
if (cache.isFull() && !cache.contains(userAccount.getUserId())) {
System.out.println("# Cache is FULL! Writing LRU data to DB...");
UserAccount toBeWrittenToDB = cache.getLRUData();
DBManager.upsertDB(toBeWrittenToDB);
UserAccount toBeWrittenToDb = cache.getLruData();
DbManager.upsertDb(toBeWrittenToDb);
}
cache.set(userAccount.getUserID(), userAccount);
cache.set(userAccount.getUserId(), userAccount);
}
/**
* Clears cache
*/
public static void clearCache() {
if (null != cache)
if (null != cache) {
cache.clear();
}
}
/**
@ -83,14 +109,18 @@ public class CacheStore {
*/
public static void flushCache() {
System.out.println("# flushCache...");
if (null == cache)
if (null == cache) {
return;
}
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
for (UserAccount userAccount : listOfUserAccounts) {
DBManager.upsertDB(userAccount);
DbManager.upsertDb(userAccount);
}
}
/**
* Print user accounts
*/
public static String print() {
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
StringBuilder sb = new StringBuilder();

View File

@ -21,7 +21,7 @@ import com.mongodb.client.model.UpdateOptions;
* during runtime (createVirtualDB()).</p>
*
*/
public class DBManager {
public class DbManager {
private static MongoClient mongoClient;
private static MongoDatabase db;
@ -29,21 +29,34 @@ public class DBManager {
private static HashMap<String, UserAccount> virtualDB;
public static void createVirtualDB() {
private DbManager() {
}
/**
* Create DB
*/
public static void createVirtualDb() {
useMongoDB = false;
virtualDB = new HashMap<String, UserAccount>();
}
/**
* Connect to DB
*/
public static void connect() throws ParseException {
useMongoDB = true;
mongoClient = new MongoClient();
db = mongoClient.getDatabase("test");
}
public static UserAccount readFromDB(String userID) {
/**
* Read user account from DB
*/
public static UserAccount readFromDb(String userId) {
if (!useMongoDB) {
if (virtualDB.containsKey(userID))
return virtualDB.get(userID);
if (virtualDB.containsKey(userId)) {
return virtualDB.get(userId);
}
return null;
}
if (null == db) {
@ -54,18 +67,22 @@ public class DBManager {
}
}
FindIterable<Document> iterable =
db.getCollection("user_accounts").find(new Document("userID", userID));
if (iterable == null)
db.getCollection("user_accounts").find(new Document("userID", userId));
if (iterable == null) {
return null;
}
Document doc = iterable.first();
UserAccount userAccount =
new UserAccount(userID, doc.getString("userName"), doc.getString("additionalInfo"));
new UserAccount(userId, doc.getString("userName"), doc.getString("additionalInfo"));
return userAccount;
}
public static void writeToDB(UserAccount userAccount) {
/**
* Write user account to DB
*/
public static void writeToDb(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
if (null == db) {
@ -76,13 +93,16 @@ public class DBManager {
}
}
db.getCollection("user_accounts").insertOne(
new Document("userID", userAccount.getUserID()).append("userName",
new Document("userID", userAccount.getUserId()).append("userName",
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo()));
}
public static void updateDB(UserAccount userAccount) {
/**
* Update DB
*/
public static void updateDb(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
if (null == db) {
@ -93,7 +113,7 @@ public class DBManager {
}
}
db.getCollection("user_accounts").updateOne(
new Document("userID", userAccount.getUserID()),
new Document("userID", userAccount.getUserId()),
new Document("$set", new Document("userName", userAccount.getUserName()).append(
"additionalInfo", userAccount.getAdditionalInfo())));
}
@ -102,9 +122,9 @@ public class DBManager {
*
* Insert data into DB if it does not exist. Else, update it.
*/
public static void upsertDB(UserAccount userAccount) {
public static void upsertDb(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
if (null == db) {
@ -115,8 +135,8 @@ public class DBManager {
}
}
db.getCollection("user_accounts").updateOne(
new Document("userID", userAccount.getUserID()),
new Document("$set", new Document("userID", userAccount.getUserID()).append("userName",
new Document("userID", userAccount.getUserId()),
new Document("$set", new Document("userID", userAccount.getUserId()).append("userName",
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo())),
new UpdateOptions().upsert(true));
}

View File

@ -12,16 +12,16 @@ import java.util.HashMap;
* LRU data is always at the end of the list.
*
*/
public class LRUCache {
public class LruCache {
class Node {
String userID;
String userId;
UserAccount userAccount;
Node previous;
Node next;
public Node(String userID, UserAccount userAccount) {
this.userID = userID;
public Node(String userId, UserAccount userAccount) {
this.userId = userId;
this.userAccount = userAccount;
}
}
@ -31,13 +31,16 @@ public class LRUCache {
Node head = null;
Node end = null;
public LRUCache(int capacity) {
public LruCache(int capacity) {
this.capacity = capacity;
}
public UserAccount get(String userID) {
if (cache.containsKey(userID)) {
Node node = cache.get(userID);
/**
* Get user account
*/
public UserAccount get(String userId) {
if (cache.containsKey(userId)) {
Node node = cache.get(userId);
remove(node);
setHead(node);
return node.userAccount;
@ -69,52 +72,63 @@ public class LRUCache {
public void setHead(Node node) {
node.next = head;
node.previous = null;
if (head != null)
if (head != null) {
head.previous = node;
}
head = node;
if (end == null)
if (end == null) {
end = head;
}
}
public void set(String userID, UserAccount userAccount) {
if (cache.containsKey(userID)) {
Node old = cache.get(userID);
/**
* Set user account
*/
public void set(String userId, UserAccount userAccount) {
if (cache.containsKey(userId)) {
Node old = cache.get(userId);
old.userAccount = userAccount;
remove(old);
setHead(old);
} else {
Node newNode = new Node(userID, userAccount);
Node newNode = new Node(userId, userAccount);
if (cache.size() >= capacity) {
System.out.println("# Cache is FULL! Removing " + end.userID + " from cache...");
cache.remove(end.userID); // remove LRU data from cache.
System.out.println("# Cache is FULL! Removing " + end.userId + " from cache...");
cache.remove(end.userId); // remove LRU data from cache.
remove(end);
setHead(newNode);
} else {
setHead(newNode);
}
cache.put(userID, newNode);
cache.put(userId, newNode);
}
}
public boolean contains(String userID) {
return cache.containsKey(userID);
public boolean contains(String userId) {
return cache.containsKey(userId);
}
public void invalidate(String userID) {
System.out.println("# " + userID + " has been updated! Removing older version from cache...");
Node toBeRemoved = cache.get(userID);
/**
* Invalidate cache for user
*/
public void invalidate(String userId) {
System.out.println("# " + userId + " has been updated! Removing older version from cache...");
Node toBeRemoved = cache.get(userId);
remove(toBeRemoved);
cache.remove(userID);
cache.remove(userId);
}
public boolean isFull() {
return cache.size() >= capacity;
}
public UserAccount getLRUData() {
public UserAccount getLruData() {
return end.userAccount;
}
/**
* Clear cache
*/
public void clear() {
head = null;
end = null;
@ -135,6 +149,9 @@ public class LRUCache {
return listOfCacheData;
}
/**
* Set cache capacity
*/
public void setCapacity(int newCapacity) {
if (capacity > newCapacity) {
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll

View File

@ -6,22 +6,25 @@ package com.iluwatar.caching;
*
*/
public class UserAccount {
private String userID;
private String userId;
private String userName;
private String additionalInfo;
public UserAccount(String userID, String userName, String additionalInfo) {
this.userID = userID;
/**
* Constructor
*/
public UserAccount(String userId, String userName, String additionalInfo) {
this.userId = userId;
this.userName = userName;
this.additionalInfo = additionalInfo;
}
public String getUserID() {
return userID;
public String getUserId() {
return userId;
}
public void setUserID(String userID) {
this.userID = userID;
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
@ -42,6 +45,6 @@ public class UserAccount {
@Override
public String toString() {
return userID + ", " + userName + ", " + additionalInfo;
return userId + ", " + userName + ", " + additionalInfo;
}
}

View File

@ -16,7 +16,7 @@ public class AppTest {
*/
@Before
public void setUp() {
AppManager.initDB(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
// to avoid Maven compilation errors. Set flag to true to run the
// tests with MongoDB (provided that MongoDB is installed and socket
// connection is open).

View File

@ -4,7 +4,11 @@ title: Callback
folder: callback
permalink: /patterns/callback/
categories: Other
tags: Java
tags:
- Java
- Difficulty-Beginner
- Functional
- Idiom
---
**Intent:** Callback is a piece of executable code that is passed as an

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.7.0</version>
<version>1.9.0</version>
</parent>
<artifactId>callback</artifactId>
<dependencies>

View File

@ -2,20 +2,24 @@ package com.iluwatar.callback;
/**
*
* Callback pattern is more native for functional languages where functions are treated as first-class citizens.
* Prior to Java 8 callbacks can be simulated using simple (alike command) interfaces.
* Callback pattern is more native for functional languages where functions are treated as
* first-class citizens. Prior to Java 8 callbacks can be simulated using simple (alike command)
* interfaces.
*
*/
public class App {
public static void main(String[] args) {
Task task = new SimpleTask();
Callback callback = new Callback() {
@Override
public void call() {
System.out.println("I'm done now.");
}
};
task.executeWith(callback);
}
/**
* Program entry point
*/
public static void main(String[] args) {
Task task = new SimpleTask();
Callback callback = new Callback() {
@Override
public void call() {
System.out.println("I'm done now.");
}
};
task.executeWith(callback);
}
}

View File

@ -7,5 +7,5 @@ package com.iluwatar.callback;
*/
public interface Callback {
public void call();
void call();
}

View File

@ -7,9 +7,8 @@ package com.iluwatar.callback;
*/
public class SimpleTask extends Task {
@Override
public void execute() {
System.out.println("Perform some important activity and after call the callback method.");
}
@Override
public void execute() {
System.out.println("Perform some important activity and after call the callback method.");
}
}

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