Compare commits

..

126 Commits
urm ... 1.8.0

Author SHA1 Message Date
f018d13c39 Set version number for monthly release 2015-12-02 23:21:44 +02: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
a2dd87d28d Merge pull request #277 from waisuan/master
Issue #273: Caching Patterns [new pattern]
2015-11-01 22:12:35 +02:00
998ba7e6e0 Issue #273: Fixed HTML tags in comments 2015-11-02 02:11:58 +08:00
6418a6c2b6 Issue #273: Fixed issues based on review remarks 2015-11-02 01:40:38 +08:00
37cfa4b295 Merge remote-tracking branch 'upstream/master' 2015-11-02 01:38:37 +08:00
7d3483daad Merge pull request #280 from ankurkaushal/master
Reformat Code According to Google Style Guide #224
2015-11-01 17:57:14 +02:00
44b7b94da6 Reformatting test case as well 2015-11-01 02:23:27 -05:00
87baa98d2b Reformat Code According to Google Style Guide #224 2015-11-01 02:05:54 -05:00
3dc9b2f97e Merge pull request #276 from hoswey/master
#271 implements producer-consumer
2015-10-31 19:02:03 +02:00
d0f5009996 Issue #273: Changed DB to internal Java data structure to avoid compilation errors + decrease in code coverage 2015-10-29 00:57:41 +08:00
9891c2e17b Issue #273:Caching Patterns [new pattern] 2015-10-28 23:55:47 +08:00
07faa2f625 #271 implements producer-consumer 2015-10-28 09:55:05 +08:00
a869294851 Merge pull request #257 from mafagafogigante/readme-warning-for-beginners
A Warning for beginners in the Readme.
2015-10-25 08:47:49 +02:00
ac5c1967df #161 - Publish Subscribe Channel realisation.Heavily based on iluwatars Message Channel implementation (#143). 2015-10-25 07:11:34 +01:00
a2b8359ab5 Merge pull request #269 from iamrichardjones/master
Add unit test to callback method
2015-10-20 23:37:39 +03:00
21a4d7b4c9 Add unit test to show that the callback method is called. 2015-10-19 22:07:00 +01:00
83c2fbdcd3 Add unit test to show that the callback method is called. 2015-10-19 22:06:35 +01:00
8eff279233 Merge pull request #263 from iamrichardjones/master
Update to singleton unit tests and lazy loading unit test
2015-10-19 21:21:45 +03:00
b961c57919 Merge pull request #268 from iluwatar/themoffster-master
Themoffster master - unit test improvements
2015-10-19 21:14:58 +03:00
b028f66b08 Removed properties 2015-10-19 21:01:41 +03:00
339db2a0bf Merge branch 'master' of https://github.com/themoffster/java-design-patterns into themoffster-master
Conflicts:
	dao/pom.xml
2015-10-19 20:44:46 +03:00
9cbc4a20b9 Merge pull request #265 from mkobit/double-checked-locking-executor-service-shutdown
Fix - Shutdown the ExecutorService in App so that the resources are c…
2015-10-14 20:10:46 +03:00
07a77ba11d Fix - Shutdown the ExecutorService in App so that the resources are collected and the process can finish
I ran App.main(String[] args) in the 'double-checked-locking' module and the process does not terminate. This is because the executor service still has open threads. I'm not sure how the JUnit tests are run, but it seems they are handling the leftover resources themselves.

Also, minor modifications to Inventory for final fields are used since there is no state change around them, and added some more meaningful printing so the example is more clearly demonstrated
2015-10-14 07:01:15 -05:00
e09dda6037 A Warning for beginners in the Readme. 2015-10-14 06:41:32 -03:00
7ab799c452 Synchronise the list as multiple threads are calling it 2015-10-13 21:23:32 -03:00
3c8b83748a Merge pull request #258 from amit2103/master
MonoState Pattern
2015-10-13 20:03:45 +03:00
5d970438bf Update comment 2015-10-12 05:22:09 -03:00
0d068a35d8 Update comment 2015-10-12 05:21:33 -03:00
64e3e1a9e8 For some reason it thinks there are two fields in the CI build. Making this more generic 2015-10-11 22:16:51 -03:00
31e2940eb1 Remove error unit test so pull request can proceed. Will check this at at later date 2015-10-11 22:11:03 -03:00
0107b24976 Fix unit test by makinig getField use the field name directly 2015-10-11 22:06:00 -03:00
7cf5b32086 Add additional unit tests to show how lazy loading is working with reflection 2015-10-11 21:58:13 -03:00
45b0ac386e Add additional unit tests to show that singletons can be created in single thread environment and multithread environment. Also add a test to demonstrate a whole with Singleton when instantiating using reflection
Add some logging. Tests pass locally but not on github?
2015-10-11 21:54:45 -03:00
6ba7f5ea04 Add additional unit tests to show that singletons can be created in single thread environment and multithread environment. Also add a test to demonstrate a whole with Singleton when instantiating using reflection 2015-10-11 21:32:51 -03:00
271ce09e26 Fixed POMS 2015-10-09 23:26:10 +05:30
9d0673af0a Made changes according to feedback issue #258 2015-10-09 23:23:34 +05:30
eb22febdfc Printing Server variables 2015-10-09 23:23:34 +05:30
9a824c7d21 MonoState #85 2015-10-09 23:23:34 +05:30
81b5bc99ce Update Pom.xml
Fixed Reference to Parent POM
2015-10-08 09:43:19 +05:30
19f5966cb9 Added Comments and Feedback 2015-10-07 20:37:45 +05:30
1e988c10f9 fixed merge conflict 2015-10-07 20:10:36 +05:30
2c45f73254 MonoState #85 2015-10-04 03:12:51 +05:30
c8fd9f3a0d Monostate pattern #85 2015-10-04 03:06:11 +05:30
0a9879a277 Improve Strategy Javadoc 2015-10-04 00:22:27 +03:00
2234a25c76 Improve State Javadoc 2015-10-04 00:19:34 +03:00
74e32259be Improve Service Locator Javadoc 2015-10-04 00:16:05 +03:00
8db2bb2ac8 Improve Servant Javadoc 2015-10-04 00:13:08 +03:00
c3a827b475 Improved Proxy Javadoc 2015-10-04 00:08:34 +03:00
9a4f83e7b8 Improve Prototype Javadoc 2015-10-04 00:04:12 +03:00
40e378c1f3 Improve Property Javadoc 2015-10-03 23:57:17 +03:00
98aa28d94e Improve Poison Pill Javadoc 2015-10-03 23:53:09 +03:00
4d1aae21f7 Improve Observer Javadoc 2015-10-03 23:48:51 +03:00
ca6bb7a3a8 Improve Memento Javadoc 2015-10-03 23:38:37 +03:00
fc66ae8084 Improve Mediator Javadoc 2015-10-03 23:34:20 +03:00
e4ff39e080 Improve Iterator Javadoc 2015-10-03 21:37:29 +03:00
44d7be9c94 Improve Interpreter Javadoc 2015-10-03 21:32:56 +03:00
b4118bb866 Improve Intercepting Filter Javadoc 2015-10-03 21:28:17 +03:00
c27291fd27 Improve Fluent Interface Javadoc 2015-10-03 21:17:56 +03:00
c989f6cb21 Improve Factory Method Javadoc 2015-10-03 21:14:40 +03:00
516b127d21 Improved Facade Javadoc 2015-10-03 21:11:19 +03:00
584a22238d Improve Event Aggregator Javadoc 2015-10-03 21:06:52 +03:00
e32b440a38 Improve Double Checked Locking Javadoc 2015-10-03 21:00:21 +03:00
8cf35fc315 Improve Decorator Javadoc 2015-10-03 20:43:38 +03:00
621793ed2d Improved DAO Javadoc 2015-10-03 20:34:15 +03:00
807478ab3d Improve Composite Javadoc 2015-10-03 20:27:28 +03:00
d5f52edecf Improve Command Javadoc 2015-10-03 20:21:56 +03:00
a2f3d58709 Improve Chain Javadoc 2015-10-03 20:19:01 +03:00
9a08e35101 Improve Builder Javadoc 2015-10-03 17:13:38 +03:00
cdd586ec7c Improve Bridge Javadoc 2015-10-03 12:40:24 +03:00
8c6caa29b7 Improve Adapter Javadoc 2015-10-03 11:56:59 +03:00
0a61d7b067 Improve Abstract Factory Javadocs 2015-10-03 11:53:44 +03:00
6413c4d2be Added more Singleton documentation #188 2015-10-02 22:53:41 +03:00
11cdd20f6f Update version to 1.7.0 2015-09-30 22:36:01 +03:00
621c3498f6 Remove broken deploy config #255 2015-09-26 22:52:59 +03:00
83fed6dd34 Fix Travis config syntax #255 2015-09-26 22:14:36 +03:00
793b1cc172 Travis configuration changes #255 2015-09-26 22:08:23 +03:00
2ad361f2c3 Travis configuration changes #255 2015-09-26 21:23:00 +03:00
e077aa34d7 Travis deploy web site to S3 2015-09-26 20:19:39 +03:00
c1fda3ad6c Merge pull request #254 from zafarella/refactor-singleton-according-to-checkstyle
Refactor singleton according to checkstyle rules
2015-09-24 22:32:11 +03:00
60f9b71278 eliminate all warnings of checkstyle. 2015-09-24 13:29:39 -04:00
ba6511fe5d Work on #226, moved POSA reference and some J2EE design pattern references 2015-09-24 12:23:02 +05:30
36809537d9 checkstyle fixes - docs, indent etc 2015-09-24 01:01:51 -04:00
6735c81b52 Merge pull request #253 from mgalushka/master
#247 adding getAttackPower method to pattern decorator
2015-09-23 21:10:35 +03:00
ec9af416ba #247 adding getAttackPower method to pattern decorator 2015-09-23 17:58:58 +01:00
b3e4e8a47b Work on #226, #213, added references of Gang Of Four to all GoF patterns, added tag for Gang Of Four, added difficulty tags to some of them 2015-09-22 18:25:56 +05:30
d0af12b1ee Removed magic number. 2015-09-16 21:32:52 +01:00
51dca28fe7 Updated unit .equals() and .hashCode() methods
Formatted code using this formatter:
https://github.com/google/styleguide/blob/gh-pages/eclipse-java-google-style.xml
Removed argument final declaration on interface
Updated addCustomer logic for cases where the added customer already
exists
2015-09-14 21:22:33 +01:00
9c43827004 Removed erroneous semi-colon. 2015-09-05 22:00:27 +01:00
3da48d970c Inclusion of log4j dependency rather than relying on
System.out.println() statements.
Added unit tests for doa module.
2015-09-05 21:54:14 +01:00
619 changed files with 12907 additions and 9710 deletions

View File

@ -1,20 +1,18 @@
language: java
jdk:
- oraclejdk8
- oraclejdk8
env:
global:
- GH_REF: github.com/iluwatar/java-design-patterns.git
- secure: "LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg="
before_install:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
- secure: LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg=
after_success:
- mvn clean test jacoco:report coveralls:report
- bash update-ghpages.sh
before_install:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
after_success:
- mvn clean test jacoco:report coveralls:report
- bash update-ghpages.sh
# Migration to container-based infrastructure
sudo: false

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

View File

@ -23,10 +23,16 @@ are familiar with the patterns.
# Getting started
Before you dive into the material, you should be familiar with various
[Programming/Software Design Principles](http://webpro.github.io/programming-principles/).
Before you dive into the material, you should be familiar with various
[Programming/Software Design Principles](http://webpro.github.io/programming-principles/).
Once you are familiar with these concepts you can start drilling down into patterns by any of the following approaches
All designs should be as simple as possible. You should start with KISS, YAGNI,
and Do The Simplest Thing That Could Possibly Work principles. Complexity and
patterns should only be introduced when they are needed for practical
extensibility.
Once you are familiar with these concepts you can start drilling down into
patterns by any of the following approaches
- Using difficulty tags, `Difficulty-Beginner`, `Difficulty-Intermediate` & `Difficulty-Expert`.
- Using pattern categories, `Creational`, `Behavioral` and others.
@ -38,7 +44,6 @@ If you are willing to contribute to the project you will find the relevant infor
# Credits
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683)
* [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/)
* [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf)
@ -48,7 +53,6 @@ If you are willing to contribute to the project you will find the relevant infor
* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)
* [Spring Data](http://www.amazon.com/Spring-Data-Mark-Pollack/dp/1449323952/ref=sr_1_1)
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697)
# License

View File

@ -4,9 +4,13 @@ title: Abstract Factory
folder: abstract-factory
permalink: /patterns/abstract-factory/
categories: Creational
tags: Java
tags:
- Java
- Gang Of Four
---
**Also known as:** Kit
**Intent:** Provide an interface for creating families of related or dependent
objects without specifying their concrete classes.
@ -22,3 +26,7 @@ objects without specifying their concrete classes.
**Real world examples:**
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,19 +1,18 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
</parent>
<artifactId>abstract-factory</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.8.0</version>
</parent>
<artifactId>abstract-factory</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -3,71 +3,108 @@ package com.iluwatar.abstractfactory;
/**
*
* The essence of the Abstract Factory pattern is a factory interface
* ({@link KingdomFactory}) and its implementations ({@link ElfKingdomFactory},
* {@link OrcKingdomFactory}).
* 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 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
* @param factory
*/
public void createKingdom(final KingdomFactory factory) {
setKing(factory.createKing());
setCastle(factory.createCastle());
setArmy(factory.createArmy());
}
/**
* 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;
}
/**
* 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());
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;
}
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());
}
}

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());
}
}

View File

@ -4,9 +4,13 @@ title: Adapter
folder: adapter
permalink: /patterns/adapter/
categories: Structural
tags: Java
tags:
- Java
- Gang Of Four
---
**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.
@ -22,3 +26,7 @@ incompatible interfaces.
**Real world examples:**
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,19 +1,23 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
</parent>
<artifactId>adapter</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.8.0</version>
</parent>
<artifactId>adapter</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<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,25 +1,28 @@
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.
*
* 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}).
* <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.
*
* <p>The Adapter ({@link GnomeEngineer}) converts the interface of the target class (
* {@link GoblinGlider}) into a suitable one expected by the client ({@link GnomeEngineeringManager}
* ).
*/
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) {
Engineer manager = new GnomeEngineeringManager(new GnomeEngineer());
manager.operateDevice();
}
}

View File

@ -7,6 +7,5 @@ package com.iluwatar.adapter;
*/
public interface Engineer {
void operateDevice();
void operateDevice();
}

View File

@ -2,23 +2,22 @@ package com.iluwatar.adapter;
/**
*
* Adapter class. Adapts the interface of the device ({@link GoblinGlider}) into
* {@link Engineer} interface expected by the client ({@link GnomeEngineeringManager}).
* 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;
private GoblinGlider glider;
public GnomeEngineer() {
glider = new GoblinGlider();
}
@Override
public void operateDevice() {
glider.attachGlider();
glider.gainSpeed();
glider.takeOff();
}
public GnomeEngineer() {
glider = new GoblinGlider();
}
@Override
public void operateDevice() {
glider.attachGlider();
glider.gainSpeed();
glider.takeOff();
}
}

View File

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

View File

@ -7,15 +7,15 @@ package com.iluwatar.adapter;
*/
public class GoblinGlider {
public void attachGlider() {
System.out.println("Glider attached.");
}
public void attachGlider() {
System.out.println("Glider attached.");
}
public void gainSpeed() {
System.out.println("Gaining speed.");
}
public void gainSpeed() {
System.out.println("Gaining speed.");
}
public void takeOff() {
System.out.println("Lift-off!");
}
public void takeOff() {
System.out.println("Lift-off!");
}
}

View File

@ -0,0 +1,68 @@
package com.iluwatar.adapter;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
/**
* 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.
*
* <p>The Adapter ({@link GnomeEngineer}) converts the interface
* of the target class ({@link GoblinGlider}) into a suitable one expected by
* the client ({@link GnomeEngineeringManager}
* ).
*/
public class AdapterPatternTest {
private Map<String, Object> beans;
private static final String ENGINEER_BEAN = "engineer";
private static final String MANAGER_BEAN = "manager";
/**
* This method runs before the test execution and sets the bean objects in the beans Map.
*/
@Before
public void setup() {
beans = new HashMap<>();
GnomeEngineer gnomeEngineer = spy(new GnomeEngineer());
beans.put(ENGINEER_BEAN, gnomeEngineer);
GnomeEngineeringManager manager = new GnomeEngineeringManager();
manager.setEngineer((GnomeEngineer) beans.get(ENGINEER_BEAN));
beans.put(MANAGER_BEAN, manager);
}
/**
* This test asserts that when we call operateDevice() method on a manager bean, it is internally
* calling operateDevice method on the engineer object. The Adapter ({@link GnomeEngineer})
* converts the interface of the target class ( {@link GoblinGlider}) into a suitable one expected
* by the client ({@link GnomeEngineeringManager} ).
*/
@Test
public void testAdapter() {
Engineer manager = (Engineer) beans.get(MANAGER_BEAN);
// when manager is asked to operate device
manager.operateDevice();
// Manager internally calls the engineer object to operateDevice
Engineer engineer = (Engineer) beans.get(ENGINEER_BEAN);
verify(engineer).operateDevice();
}
}

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

@ -1,19 +1,18 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
</parent>
<artifactId>async-method-invocation</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.8.0</version>
</parent>
<artifactId>async-method-invocation</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -4,23 +4,24 @@ 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>AsyncResult</code> which is an intermediate container for an asynchronously evaluated
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
* <p>
* The main method shows example flow of async invocations. The main thread starts multiple tasks with
* variable durations and then continues its own work. When the main thread has done it's 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 +33,68 @@ 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();
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

@ -10,26 +10,26 @@ import java.util.concurrent.ExecutionException;
*/
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

@ -4,9 +4,13 @@ title: Bridge
folder: bridge
permalink: /patterns/bridge/
categories: Structural
tags: Java
tags:
- Java
- Gang Of Four
---
**Also known as:** Handle/Body
**Intent:** Decouple an abstraction from its implementation so that the two can
vary independently.
@ -20,3 +24,7 @@ vary independently.
* changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled.
* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies
* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,19 +1,18 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
</parent>
<artifactId>bridge</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.8.0</version>
</parent>
<artifactId>bridge</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -2,38 +2,38 @@ package com.iluwatar.bridge;
/**
*
* 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.
* 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.
*
*/
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

@ -11,9 +11,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

@ -4,7 +4,9 @@ title: Builder
folder: builder
permalink: /patterns/builder/
categories: Creational
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Separate the construction of a complex object from its
@ -22,3 +24,7 @@ representations.
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,19 +1,18 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
</parent>
<artifactId>builder</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.8.0</version>
</parent>
<artifactId>builder</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,42 +1,55 @@
package com.iluwatar.builder;
import com.iluwatar. builder.Hero.HeroBuilder;
import com.iluwatar.builder.Hero.HeroBuilder;
/**
*
* This is the Builder pattern variation as described by Joshua Bloch in
* Effective Java 2nd Edition.
* 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>
* 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.
* 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.
* <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.
*
*/
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,122 @@ 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;
}
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,7 +2,7 @@ package com.iluwatar.builder;
import org.junit.Test;
import com.iluwatar. builder.App;
import com.iluwatar.builder.App;
/**
*
@ -11,9 +11,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

@ -6,7 +6,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
<version>1.8.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();
}
}

1
caching/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target/

BIN
caching/etc/caching.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

106
caching/etc/caching.ucls Normal file
View File

@ -0,0 +1,106 @@
<?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="main.java.com.wssia.caching.App" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/App.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="249" y="150"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="main.java.com.wssia.caching.AppManager" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/AppManager.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="502" y="163"/>
<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="main.java.com.wssia.caching.CacheStore" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/CacheStore.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="537" y="436"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<enumeration id="4" language="java" name="main.java.com.wssia.caching.CachingPolicy" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/CachingPolicy.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="789" y="162"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</enumeration>
<class id="5" language="java" name="main.java.com.wssia.caching.DBManager" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/DBManager.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1137" y="134"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="6" language="java" name="main.java.com.wssia.caching.LRUCache" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/LRUCache.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="884" y="435"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="7" language="java" name="main.java.com.wssia.caching.UserAccount" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/UserAccount.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1140" y="405"/>
<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="8" language="java" name="test.java.com.wssia.caching.AppTest" project="CachingPatterns"
file="/CachingPatterns/src/test/java/com/wssia/caching/AppTest.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="251" y="374"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<association id="9">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="10" name="cachingPolicy"/>
<multiplicity id="11" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="12">
<end type="SOURCE" refId="8" navigable="false">
<attribute id="13" name="app"/>
<multiplicity id="14" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="15">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="16" name="cache"/>
<multiplicity id="17" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="6" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

24
caching/index.md Normal file
View File

@ -0,0 +1,24 @@
---
layout: pattern
title: Caching
folder: caching
permalink: /patterns/caching/
categories: Other
tags:
- Java
---
**Intent:** To avoid expensive re-acquisition of resources by not releasing
the resources immediately after their use. The resources retain their identity, are kept in some
fast-access storage, and are re-used to avoid having to acquire them again.
![alt text](./etc/caching.png "Caching")
**Applicability:** Use the Caching pattern(s) when
* Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead.
**Credits**
* [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained)
* [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177)

51
caching/pom.xml Normal file
View File

@ -0,0 +1,51 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.8.0</version>
</parent>
<artifactId>caching</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-core</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>bson</artifactId>
<version>3.0.4</version>
</dependency>
</dependencies>
<!--
Due to the use of MongoDB in the test of this pattern, TRAVIS and/or MAVEN might fail if the DB connection is
not open for the JUnit test. To avoid disrupting the compilation process, the surefire plug-in was used
to SKIP the running of the JUnit tests for this pattern. To ACTIVATE the running of the tests, change the
skipTests (below) flag to 'false' and vice-versa.
-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<skipTests>false</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,117 @@
package com.iluwatar.caching;
/**
*
* The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing
* the resources immediately after their use. The resources retain their identity, are kept in some
* fast-access storage, and are re-used to avoid having to acquire them again. There are three main
* caching strategies/techniques in this pattern; each with their own pros and cons. They are:
* <code>write-through</code> which writes data to the cache and DB in a single transaction,
* <code>write-around</code> which writes data immediately into the DB instead of the cache, and
* <code>write-behind</code> which writes data into the cache initially whilst the data is only
* written into the DB when the cache is full. The <code>read-through</code> strategy is also
* included in the mentioned three strategies -- returns data from the cache to the caller <b>if</b>
* it exists <b>else</b> queries from DB and stores it into the cache for future use. These
* strategies determine when the data in the cache should be written back to the backing store (i.e.
* Database) and help keep both data sources synchronized/up-to-date. This pattern can improve
* performance and also helps to maintain consistency between data held in the cache and the data in
* the underlying data store.
* <p>
* In this example, the user account ({@link UserAccount}) entity is used as the underlying
* 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 (
* {@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
* (depending on the preferred caching policy/strategy).
*
* <i>App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager</i>
* </p>
*
* @see CacheStore
* @See LRUCache
* @see CachingPolicy
*
*/
public class App {
/**
* Program entry point
*
* @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
// 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).
AppManager.initCacheCapacity(3);
App app = new App();
app.useReadAndWriteThroughStrategy();
app.useReadThroughAndWriteAroundStrategy();
app.useReadThroughAndWriteBehindStrategy();
}
/**
* Read-through and write-through
*/
public void useReadAndWriteThroughStrategy() {
System.out.println("# CachingPolicy.THROUGH");
AppManager.initCachingPolicy(CachingPolicy.THROUGH);
UserAccount userAccount1 = new UserAccount("001", "John", "He is a boy.");
AppManager.save(userAccount1);
System.out.println(AppManager.printCacheContent());
userAccount1 = AppManager.find("001");
userAccount1 = AppManager.find("001");
}
/**
* Read-through and write-around
*/
public void useReadThroughAndWriteAroundStrategy() {
System.out.println("# CachingPolicy.AROUND");
AppManager.initCachingPolicy(CachingPolicy.AROUND);
UserAccount userAccount2 = new UserAccount("002", "Jane", "She is a girl.");
AppManager.save(userAccount2);
System.out.println(AppManager.printCacheContent());
userAccount2 = 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");
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
}
/**
* Read-through and write-behind
*/
public void useReadThroughAndWriteBehindStrategy() {
System.out.println("# CachingPolicy.BEHIND");
AppManager.initCachingPolicy(CachingPolicy.BEHIND);
UserAccount userAccount3 = new UserAccount("003", "Adam", "He likes food.");
UserAccount userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
UserAccount userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
AppManager.save(userAccount3);
AppManager.save(userAccount4);
AppManager.save(userAccount5);
System.out.println(AppManager.printCacheContent());
userAccount3 = 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");
System.out.println(AppManager.printCacheContent());
}
}

View File

@ -0,0 +1,75 @@
package com.iluwatar.caching;
import java.text.ParseException;
/**
*
* AppManager helps to bridge the gap in communication between the main class and the application's
* back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
* also initialized here. Before the cache can be used, the size of the cache has to be set.
* Depending on the chosen caching policy, AppManager will call the appropriate function in the
* CacheStore class.
*
*/
public class AppManager {
private static CachingPolicy cachingPolicy;
/**
*
* 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) {
try {
DBManager.connect();
} catch (ParseException e) {
e.printStackTrace();
}
} else {
DBManager.createVirtualDB();
}
}
public static void initCachingPolicy(CachingPolicy policy) {
cachingPolicy = policy;
if (cachingPolicy == CachingPolicy.BEHIND) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
CacheStore.flushCache();
}
}));
}
CacheStore.clearCache();
}
public static void initCacheCapacity(int capacity) {
CacheStore.initCapacity(capacity);
}
public static UserAccount find(String userID) {
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
return CacheStore.readThrough(userID);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
return CacheStore.readThroughWithWriteBackPolicy(userID);
}
return null;
}
public static void save(UserAccount userAccount) {
if (cachingPolicy == CachingPolicy.THROUGH) {
CacheStore.writeThrough(userAccount);
} else if (cachingPolicy == CachingPolicy.AROUND) {
CacheStore.writeAround(userAccount);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
CacheStore.writeBehind(userAccount);
}
}
public static String printCacheContent() {
return CacheStore.print();
}
}

View File

@ -0,0 +1,104 @@
package com.iluwatar.caching;
import java.util.ArrayList;
/**
*
* The caching strategies are implemented in this class.
*
*/
public class CacheStore {
static LRUCache cache = null;
public static void initCapacity(int capacity) {
if (null == cache)
cache = new LRUCache(capacity);
else
cache.setCapacity(capacity);
}
public static UserAccount readThrough(String userID) {
if (cache.contains(userID)) {
System.out.println("# Cache Hit!");
return cache.get(userID);
}
System.out.println("# Cache Miss!");
UserAccount userAccount = DBManager.readFromDB(userID);
cache.set(userID, userAccount);
return userAccount;
}
public static void writeThrough(UserAccount userAccount) {
if (cache.contains(userAccount.getUserID())) {
DBManager.updateDB(userAccount);
} else {
DBManager.writeToDB(userAccount);
}
cache.set(userAccount.getUserID(), userAccount);
}
public static void writeAround(UserAccount userAccount) {
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);
}
}
public static UserAccount readThroughWithWriteBackPolicy(String userID) {
if (cache.contains(userID)) {
System.out.println("# Cache Hit!");
return cache.get(userID);
}
System.out.println("# Cache Miss!");
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);
}
cache.set(userID, userAccount);
return userAccount;
}
public static void writeBehind(UserAccount userAccount) {
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);
}
cache.set(userAccount.getUserID(), userAccount);
}
public static void clearCache() {
if (null != cache)
cache.clear();
}
/**
* Writes remaining content in the cache into the DB.
*/
public static void flushCache() {
System.out.println("# flushCache...");
if (null == cache)
return;
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
for (UserAccount userAccount : listOfUserAccounts) {
DBManager.upsertDB(userAccount);
}
}
public static String print() {
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
StringBuilder sb = new StringBuilder();
sb.append("\n--CACHE CONTENT--\n");
for (UserAccount userAccount : listOfUserAccounts) {
sb.append(userAccount.toString() + "\n");
}
sb.append("----\n");
return sb.toString();
}
}

View File

@ -0,0 +1,20 @@
package com.iluwatar.caching;
/**
*
* Enum class containing the three caching strategies implemented in the pattern.
*
*/
public enum CachingPolicy {
THROUGH("through"), AROUND("around"), BEHIND("behind");
private String policy;
private CachingPolicy(String policy) {
this.policy = policy;
}
public String getPolicy() {
return policy;
}
}

View File

@ -0,0 +1,123 @@
package com.iluwatar.caching;
import java.text.ParseException;
import java.util.HashMap;
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.UpdateOptions;
/**
*
* <p>DBManager handles the communication with the underlying data store i.e. Database. It contains the
* implemented methods for querying, inserting, and updating data. MongoDB was used as the database
* for the application.</p>
*
* <p>Developer/Tester is able to choose whether the application should use MongoDB as its underlying
* data storage (connect()) or a simple Java data structure to (temporarily) store the data/objects
* during runtime (createVirtualDB()).</p>
*
*/
public class DBManager {
private static MongoClient mongoClient;
private static MongoDatabase db;
private static boolean useMongoDB;
private static HashMap<String, UserAccount> virtualDB;
public static void createVirtualDB() {
useMongoDB = false;
virtualDB = new HashMap<String, UserAccount>();
}
public static void connect() throws ParseException {
useMongoDB = true;
mongoClient = new MongoClient();
db = mongoClient.getDatabase("test");
}
public static UserAccount readFromDB(String userID) {
if (!useMongoDB) {
if (virtualDB.containsKey(userID))
return virtualDB.get(userID);
return null;
}
if (null == db) {
try {
connect();
} catch (ParseException e) {
e.printStackTrace();
}
}
FindIterable<Document> iterable =
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"));
return userAccount;
}
public static void writeToDB(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
return;
}
if (null == db) {
try {
connect();
} catch (ParseException e) {
e.printStackTrace();
}
}
db.getCollection("user_accounts").insertOne(
new Document("userID", userAccount.getUserID()).append("userName",
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo()));
}
public static void updateDB(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
return;
}
if (null == db) {
try {
connect();
} catch (ParseException e) {
e.printStackTrace();
}
}
db.getCollection("user_accounts").updateOne(
new Document("userID", userAccount.getUserID()),
new Document("$set", new Document("userName", userAccount.getUserName()).append(
"additionalInfo", userAccount.getAdditionalInfo())));
}
/**
*
* Insert data into DB if it does not exist. Else, update it.
*/
public static void upsertDB(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
return;
}
if (null == db) {
try {
connect();
} catch (ParseException e) {
e.printStackTrace();
}
}
db.getCollection("user_accounts").updateOne(
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

@ -0,0 +1,146 @@
package com.iluwatar.caching;
import java.util.ArrayList;
import java.util.HashMap;
/**
*
* Data structure/implementation of the application's cache. The data structure consists of a hash
* table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the
* LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated,
* the data is moved to the front of the list to depict itself as the most-recently-used data. The
* LRU data is always at the end of the list.
*
*/
public class LRUCache {
class Node {
String userID;
UserAccount userAccount;
Node previous;
Node next;
public Node(String userID, UserAccount userAccount) {
this.userID = userID;
this.userAccount = userAccount;
}
}
int capacity;
HashMap<String, Node> cache = new HashMap<String, Node>();
Node head = null;
Node end = null;
public LRUCache(int capacity) {
this.capacity = capacity;
}
public UserAccount get(String userID) {
if (cache.containsKey(userID)) {
Node node = cache.get(userID);
remove(node);
setHead(node);
return node.userAccount;
}
return null;
}
/**
*
* Remove node from linked list.
*/
public void remove(Node node) {
if (node.previous != null) {
node.previous.next = node.next;
} else {
head = node.next;
}
if (node.next != null) {
node.next.previous = node.previous;
} else {
end = node.previous;
}
}
/**
*
* Move node to the front of the list.
*/
public void setHead(Node node) {
node.next = head;
node.previous = null;
if (head != null)
head.previous = node;
head = node;
if (end == null)
end = head;
}
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);
if (cache.size() >= capacity) {
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);
}
}
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);
remove(toBeRemoved);
cache.remove(userID);
}
public boolean isFull() {
return cache.size() >= capacity;
}
public UserAccount getLRUData() {
return end.userAccount;
}
public void clear() {
head = null;
end = null;
cache.clear();
}
/**
*
* Returns cache data in list form.
*/
public ArrayList<UserAccount> getCacheDataInListForm() {
ArrayList<UserAccount> listOfCacheData = new ArrayList<UserAccount>();
Node temp = head;
while (temp != null) {
listOfCacheData.add(temp.userAccount);
temp = temp.next;
}
return listOfCacheData;
}
public void setCapacity(int newCapacity) {
if (capacity > newCapacity) {
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll
// just clear the cache.
} else {
this.capacity = newCapacity;
}
}
}

View File

@ -0,0 +1,47 @@
package com.iluwatar.caching;
/**
*
* Entity class (stored in cache and DB) used in the application.
*
*/
public class UserAccount {
private String userID;
private String userName;
private String additionalInfo;
public UserAccount(String userID, String userName, String additionalInfo) {
this.userID = userID;
this.userName = userName;
this.additionalInfo = additionalInfo;
}
public String getUserID() {
return userID;
}
public void setUserID(String userID) {
this.userID = userID;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getAdditionalInfo() {
return additionalInfo;
}
public void setAdditionalInfo(String additionalInfo) {
this.additionalInfo = additionalInfo;
}
@Override
public String toString() {
return userID + ", " + userName + ", " + additionalInfo;
}
}

View File

@ -0,0 +1,41 @@
package com.iluwatar.caching;
import org.junit.Before;
import org.junit.Test;
/**
*
* Application test
*
*/
public class AppTest {
App app;
/**
* Setup of application test includes: initializing DB connection and cache size/capacity.
*/
@Before
public void setUp() {
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).
AppManager.initCacheCapacity(3);
app = new App();
}
@Test
public void testReadAndWriteThroughStrategy() {
app.useReadAndWriteThroughStrategy();
}
@Test
public void testReadThroughAndWriteAroundStrategy() {
app.useReadThroughAndWriteAroundStrategy();
}
@Test
public void testReadThroughAndWriteBehindStrategy() {
app.useReadThroughAndWriteBehindStrategy();
}
}

View File

@ -1,19 +1,18 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
</parent>
<artifactId>callback</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.8.0</version>
</parent>
<artifactId>callback</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -2,20 +2,21 @@ 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);
}
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();
public 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.");
}
@Override
public void execute() {
System.out.println("Perform some important activity and after call the callback method.");
}
}

View File

@ -7,12 +7,12 @@ package com.iluwatar.callback;
*/
public abstract class Task {
public final void executeWith(Callback callback) {
execute();
if (callback != null) {
callback.call();
}
}
public final void executeWith(Callback callback) {
execute();
if (callback != null) {
callback.call();
}
}
public abstract void execute();
public abstract void execute();
}

View File

@ -2,18 +2,38 @@ package com.iluwatar.callback;
import org.junit.Test;
import com.iluwatar.callback.App;
import static org.junit.Assert.assertEquals;
/**
*
* Application test
* Add a field as a counter. Every time the callback method is called increment this field. Unit
* test checks that the field is being incremented.
*
* Could be done with mock objects as well where the call method call is verified.
*/
public class AppTest {
@Test
public void test() {
String[] args = {};
App.main(args);
}
private Integer callingCount = 0;
@Test
public void test() {
Callback callback = new Callback() {
@Override
public void call() {
callingCount++;
}
};
Task task = new SimpleTask();
assertEquals("Initial calling count of 0", new Integer(0), callingCount);
task.executeWith(callback);
assertEquals("Callback called once", new Integer(1), callingCount);
task.executeWith(callback);
assertEquals("Callback called twice", new Integer(2), callingCount);
}
}

View File

@ -4,7 +4,9 @@ title: Chain of responsibility
folder: chain
permalink: /patterns/chain/
categories: Behavioral
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Avoid coupling the sender of a request to its receiver by giving
@ -23,3 +25,7 @@ objects and pass the request along the chain until an object handles it.
* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29)
* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,19 +1,18 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
</parent>
<artifactId>chain</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.8.0</version>
</parent>
<artifactId>chain</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -2,25 +2,30 @@ package com.iluwatar.chain;
/**
*
* Chain of Responsibility organizes request handlers ({@link RequestHandler}) into a
* chain where each handler has a chance to act on the request on its turn. In
* this example the king ({@link OrcKing}) makes requests and the military orcs
* ({@link OrcCommander}, {@link OrcOfficer}, {@link OrcSoldier}) form the handler chain.
* The Chain of Responsibility pattern is a design pattern consisting of command objects and a
* series of processing objects. Each processing object contains logic that defines the types of
* command objects that it can handle; the rest are passed to the next processing object in the
* chain. A mechanism also exists for adding new processing objects to the end of this chain.
* <p>
* In this example we organize the request handlers ({@link RequestHandler}) into a chain where each
* handler has a chance to act on the request on its turn. Here the king ({@link OrcKing}) makes
* requests and the military orcs ({@link OrcCommander}, {@link OrcOfficer}, {@link OrcSoldier})
* form the handler chain.
*
*/
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) {
OrcKing king = new OrcKing();
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle"));
king.makeRequest(new Request(RequestType.TORTURE_PRISONER,
"torture prisoner"));
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax"));
OrcKing king = new OrcKing();
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle"));
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner"));
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax"));
}
}
}

View File

@ -7,21 +7,21 @@ package com.iluwatar.chain;
*/
public class OrcCommander extends RequestHandler {
public OrcCommander(RequestHandler handler) {
super(handler);
}
public OrcCommander(RequestHandler handler) {
super(handler);
}
@Override
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) {
printHandling(req);
} else {
super.handleRequest(req);
}
}
@Override
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) {
printHandling(req);
} else {
super.handleRequest(req);
}
}
@Override
public String toString() {
return "Orc commander";
}
@Override
public String toString() {
return "Orc commander";
}
}

View File

@ -7,18 +7,18 @@ package com.iluwatar.chain;
*/
public class OrcKing {
RequestHandler chain;
RequestHandler chain;
public OrcKing() {
buildChain();
}
public OrcKing() {
buildChain();
}
private void buildChain() {
chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null)));
}
private void buildChain() {
chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null)));
}
public void makeRequest(Request req) {
chain.handleRequest(req);
}
public void makeRequest(Request req) {
chain.handleRequest(req);
}
}

View File

@ -7,22 +7,22 @@ package com.iluwatar.chain;
*/
public class OrcOfficer extends RequestHandler {
public OrcOfficer(RequestHandler handler) {
super(handler);
}
public OrcOfficer(RequestHandler handler) {
super(handler);
}
@Override
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.TORTURE_PRISONER)) {
printHandling(req);
} else {
super.handleRequest(req);
}
}
@Override
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.TORTURE_PRISONER)) {
printHandling(req);
} else {
super.handleRequest(req);
}
}
@Override
public String toString() {
return "Orc officer";
}
@Override
public String toString() {
return "Orc officer";
}
}

View File

@ -7,21 +7,21 @@ package com.iluwatar.chain;
*/
public class OrcSoldier extends RequestHandler {
public OrcSoldier(RequestHandler handler) {
super(handler);
}
public OrcSoldier(RequestHandler handler) {
super(handler);
}
@Override
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.COLLECT_TAX)) {
printHandling(req);
} else {
super.handleRequest(req);
}
}
@Override
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.COLLECT_TAX)) {
printHandling(req);
} else {
super.handleRequest(req);
}
}
@Override
public String toString() {
return "Orc soldier";
}
@Override
public String toString() {
return "Orc soldier";
}
}

View File

@ -7,32 +7,32 @@ package com.iluwatar.chain;
*/
public class Request {
private String requestDescription;
private RequestType requestType;
private String requestDescription;
private RequestType requestType;
public Request(RequestType requestType, String requestDescription) {
this.setRequestType(requestType);
this.setRequestDescription(requestDescription);
}
public Request(RequestType requestType, String requestDescription) {
this.setRequestType(requestType);
this.setRequestDescription(requestDescription);
}
public String getRequestDescription() {
return requestDescription;
}
public String getRequestDescription() {
return requestDescription;
}
public void setRequestDescription(String requestDescription) {
this.requestDescription = requestDescription;
}
public void setRequestDescription(String requestDescription) {
this.requestDescription = requestDescription;
}
public RequestType getRequestType() {
return requestType;
}
public RequestType getRequestType() {
return requestType;
}
public void setRequestType(RequestType requestType) {
this.requestType = requestType;
}
public void setRequestType(RequestType requestType) {
this.requestType = requestType;
}
@Override
public String toString() {
return getRequestDescription();
}
@Override
public String toString() {
return getRequestDescription();
}
}

View File

@ -7,22 +7,22 @@ package com.iluwatar.chain;
*/
public abstract class RequestHandler {
private RequestHandler next;
private RequestHandler next;
public RequestHandler(RequestHandler next) {
this.next = next;
}
public RequestHandler(RequestHandler next) {
this.next = next;
}
public void handleRequest(Request req) {
if (next != null) {
next.handleRequest(req);
}
}
public void handleRequest(Request req) {
if (next != null) {
next.handleRequest(req);
}
}
protected void printHandling(Request req) {
System.out.println(this + " handling request \"" + req + "\"");
}
protected void printHandling(Request req) {
System.out.println(this + " handling request \"" + req + "\"");
}
@Override
public abstract String toString();
@Override
public abstract String toString();
}

View File

@ -7,6 +7,6 @@ package com.iluwatar.chain;
*/
public enum RequestType {
DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
}

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