Compare commits

...

87 Commits
urm ... 1.7.0

Author SHA1 Message Date
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
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
192 changed files with 2782 additions and 697 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

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,7 +4,9 @@ title: Abstract Factory
folder: abstract-factory
permalink: /patterns/abstract-factory/
categories: Creational
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Provide an interface for creating families of related or dependent
@ -22,3 +24,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

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

View File

@ -3,12 +3,21 @@ package com.iluwatar.abstractfactory;
/**
*
* The Abstract Factory pattern provides a way to encapsulate a group of individual
* factories that have a common theme without specifying their concrete classes. In
* normal usage, the client software creates a concrete implementation of the abstract
* factory and then uses the generic interface of the factory to create the concrete
* objects that are part of the theme. The client does not know (or care) which
* concrete objects it gets from each of these internal factories, since it uses only
* the generic interfaces of their products. This pattern separates the details of
* implementation of a set of objects from their general usage and relies on object
* composition, as object creation is implemented in methods exposed in the factory
* interface.
* <p>
* The essence of the Abstract Factory pattern is a factory interface
* ({@link KingdomFactory}) and its implementations ({@link ElfKingdomFactory},
* {@link OrcKingdomFactory}).
* <p>
* The example uses both concrete implementations to create a king, a castle and
* an army.
* {@link OrcKingdomFactory}). The example uses both concrete implementations to
* create a king, a castle and an army.
*
*/
public class App {

View File

@ -7,7 +7,7 @@ import org.junit.Test;
public class AppTest {
private App app = new App();;
private App app = new App();
private KingdomFactory elfFactory;
private KingdomFactory orcFactory;

View File

@ -4,7 +4,9 @@ title: Adapter
folder: adapter
permalink: /patterns/adapter/
categories: Structural
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Convert the interface of a class into another interface the clients
@ -22,3 +24,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

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

View File

@ -1,7 +1,13 @@
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.
* <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

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
<version>1.7.0</version>
</parent>
<artifactId>async-method-invocation</artifactId>
<dependencies>

View File

@ -4,7 +4,9 @@ title: Bridge
folder: bridge
permalink: /patterns/bridge/
categories: Structural
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Decouple an abstraction from its implementation so that the two can
@ -20,3 +22,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

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

View File

@ -2,6 +2,9 @@ package com.iluwatar.bridge;
/**
*
* The Bridge pattern can also be thought of as two layers of abstraction. With Bridge,
* you can decouple an abstraction from its implementation so that the two can vary independently.
* <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.

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

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

View File

@ -4,7 +4,20 @@ import com.iluwatar. builder.Hero.HeroBuilder;
/**
*
* This is the Builder pattern variation as described by Joshua Bloch in
* The intention of the Builder pattern is to find a solution to the telescoping
* constructor anti-pattern. The telescoping constructor anti-pattern occurs when the
* increase of object constructor parameter combination leads to an exponential list
* of constructors. Instead of using numerous constructors, the builder pattern uses
* another object, a builder, that receives each initialization parameter step by step
* and then returns the resulting constructed object at once.
* <p>
* The Builder pattern has another benefit. It can be used for objects that contain
* flat data (html code, SQL query, X.509 certificate...), that is to say, data that
* can't be easily edited. This type of data cannot be edited step by step and must
* be edited at once. The best way to construct such an object is to use a builder
* class.
* <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

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
<version>1.7.0</version>
</parent>
<artifactId>business-delegate</artifactId>
<dependencies>

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.7.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

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

View File

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

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 {
private Integer callingCount = 0;
@Test
public void test() {
String[] args = {};
App.main(args);
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

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

View File

@ -2,10 +2,16 @@ 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 {

View File

@ -4,7 +4,9 @@ title: Command
folder: command
permalink: /patterns/command/
categories: Behavioral
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Encapsulate a request as an object, thereby letting you
@ -30,3 +32,7 @@ support undoable operations.
**Real world examples:**
* [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

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

View File

@ -2,7 +2,9 @@ package com.iluwatar.command;
/**
*
* In Command pattern actions are objects that can be executed and undone.
* The Command pattern is a behavioral design pattern in which an object is used to encapsulate all information
* needed to perform an action or trigger an event at a later time. This information includes the method name,
* the object that owns the method and values for the method parameters.
* <p>
* Four terms always associated with the command pattern are command, receiver, invoker and client. A command
* object (spell) knows about the receiver (target) and invokes a method of the receiver. Values for parameters of

View File

@ -4,7 +4,9 @@ title: Composite
folder: composite
permalink: /patterns/composite/
categories: Structural
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Compose objects into tree structures to represent part-whole
@ -22,3 +24,7 @@ of objects uniformly.
* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html)
* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

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

View File

@ -1,10 +1,14 @@
package com.iluwatar.composite;
/**
*
* With Composite we can treat tree hierarchies of objects with uniform
* interface ({@link LetterComposite}). In this example we have sentences composed of
* words composed of letters.
* The Composite pattern is a partitioning design pattern. The Composite pattern
* describes that a group of objects is to be treated in the same way as a single
* instance of an object. The intent of a composite is to "compose" objects into
* tree structures to represent part-whole hierarchies. Implementing the Composite
* pattern lets clients treat individual objects and compositions uniformly.
* <p>
* In this example we have sentences composed of words composed of letters. All of
* the objects can be treated through the same interface ({@link LetterComposite}).
*
*/
public class App {

View File

@ -4,7 +4,9 @@ title: Data Access Object
folder: dao
permalink: /patterns/dao/
categories: Architectural
tags: Java
tags:
- Java
- Difficulty-Beginner
---
**Intent:** Object provides an abstract interface to some type of database or
@ -16,3 +18,7 @@ other persistence mechanism.
* when you want to consolidate how the data layer is accessed
* when you want to avoid writing multiple data retrieval/persistence layers
**Credits:**
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)

View File

@ -1,18 +1,24 @@
<?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>dao</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.7.0</version>
</parent>
<artifactId>dao</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -3,51 +3,54 @@ package com.iluwatar.dao;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
/**
*
* Data Access Object (DAO) is an object that provides an abstract interface to some type of database or other
* persistence mechanism. By mapping application calls to the persistence layer, DAO provide some specific data
* operations without exposing details of the database. This isolation supports the Single responsibility principle.
* It separates what data accesses the application needs, in terms of domain-specific objects and data types
* (the public interface of the DAO), from how these needs can be satisfied with a specific DBMS.
* <p>
* With the DAO pattern, we can use various method calls to retrieve/add/delete/update data without directly
* interacting with the data. The below example demonstrates basic operations(CRUD): select, add, update, and delete.
* interacting with the data. The below example demonstrates basic CRUD operations: select, add, update, and delete.
*
*/
public class App {
private static Logger LOGGER = Logger.getLogger(App.class);
/**
* Program entry point
* @param args command line args
* Program entry point.
*
* @param args command line args.
*/
public static void main(String[] args) {
CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers());
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
System.out.println("customerDao.getCusterById(2): " + customerDao.getCusterById(2));
Customer customer = new Customer(4, "Dan", "Danson");
public static void main(final String[] args) {
final CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers());
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
LOGGER.info("customerDao.getCusterById(2): " + customerDao.getCustomerById(2));
final Customer customer = new Customer(4, "Dan", "Danson");
customerDao.addCustomer(customer);
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
customer.setFirstName("Daniel");
customer.setLastName("Danielson");
customerDao.updateCustomer(customer);
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
customerDao.deleteCustomer(customer);
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
}
/**
* Generate customers
* @return list of customers
* Generate customers.
*
* @return list of customers.
*/
public static List<Customer> generateSampleCustomers() {
Customer customer1 = new Customer(1, "Adam", "Adamson");
Customer customer2 = new Customer(2, "Bob", "Bobson");
Customer customer3 = new Customer(3, "Carl", "Carlson");
List<Customer> customers = new ArrayList<Customer>();
final Customer customer1 = new Customer(1, "Adam", "Adamson");
final Customer customer2 = new Customer(2, "Bob", "Bobson");
final Customer customer3 = new Customer(3, "Carl", "Carlson");
final List<Customer> customers = new ArrayList<Customer>();
customers.add(customer1);
customers.add(customer2);
customers.add(customer3);

View File

@ -6,65 +6,63 @@ package com.iluwatar.dao;
*
*/
public class Customer {
private int id;
private String firstName;
private String lastName;
public Customer(int id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
private int id;
private String firstName;
private String lastName;
public Customer(final int id, final String firstName, final String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
public int getId() {
return id;
}
public void setId(final int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(final String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(final String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "Customer{" + "id=" + getId() + ", firstName='" + getFirstName() + '\'' + ", lastName='"
+ getLastName() + '\'' + '}';
}
@Override
public boolean equals(final Object o) {
boolean isEqual = false;
if (this == o) {
isEqual = true;
} else if (o != null && (getClass() == o.getClass())) {
final Customer customer = (Customer) o;
if (getId() == customer.getId())
isEqual = true;
}
return isEqual;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
if (id != customer.id) return false;
return true;
}
@Override
public int hashCode() {
int result = id;
return result;
}
}
@Override
public int hashCode() {
int result = getId();
return result;
}
}

View File

@ -8,10 +8,14 @@ import java.util.List;
*
*/
public interface CustomerDao {
public List<Customer> getAllCustomers();
public Customer getCusterById(int id);
public void addCustomer(Customer customer);
public void updateCustomer(Customer customer);
public void deleteCustomer(Customer customer);
}
List<Customer> getAllCustomers();
Customer getCustomerById(int id);
void addCustomer(Customer customer);
void updateCustomer(Customer customer);
void deleteCustomer(Customer customer);
}

View File

@ -4,54 +4,59 @@ import java.util.List;
/**
*
* The data access object (DAO) is an object that provides an abstract interface to some type of database or other persistence mechanism.
* By mapping application calls to the persistence layer, DAO provide some specific data operations without exposing details of the database.
* This isolation supports the Single responsibility principle. It separates what data accesses the application needs, in terms of
* domain-specific objects and data types (the public interface of the DAO), from how these needs can be satisfied with a specific DBMS,
* database schema, etc.
* The data access object (DAO) is an object that provides an abstract interface to some type of
* database or other persistence mechanism. By mapping application calls to the persistence layer,
* DAO provide some specific data operations without exposing details of the database. This
* isolation supports the Single responsibility principle. It separates what data accesses the
* application needs, in terms of domain-specific objects and data types (the public interface of
* the DAO), from how these needs can be satisfied with a specific DBMS, database schema, etc.
*
*/
public class CustomerDaoImpl implements CustomerDao {
// Represents the DB structure for our example so we don't have to managed it ourselves
// Note: Normally this would be in the form of an actual database and not part of the Dao Impl.
private List<Customer> customers;
// Represents the DB structure for our example so we don't have to managed it ourselves
// Note: Normally this would be in the form of an actual database and not part of the Dao Impl.
private List<Customer> customers;
public CustomerDaoImpl(List<Customer> customers) {
this.customers = customers;
public CustomerDaoImpl(final List<Customer> customers) {
this.customers = customers;
}
@Override
public List<Customer> getAllCustomers() {
return customers;
}
@Override
public Customer getCustomerById(final int id) {
Customer customer = null;
for (final Customer cus : getAllCustomers()) {
if (cus.getId() == id) {
customer = cus;
break;
}
}
return customer;
}
@Override
public List<Customer> getAllCustomers() {
return customers;
@Override
public void addCustomer(final Customer customer) {
if (getCustomerById(customer.getId()) == null) {
customers.add(customer);
}
}
@Override
public Customer getCusterById(int id) {
for (int i = 0; i < customers.size(); i++) {
if (customers.get(i).getId() == id) {
return customers.get(i);
}
}
// No customer found
return null;
@Override
public void updateCustomer(final Customer customer) {
if (getAllCustomers().contains(customer)) {
final int index = getAllCustomers().indexOf(customer);
getAllCustomers().set(index, customer);
}
}
@Override
public void addCustomer(Customer customer) {
customers.add(customer);
}
@Override
public void updateCustomer(Customer customer) {
if (customers.contains(customer)) {
customers.set(customers.indexOf(customer), customer);
}
}
@Override
public void deleteCustomer(Customer customer) {
customers.remove(customer);
}
}
@Override
public void deleteCustomer(final Customer customer) {
getAllCustomers().remove(customer);
}
}

View File

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

View File

@ -0,0 +1,99 @@
package com.iluwatar.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
public class CustomerDaoImplTest {
private CustomerDaoImpl impl;
private List<Customer> customers;
private static final Customer CUSTOMER = new Customer(1, "Freddy", "Kruger");
@Before
public void setUp() {
customers = new ArrayList<Customer>();
customers.add(CUSTOMER);
impl = new CustomerDaoImpl(customers);
}
@Test
public void deleteExistingCustomer() {
assertEquals(1, impl.getAllCustomers().size());
impl.deleteCustomer(CUSTOMER);
assertTrue(impl.getAllCustomers().isEmpty());
}
@Test
public void deleteNonExistingCustomer() {
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
impl.deleteCustomer(nonExistingCustomer);
assertEquals(1, impl.getAllCustomers().size());
}
@Test
public void updateExistingCustomer() {
final String newFirstname = "Bernard";
final String newLastname = "Montgomery";
final Customer customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname);
impl.updateCustomer(customer);
final Customer cust = impl.getCustomerById(CUSTOMER.getId());
assertEquals(newFirstname, cust.getFirstName());
assertEquals(newLastname, cust.getLastName());
}
@Test
public void updateNonExistingCustomer() {
final int nonExistingId = getNonExistingCustomerId();
final String newFirstname = "Douglas";
final String newLastname = "MacArthur";
final Customer customer = new Customer(nonExistingId, newFirstname, newLastname);
impl.updateCustomer(customer);
assertNull(impl.getCustomerById(nonExistingId));
final Customer existingCustomer = impl.getCustomerById(CUSTOMER.getId());
assertEquals(CUSTOMER.getFirstName(), existingCustomer.getFirstName());
assertEquals(CUSTOMER.getLastName(), existingCustomer.getLastName());
}
@Test
public void addCustomer() {
final Customer newCustomer = new Customer(3, "George", "Patton");
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
}
@Test
public void addAlreadyAddedCustomer() {
final Customer newCustomer = new Customer(3, "George", "Patton");
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
}
@Test
public void getExistinCustomerById() {
assertEquals(CUSTOMER, impl.getCustomerById(CUSTOMER.getId()));
}
@Test
public void getNonExistinCustomerById() {
final int nonExistingId = getNonExistingCustomerId();
assertNull(impl.getCustomerById(nonExistingId));
}
/**
* An arbitrary number which does not correspond to an active Customer id.
*
* @return an int of a customer id which doesn't exist
*/
private int getNonExistingCustomerId() {
return 999;
}
}

View File

@ -0,0 +1,74 @@
package com.iluwatar.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import org.junit.Before;
import org.junit.Test;
public class CustomerTest {
private Customer customer;
private static final int ID = 1;
private static final String FIRSTNAME = "Winston";
private static final String LASTNAME = "Churchill";
@Before
public void setUp() {
customer = new Customer(ID, FIRSTNAME, LASTNAME);
}
@Test
public void getAndSetId() {
final int newId = 2;
customer.setId(newId);
assertEquals(newId, customer.getId());
}
@Test
public void getAndSetFirstname() {
final String newFirstname = "Bill";
customer.setFirstName(newFirstname);
assertEquals(newFirstname, customer.getFirstName());
}
@Test
public void getAndSetLastname() {
final String newLastname = "Clinton";
customer.setLastName(newLastname);
assertEquals(newLastname, customer.getLastName());
}
@Test
public void notEqualWithDifferentId() {
final int newId = 2;
final Customer otherCustomer = new Customer(newId, FIRSTNAME, LASTNAME);
assertNotEquals(customer, otherCustomer);
assertNotEquals(customer.hashCode(), otherCustomer.hashCode());
}
@Test
public void equalsWithSameObjectValues() {
final Customer otherCustomer = new Customer(ID, FIRSTNAME, LASTNAME);
assertEquals(customer, otherCustomer);
assertEquals(customer.hashCode(), otherCustomer.hashCode());
}
@Test
public void equalsWithSameObjects() {
assertEquals(customer, customer);
assertEquals(customer.hashCode(), customer.hashCode());
}
@Test
public void testToString() {
final StringBuffer buffer = new StringBuffer();
buffer.append("Customer{id=");
buffer.append("" + customer.getId());
buffer.append(", firstName='");
buffer.append(customer.getFirstName());
buffer.append("\', lastName='");
buffer.append(customer.getLastName() + "\'}");
assertEquals(buffer.toString(), customer.toString());
}
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true"
xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="console" />
</root>
</log4j:configuration>

View File

@ -4,7 +4,9 @@ title: Decorator
folder: decorator
permalink: /patterns/decorator/
categories: Structural
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Attach additional responsibilities to an object dynamically.
@ -18,3 +20,7 @@ functionality.
* to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects
* for responsibilities that can be withdrawn
* when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

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

View File

@ -2,12 +2,15 @@ package com.iluwatar.decorator;
/**
*
* Decorator pattern is a more flexible alternative to subclassing. The decorator
* The Decorator pattern is a more flexible alternative to subclassing. The Decorator
* class implements the same interface as the target and uses composition to
* "decorate" calls to the target.
* "decorate" calls to the target. Using the Decorator pattern it is possible to
* change the behavior of the class during runtime.
* <p>
* Using decorator pattern it is possible to change class behavior during
* runtime, as the example shows.
* In this example we show how the simple {@link Troll} first attacks and then
* flees the battle. Then we decorate the {@link Troll} with a {@link SmartTroll}
* and perform the attack again. You can see how the behavior changes after the
* decoration.
*
*/
public class App {
@ -23,11 +26,13 @@ public class App {
Hostile troll = new Troll();
troll.attack();
troll.fleeBattle();
System.out.printf("Simple troll power %d.\n", troll.getAttackPower());
// change the behavior of the simple troll by adding a decorator
System.out.println("\nA smart looking troll surprises you.");
Hostile smart = new SmartTroll(troll);
smart.attack();
smart.fleeBattle();
System.out.printf("Smart troll power %d.\n", smart.getAttackPower());
}
}

View File

@ -9,6 +9,8 @@ public interface Hostile {
void attack();
int getAttackPower();
void fleeBattle();
}

View File

@ -21,6 +21,12 @@ public class SmartTroll implements Hostile {
decorated.attack();
}
@Override
public int getAttackPower() {
// decorated troll power + 20 because it is smart
return decorated.getAttackPower() + 20;
}
@Override
public void fleeBattle() {
System.out.println("The troll calls for help!");

View File

@ -11,6 +11,11 @@ public class Troll implements Hostile {
System.out.println("The troll swings at you with a club!");
}
@Override
public int getAttackPower() {
return 10;
}
public void fleeBattle() {
System.out.println("The troll shrieks in horror and runs away!");
}

View File

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

View File

@ -3,7 +3,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
<version>1.7.0</version>
</parent>
<artifactId>double-checked-locking</artifactId>
<dependencies>

View File

@ -2,9 +2,15 @@ package com.iluwatar.doublechecked.locking;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
*
* Double Checked Locking is a concurrency design pattern used to reduce the overhead
* of acquiring a lock by first testing the locking criterion (the "lock hint") without
* actually acquiring the lock. Only if the locking criterion check indicates that
* locking is required does the actual locking logic proceed.
* <p>
* In {@link Inventory} we store the items with a given size. However, we do not store
* more items than the inventory size. To address concurrent access problems we
* use double checked locking to add item to inventory. In this method, the
@ -21,13 +27,17 @@ public class App {
final Inventory inventory = new Inventory(1000);
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
while (inventory.addItem(new Item()))
;
}
});
executorService.execute(() -> {
while (inventory.addItem(new Item()))
;
});
}
executorService.shutdown();
try {
executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println("Error waiting for ExecutorService shutdown");
}
}
}

View File

@ -12,13 +12,14 @@ import java.util.concurrent.locks.ReentrantLock;
*/
public class Inventory {
private int inventorySize;
private List<Item> items;
private Lock lock = new ReentrantLock();
private final int inventorySize;
private final List<Item> items;
private final Lock lock;
public Inventory(int inventorySize) {
this.inventorySize = inventorySize;
this.items = new ArrayList<Item>(inventorySize);
this.items = new ArrayList<>(inventorySize);
this.lock = new ReentrantLock();
}
public boolean addItem(Item item) {
@ -27,7 +28,9 @@ public class Inventory {
try {
if (items.size() < inventorySize) {
items.add(item);
System.out.println(Thread.currentThread());
System.out.println(Thread.currentThread()
+ ": items.size()=" + items.size()
+ ", inventorySize=" + inventorySize);
return true;
}
} finally {

View File

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

View File

@ -4,7 +4,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
<version>1.7.0</version>
</parent>
<artifactId>event-aggregator</artifactId>
<dependencies>

View File

@ -5,8 +5,12 @@ import java.util.List;
/**
*
* The Event Aggregator pattern channels events from multiple objects
* into a single object to simplify registration for clients.
* A system with lots of objects can lead to complexities when a client wants to subscribe
* to events. The client has to find and register for each object individually, if each
* object has multiple events then each event requires a separate subscription.
* <p>
* An Event Aggregator acts as a single source of events for many objects. It registers
* for all the events of the many objects allowing clients to register with just the aggregator.
* <p>
* In the example {@link LordBaelish}, {@link LordVarys} and {@link Scout} deliver events to
* {@link KingsHand}. {@link KingsHand}, the event aggregator, then delivers the events

View File

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

View File

@ -4,7 +4,9 @@ title: Facade
folder: facade
permalink: /patterns/facade/
categories: Structural
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Provide a unified interface to a set of interfaces in a subsystem.
@ -17,3 +19,7 @@ Facade defines a higher-level interface that makes the subsystem easier to use.
* you want to provide a simple interface to a complex subsystem. Subsystems often get more complex as they evolve. Most patterns, when applied, result in more and smaller classes. This makes the subsystem more reusable and easier to customize, but it also becomes harder to use for clients that don't need to customize it. A facade can provide a simple default view of the subsystem that is good enough for most clients. Only clients needing more customizability will need to look beyond the facade.
* there are many dependencies between clients and the implementation classes of an abstraction. Introduce a facade to decouple the subsystem from clients and other subsystems, thereby promoting subsystem independence and portability.
* you want to layer your subsystems. Use a facade to define an entry point to each subsystem level. If subsystems are dependent, the you can simplify the dependencies between them by making them communicate with each other solely through their facades
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

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

View File

@ -2,9 +2,15 @@ package com.iluwatar.facade;
/**
*
* Facade ({@link DwarvenGoldmineFacade}) provides simpler interface to subsystem.
* The Facade design pattern is often used when a system is very complex or difficult
* to understand because the system has a large number of interdependent classes or
* its source code is unavailable. This pattern hides the complexities of the larger
* system and provides a simpler interface to the client. It typically involves a single
* wrapper class which contains a set of members required by client. These members access
* the system on behalf of the facade client and hide the implementation details.
* <p>
* http://en.wikipedia.org/wiki/Facade_pattern
* In this example the Facade is ({@link DwarvenGoldmineFacade}) and it provides a simpler
* interface to the goldmine subsystem.
*
*/
public class App {

View File

@ -7,6 +7,7 @@ categories: Creational
tags:
- Java
- Difficulty-Beginner
- Gang Of Four
---
**Intent:** Define an interface for creating an object, but let subclasses
@ -20,3 +21,7 @@ instantiation to subclasses.
* a class can't anticipate the class of objects it must create
* a class wants its subclasses to specify the objects it creates
* classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

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

View File

@ -2,7 +2,14 @@ package com.iluwatar.factory.method;
/**
*
* In Factory Method we have an interface ({@link Blacksmith}) with a method for
* The Factory Method is a creational design pattern which uses factory methods to deal
* with the problem of creating objects without specifying the exact class of object
* that will be created. This is done by creating objects via calling a factory
* method either specified in an interface and implemented by child classes, or implemented
* in a base class and optionally overridden by derived classes—rather than by calling a
* constructor.
* <p>
* In this Factory Method example we have an interface ({@link Blacksmith}) with a method for
* creating objects ({@link Blacksmith#manufactureWeapon}). The concrete subclasses
* ({@link OrcBlacksmith}, {@link ElfBlacksmith}) then override the method to produce
* objects of their liking.

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.6.0</version>
<version>1.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -11,15 +11,14 @@ import java.util.function.Predicate;
import static java.lang.String.valueOf;
/**
* Fluent interface pattern is useful when you want to provide an easy readable, flowing API. Those
* interfaces tend to mimic domain specific languages, so they can nearly be read as human
* languages.
* The Fluent Interface pattern is useful when you want to provide an easy readable, flowing API. Those
* interfaces tend to mimic domain specific languages, so they can nearly be read as human languages.
* <p>
* In this example two implementations of a {@link FluentIterable} interface are given. The
* SimpleFluentIterable evaluates eagerly and would be too costly for real world applications. The
* LazyFluentIterable is evaluated on termination. Their usage is demonstrated with a simple number
* list that is filtered, transformed and collected. The result is printed afterwards.
* <p>
* {@link SimpleFluentIterable} evaluates eagerly and would be too costly for real world applications.
* The {@link LazyFluentIterable} is evaluated on termination. Their usage is demonstrated with a
* simple number list that is filtered, transformed and collected. The result is printed afterwards.
*
*/
public class App {

View File

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

View File

@ -4,7 +4,9 @@ title: Flyweight
folder: flyweight
permalink: /patterns/flyweight/
categories: Structural
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Use sharing to support large numbers of fine-grained objects
@ -25,3 +27,7 @@ true
**Real world examples:**
* [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

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

View File

@ -22,3 +22,9 @@ internationalization, routing and logging in a single place.
**Real world examples:**
* [Apache Struts](https://struts.apache.org/)
**Credits:**
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
* [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns)
* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
<version>1.7.0</version>
</parent>
<artifactId>front-controller</artifactId>
<dependencies>

View File

@ -30,3 +30,4 @@ degrading execution efficiency.
**Credits:**
* [Douglas C. Schmidt and Charles D. Cranor - Half Sync/Half Async](http://www.cs.wustl.edu/~schmidt/PDF/PLoP-95.pdf)
* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
<version>1.7.0</version>
</parent>
<artifactId>half-sync-half-async</artifactId>
<dependencies>

View File

@ -18,6 +18,11 @@ post-processing to requests from a client to a target
* a system should do the authentication/ authorization/ logging or tracking of request and then pass the requests to corresponding handlers
* you want a modular approach to configuring pre-processing and post-processing schemes
**Real world examples:**
* [Struts 2 - Interceptors](https://struts.apache.org/docs/interceptors.html)
**Credits:**
* [TutorialsPoint - Intercepting Filter](http://www.tutorialspoint.com/design_pattern/intercepting_filter_pattern.htm)
* [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns)

View File

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

View File

@ -1,9 +1,33 @@
package com.iluwatar.intercepting.filter;
/**
*
* This is an app that checks whether the order request is valid through pre-processing done via {@link Filter}.
* Each field has its own corresponding {@link Filter}
*
* When a request enters a Web application, it often must pass several entrance
* tests prior to the main processing stage. For example,
* - Has the client been authenticated?
* - Does the client have a valid session?
* - Is the client's IP address from a trusted network?
* - Does the request path violate any constraints?
* - What encoding does the client use to send the data?
* - Do we support the browser type of the client?
* Some of these checks are tests, resulting in a yes or no answer that determines
* whether processing will continue. Other checks manipulate the incoming data
* stream into a form suitable for processing.
* <p>
* The classic solution consists of a series of conditional checks, with any failed
* check aborting the request. Nested if/else statements are a standard strategy,
* but this solution leads to code fragility and a copy-and-paste style of programming,
* because the flow of the filtering and the action of the filters is compiled into
* the application.
* <p>
* The key to solving this problem in a flexible and unobtrusive manner is to have a
* simple mechanism for adding and removing processing components, in which each
* component completes a specific filtering action. This is the Intercepting Filter
* pattern in action.
* <p>
* In this example we check whether the order request is valid through pre-processing
* done via {@link Filter}. Each field has its own corresponding {@link Filter}
* <p>
* @author joshzambales
*
*/

View File

@ -4,7 +4,9 @@ title: Interpreter
folder: interpreter
permalink: /patterns/interpreter/
categories: Behavioral
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Given a language, define a representation for its grammar along
@ -19,3 +21,7 @@ trees. The Interpreter pattern works best when
* the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time
* efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example, regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

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

View File

@ -4,8 +4,14 @@ import java.util.Stack;
/**
*
* Interpreter pattern breaks sentences into expressions ({@link Expression}) that can
* be evaluated and as a whole form the result.
* The Interpreter pattern is a design pattern that specifies how to evaluate sentences
* in a language. The basic idea is to have a class for each symbol (terminal or nonterminal)
* in a specialized computer language. The syntax tree of a sentence in the language is an
* instance of the composite pattern and is used to evaluate (interpret) the sentence for a
* client.
* <p>
* In this example we use the Interpreter pattern to break sentences into expressions
* ({@link Expression}) that can be evaluated and as a whole form the result.
*
*/
public class App {

View File

@ -4,7 +4,10 @@ title: Iterator
folder: iterator
permalink: /patterns/iterator/
categories: Behavioral
tags: Java
tags:
- Java
- Difficulty-Beginner
- Gang Of Four
---
**Intent:** Provide a way to access the elements of an aggregate object
@ -21,3 +24,7 @@ sequentially without exposing its underlying representation.
**Real world examples:**
* [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

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

View File

@ -2,9 +2,13 @@ package com.iluwatar.iterator;
/**
*
* Iterator ({@link ItemIterator}) adds abstraction layer on top of a collection
* ({@link TreasureChest}). This way the collection can change its internal
* implementation without affecting its clients.
* The Iterator pattern is a design pattern in which an iterator is used to
* traverse a container and access the container's elements. The Iterator pattern
* decouples algorithms from containers.
* <p>
* In this example the Iterator ({@link ItemIterator}) adds abstraction layer on
* top of a collection ({@link TreasureChest}). This way the collection can change
* its internal implementation without affecting its clients.
*
*/
public class App {

View File

@ -17,3 +17,8 @@ tags: Java
* you want clearly divide software responsibilities into differents parts of the program
* you want to prevent a change from propagating throughout the application
* you want to make your application more maintainable and testable
**Credits:**
* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697)

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.6.0</version>
<version>1.7.0</version>
</parent>
<groupId>com.iluwatar.layers</groupId>
<artifactId>layers</artifactId>

View File

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

View File

@ -0,0 +1,42 @@
package com.iluwatar.lazy.loading;
import org.junit.Test;
import java.lang.reflect.Field;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* Using reflection this test shows that the heavy field is not instantiated until the method getHeavy is called
*
* Created by jones on 11/10/2015.
*/
public class HolderThreadSafeTest {
@Test
public void test() throws IllegalAccessException {
HolderThreadSafe hts = new HolderThreadSafe();
{//first call is null
Field[] ff = HolderThreadSafe.class.getDeclaredFields();
for (Field f: ff) {
f.setAccessible(true);
}
assertNull(ff[0].get(hts));
}
// now it is lazily loaded
hts.getHeavy();
{//now it is not null - call via reflection so that the test is the same before and after
Field[] ff = HolderThreadSafe.class.getDeclaredFields();
for (Field f: ff) {
f.setAccessible(true);
}
assertNotNull(ff[0].get(hts));
}
}
}

View File

@ -4,7 +4,10 @@ title: Mediator
folder: mediator
permalink: /patterns/mediator/
categories: Behavioral
tags: Java
tags:
- Java
- Gang Of Four
- Difficulty-Intermediate
---
**Intent:** Define an object that encapsulates how a set of objects interact.
@ -18,3 +21,7 @@ other explicitly, and it lets you vary their interaction independently.
* a set of objects communicate in well-defined but complex ways. The resulting interdependencies are unstructured and difficult to understand
* reusing an object is difficult because it refers to and communicates with many other objects
* a behavior that's distributed between several classes should be customizable without a lot of subclassing
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

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

View File

@ -2,8 +2,26 @@ package com.iluwatar.mediator;
/**
*
* Mediator encapsulates how a set of objects ({@link PartyMember}) interact. Instead of
* referring to each other directly they use a mediator ({@link Party}) interface.
* The Mediator pattern defines an object that encapsulates how a set of objects
* interact. This pattern is considered to be a behavioral pattern due to the way
* it can alter the program's running behavior.
* <p>
* Usually a program is made up of a large number of classes. So the logic and
* computation is distributed among these classes. However, as more classes are
* developed in a program, especially during maintenance and/or refactoring,
* the problem of communication between these classes may become more complex.
* This makes the program harder to read and maintain. Furthermore, it can become
* difficult to change the program, since any change may affect code in several
* other classes.
* <p>
* With the Mediator pattern, communication between objects is encapsulated with
* a mediator object. Objects no longer communicate directly with each other, but
* instead communicate through the mediator. This reduces the dependencies between
* communicating objects, thereby lowering the coupling.
* <p>
* In this example the mediator encapsulates how a set of objects ({@link PartyMember})
* interact. Instead of referring to each other directly they use the mediator
* ({@link Party}) interface.
*
*/
public class App {

View File

@ -4,7 +4,9 @@ title: Memento
folder: memento
permalink: /patterns/memento/
categories: Behavioral
tags: Java
tags:
- Java
- Gang Of Four
---
**Intent:** Without violating encapsulation, capture and externalize an
@ -20,3 +22,7 @@ object's internal state so that the object can be restored to this state later.
**Real world examples:**
* [java.util.Date](http://docs.oracle.com/javase/8/docs/api/java/util/Date.html)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

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

View File

@ -4,7 +4,20 @@ import java.util.Stack;
/**
*
* Memento pattern is for storing and restoring object state. The object ({@link Star})
* The Memento pattern is a software design pattern that provides the ability to restore
* an object to its previous state (undo via rollback).
* <p>
* The Memento pattern is implemented with three objects: the originator, a caretaker and
* a memento. The originator is some object that has an internal state. The caretaker is
* going to do something to the originator, but wants to be able to undo the change. The
* caretaker first asks the originator for a memento object. Then it does whatever operation
* (or sequence of operations) it was going to do. To roll back to the state before the
* operations, it returns the memento object to the originator. The memento object itself
* is an opaque object (one which the caretaker cannot, or should not, change). When using
* this pattern, care should be taken if the originator may change other objects or
* resources - the memento pattern operates on a single object.
* <p>
* In this example the object ({@link Star})
* gives out a "memento" ({@link StarMemento}) that contains the state of the object.
* Later on the memento can be set back to the object restoring the state.
*

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