Skip to content

Commit 9891c2e

Browse files
committed
Issue iluwatar#273:Caching Patterns [new pattern]
1 parent a869294 commit 9891c2e

File tree

14 files changed

+798
-0
lines changed

14 files changed

+798
-0
lines changed

caching/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target/

caching/etc/caching.png

50.2 KB
Loading

caching/etc/caching.ucls

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
3+
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
4+
<class id="1" language="java" name="main.java.com.wssia.caching.App" project="CachingPatterns"
5+
file="/CachingPatterns/src/main/java/com/wssia/caching/App.java" binary="false" corner="BOTTOM_RIGHT">
6+
<position height="-1" width="-1" x="249" y="150"/>
7+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
8+
sort-features="false" accessors="true" visibility="true">
9+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
10+
<operations public="true" package="true" protected="true" private="true" static="true"/>
11+
</display>
12+
</class>
13+
<class id="2" language="java" name="main.java.com.wssia.caching.AppManager" project="CachingPatterns"
14+
file="/CachingPatterns/src/main/java/com/wssia/caching/AppManager.java" binary="false" corner="BOTTOM_RIGHT">
15+
<position height="-1" width="-1" x="502" y="163"/>
16+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
17+
sort-features="false" accessors="true" visibility="true">
18+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
19+
<operations public="true" package="true" protected="true" private="true" static="true"/>
20+
</display>
21+
</class>
22+
<class id="3" language="java" name="main.java.com.wssia.caching.CacheStore" project="CachingPatterns"
23+
file="/CachingPatterns/src/main/java/com/wssia/caching/CacheStore.java" binary="false" corner="BOTTOM_RIGHT">
24+
<position height="-1" width="-1" x="537" y="436"/>
25+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
26+
sort-features="false" accessors="true" visibility="true">
27+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
28+
<operations public="true" package="true" protected="true" private="true" static="true"/>
29+
</display>
30+
</class>
31+
<enumeration id="4" language="java" name="main.java.com.wssia.caching.CachingPolicy" project="CachingPatterns"
32+
file="/CachingPatterns/src/main/java/com/wssia/caching/CachingPolicy.java" binary="false" corner="BOTTOM_RIGHT">
33+
<position height="-1" width="-1" x="789" y="162"/>
34+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
35+
sort-features="false" accessors="true" visibility="true">
36+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
37+
<operations public="true" package="true" protected="true" private="true" static="true"/>
38+
</display>
39+
</enumeration>
40+
<class id="5" language="java" name="main.java.com.wssia.caching.DBManager" project="CachingPatterns"
41+
file="/CachingPatterns/src/main/java/com/wssia/caching/DBManager.java" binary="false" corner="BOTTOM_RIGHT">
42+
<position height="-1" width="-1" x="1137" y="134"/>
43+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
44+
sort-features="false" accessors="true" visibility="true">
45+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
46+
<operations public="true" package="true" protected="true" private="true" static="true"/>
47+
</display>
48+
</class>
49+
<class id="6" language="java" name="main.java.com.wssia.caching.LRUCache" project="CachingPatterns"
50+
file="/CachingPatterns/src/main/java/com/wssia/caching/LRUCache.java" binary="false" corner="BOTTOM_RIGHT">
51+
<position height="-1" width="-1" x="884" y="435"/>
52+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
53+
sort-features="false" accessors="true" visibility="true">
54+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
55+
<operations public="true" package="true" protected="true" private="true" static="true"/>
56+
</display>
57+
</class>
58+
<class id="7" language="java" name="main.java.com.wssia.caching.UserAccount" project="CachingPatterns"
59+
file="/CachingPatterns/src/main/java/com/wssia/caching/UserAccount.java" binary="false" corner="BOTTOM_RIGHT">
60+
<position height="-1" width="-1" x="1137" y="382"/>
61+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
62+
sort-features="false" accessors="true" visibility="true">
63+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
64+
<operations public="true" package="true" protected="true" private="true" static="true"/>
65+
</display>
66+
</class>
67+
<class id="8" language="java" name="test.java.com.wssia.caching.AppTest" project="CachingPatterns"
68+
file="/CachingPatterns/src/test/java/com/wssia/caching/AppTest.java" binary="false" corner="BOTTOM_RIGHT">
69+
<position height="-1" width="-1" x="251" y="374"/>
70+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
71+
sort-features="false" accessors="true" visibility="true">
72+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
73+
<operations public="true" package="true" protected="true" private="true" static="true"/>
74+
</display>
75+
</class>
76+
<association id="9">
77+
<end type="SOURCE" refId="2" navigable="false">
78+
<attribute id="10" name="cachingPolicy"/>
79+
<multiplicity id="11" minimum="0" maximum="1"/>
80+
</end>
81+
<end type="TARGET" refId="4" navigable="true"/>
82+
<display labels="true" multiplicity="true"/>
83+
</association>
84+
<association id="12">
85+
<end type="SOURCE" refId="8" navigable="false">
86+
<attribute id="13" name="app"/>
87+
<multiplicity id="14" minimum="0" maximum="1"/>
88+
</end>
89+
<end type="TARGET" refId="1" navigable="true"/>
90+
<display labels="true" multiplicity="true"/>
91+
</association>
92+
<association id="15">
93+
<end type="SOURCE" refId="3" navigable="false">
94+
<attribute id="16" name="cache"/>
95+
<multiplicity id="17" minimum="0" maximum="1"/>
96+
</end>
97+
<end type="TARGET" refId="6" navigable="true"/>
98+
<display labels="true" multiplicity="true"/>
99+
</association>
100+
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
101+
sort-features="false" accessors="true" visibility="true">
102+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
103+
<operations public="true" package="true" protected="true" private="true" static="true"/>
104+
</classifier-display>
105+
<association-display labels="true" multiplicity="true"/>
106+
</class-diagram>

caching/index.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
layout: pattern
3+
title: Caching
4+
folder: caching
5+
permalink: /patterns/caching/
6+
categories: Other
7+
tags:
8+
- Java
9+
---
10+
11+
**Intent:** To avoid expensive re-acquisition of resources by not releasing
12+
the resources immediately after their use. The resources retain their identity, are kept in some
13+
fast-access storage, and are re-used to avoid having to acquire them again.
14+
15+
![alt text](./etc/caching.png "Caching")
16+
17+
**Applicability:** Use the Caching pattern(s) when
18+
19+
* Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead.
20+
21+
**Credits**
22+
23+
* [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained)
24+
* [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177)

caching/pom.xml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?xml version="1.0"?>
2+
<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"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>com.iluwatar</groupId>
7+
<artifactId>java-design-patterns</artifactId>
8+
<version>1.7.0</version>
9+
</parent>
10+
<artifactId>caching</artifactId>
11+
<dependencies>
12+
<dependency>
13+
<groupId>junit</groupId>
14+
<artifactId>junit</artifactId>
15+
<scope>test</scope>
16+
</dependency>
17+
<dependency>
18+
<groupId>org.mongodb</groupId>
19+
<artifactId>mongodb-driver</artifactId>
20+
<version>3.0.4</version>
21+
</dependency>
22+
<dependency>
23+
<groupId>org.mongodb</groupId>
24+
<artifactId>mongodb-driver-core</artifactId>
25+
<version>3.0.4</version>
26+
</dependency>
27+
<dependency>
28+
<groupId>org.mongodb</groupId>
29+
<artifactId>bson</artifactId>
30+
<version>3.0.4</version>
31+
</dependency>
32+
</dependencies>
33+
<!--
34+
Due to the use of MongoDB in the test of this pattern, TRAVIS and/or MAVEN might fail if the DB connection is
35+
not open for the JUnit test. To avoid disrupting the compilation process, the surefire plug-in was used
36+
to SKIP the running of the JUnit tests for this pattern. To RE-ACTIVATE the running of the tests, change the
37+
skipTests (below) flag to 'false'.
38+
-->
39+
<build>
40+
<plugins>
41+
<plugin>
42+
<groupId>org.apache.maven.plugins</groupId>
43+
<artifactId>maven-surefire-plugin</artifactId>
44+
<version>2.19</version>
45+
<configuration>
46+
<skipTests>true</skipTests>
47+
</configuration>
48+
</plugin>
49+
</plugins>
50+
</build>
51+
</project>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package main.java.com.wssia.caching;
2+
3+
/**
4+
*
5+
* The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing
6+
* the resources immediately after their use. The resources retain their identity, are kept in some
7+
* fast-access storage, and are re-used to avoid having to acquire them again. There are three main
8+
* caching strategies/techniques in this pattern; each with their own pros and cons. They are:
9+
* <code>write-through</code> which writes data to the cache and DB in a single transaction,
10+
* <code>write-around</code> which writes data immediately into the DB instead of the cache, and
11+
* <code>write-behind</code> which writes data into the cache initially whilst the data is only
12+
* written into the DB when the cache is full. The <code>read-through</code> strategy is also
13+
* included in the mentioned three strategies -- returns data from the cache to the caller <b>if</b>
14+
* it exists <b>else</b> queries from DB and stores it into the cache for future use. These
15+
* strategies determine when the data in the cache should be written back to the backing store (i.e.
16+
* Database) and help keep both data sources synchronized/up-to-date. This pattern can improve
17+
* performance and also helps to maintain consistency between data held in the cache and the data in
18+
* the underlying data store.
19+
* <p>
20+
* In this example, the user account ({@link UserAccount}) entity is used as the underlying
21+
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts
22+
* a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The three
23+
* strategies are individually tested. The testing of the cache is restricted towards saving and
24+
* querying of user accounts from the underlying data store ( {@link DBManager}). The main class (
25+
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
26+
* whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
27+
* ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
28+
* (depending on the preferred caching policy/strategy).
29+
*
30+
* <i>App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager</i>
31+
* <p>
32+
*
33+
* @see CacheStore
34+
* @See LRUCache
35+
* @see CachingPolicy
36+
*
37+
*/
38+
public class App {
39+
40+
/**
41+
* Read-through and write-through
42+
*/
43+
public void useReadAndWriteThroughStrategy() {
44+
System.out.println("# CachingPolicy.THROUGH");
45+
AppManager.initCachingPolicy(CachingPolicy.THROUGH);
46+
47+
UserAccount userAccount1 = new UserAccount("001", "John", "He is a boy.");
48+
49+
AppManager.save(userAccount1);
50+
System.out.println(AppManager.printCacheContent());
51+
userAccount1 = AppManager.find("001");
52+
userAccount1 = AppManager.find("001");
53+
}
54+
55+
/**
56+
* Read-through and write-around
57+
*/
58+
public void useReadThroughAndWriteAroundStrategy() {
59+
System.out.println("# CachingPolicy.AROUND");
60+
AppManager.initCachingPolicy(CachingPolicy.AROUND);
61+
62+
UserAccount userAccount2 = new UserAccount("002", "Jane", "She is a girl.");
63+
64+
AppManager.save(userAccount2);
65+
System.out.println(AppManager.printCacheContent());
66+
userAccount2 = AppManager.find("002");
67+
System.out.println(AppManager.printCacheContent());
68+
userAccount2 = AppManager.find("002");
69+
userAccount2.setUserName("Jane G.");
70+
AppManager.save(userAccount2);
71+
System.out.println(AppManager.printCacheContent());
72+
userAccount2 = AppManager.find("002");
73+
System.out.println(AppManager.printCacheContent());
74+
userAccount2 = AppManager.find("002");
75+
}
76+
77+
/**
78+
* Read-through and write-behind
79+
*/
80+
public void useReadThroughAndWriteBehindStrategy() {
81+
System.out.println("# CachingPolicy.BEHIND");
82+
AppManager.initCachingPolicy(CachingPolicy.BEHIND);
83+
84+
UserAccount userAccount3 = new UserAccount("003", "Adam", "He likes food.");
85+
UserAccount userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
86+
UserAccount userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
87+
88+
AppManager.save(userAccount3);
89+
AppManager.save(userAccount4);
90+
AppManager.save(userAccount5);
91+
System.out.println(AppManager.printCacheContent());
92+
userAccount3 = AppManager.find("003");
93+
System.out.println(AppManager.printCacheContent());
94+
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
95+
AppManager.save(userAccount6);
96+
System.out.println(AppManager.printCacheContent());
97+
userAccount4 = AppManager.find("004");
98+
System.out.println(AppManager.printCacheContent());
99+
}
100+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package main.java.com.wssia.caching;
2+
3+
import java.text.ParseException;
4+
5+
/**
6+
*
7+
* AppManager helps to bridge the gap in communication between the main class and the application's
8+
* back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
9+
* also initialized here. Before the cache can be used, the size of the cache has to be set.
10+
* Depending on the chosen caching policy, AppManager will call the appropriate function in the
11+
* CacheStore class.
12+
*
13+
*/
14+
public class AppManager {
15+
16+
private static CachingPolicy cachingPolicy;
17+
18+
public static void init() {
19+
try {
20+
DBManager.connect();
21+
} catch (ParseException e) {
22+
e.printStackTrace();
23+
}
24+
}
25+
26+
public static void initCachingPolicy(CachingPolicy policy) {
27+
cachingPolicy = policy;
28+
if (cachingPolicy == CachingPolicy.BEHIND) {
29+
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
30+
@Override
31+
public void run() {
32+
CacheStore.flushCache();
33+
}
34+
}));
35+
}
36+
CacheStore.clearCache();
37+
}
38+
39+
public static void initCacheCapacity(int capacity) {
40+
CacheStore.initCapacity(capacity);
41+
}
42+
43+
public static UserAccount find(String userID) {
44+
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
45+
return CacheStore.readThrough(userID);
46+
} else if (cachingPolicy == CachingPolicy.BEHIND) {
47+
return CacheStore.readThroughWithWriteBackPolicy(userID);
48+
}
49+
return null;
50+
}
51+
52+
public static void save(UserAccount userAccount) {
53+
if (cachingPolicy == CachingPolicy.THROUGH) {
54+
CacheStore.writeThrough(userAccount);
55+
} else if (cachingPolicy == CachingPolicy.AROUND) {
56+
CacheStore.writeAround(userAccount);
57+
} else if (cachingPolicy == CachingPolicy.BEHIND) {
58+
CacheStore.writeBehind(userAccount);
59+
}
60+
}
61+
62+
public static String printCacheContent() {
63+
return CacheStore.print();
64+
}
65+
}

0 commit comments

Comments
 (0)