Compare commits

...

32 Commits

Author SHA1 Message Date
Ilkka Seppälä
e05595194d
Update image paths for localizations 2021-11-01 21:41:20 +02:00
Ilkka Seppälä
f4d67c77f7
update circuit breaker image paths 2021-10-31 20:08:01 +02:00
Ilkka Seppälä
4d54781f79
update image paths 2021-10-31 20:00:22 +02:00
Ilkka Seppälä
fd915161ee
Add index.md for Chinese translation 2021-10-31 18:43:28 +02:00
Ilkka Seppälä
097ddb385f
Update pattern index.md 2021-10-12 20:52:19 +03:00
Ilkka Seppälä
e4ab8c8066
add missing png 2021-10-12 15:18:54 +03:00
Ilkka Seppälä
c3ef3dbe8c
fix frontmatter 2021-10-12 14:30:49 +03:00
Ilkka Seppälä
c6c4e6ebb6
Merge branch 'master' into vuepress
# Conflicts:
#	README.md
#	command/README.md
2021-10-12 14:05:22 +03:00
Ilkka Seppälä
801be2520b
Merge branch 'master' of https://github.com/iluwatar/java-design-patterns 2021-10-12 13:59:21 +03:00
Ilkka Seppälä
ce4a6df5c5
Merge branch 'master' of https://github.com/iluwatar/java-design-patterns 2021-10-09 09:34:47 +03:00
Ilkka Seppälä
95699c5de3
Merge branch 'issue-1694' of https://github.com/VictorZZZZ/java-design-patterns 2021-10-08 20:17:49 +03:00
Victor Zalevskii
c459b92933 Fixes according PR comments. Mainly Readme edits. 2021-10-06 15:34:04 +03:00
Ilkka Seppälä
223b779c83
Work on index page 2021-09-12 20:54:19 +03:00
Ilkka Seppälä
9472f10e18
Work on patterns index page 2021-09-12 20:09:17 +03:00
Ilkka Seppälä
b6b2079a10
Update frontmatters 2021-09-12 13:27:23 +03:00
Ilkka Seppälä
81f9291e76
Fix link 2021-09-11 19:11:30 +03:00
Ilkka Seppälä
58c0008b75
Fix some links in localization folders 2021-09-11 19:09:39 +03:00
Ilkka Seppälä
bc7040566e
Update filename 2021-09-11 18:58:50 +03:00
Ilkka Seppälä
bb49e3c4e9
Remove extra space 2021-09-11 17:55:04 +03:00
Ilkka Seppälä
842b400fee
Fix some broken links 2021-09-11 16:56:02 +03:00
Victor Zalevskii
2a01563d21 Comments to start Application with mongo. 2021-09-02 15:33:06 +03:00
Victor Zalevskii
45af6987c1 Provided missing tests 2021-09-02 15:30:07 +03:00
Victor Zalevskii
6adb27ca3d Added docker-compose for mongo db. MongoDb db work fixed. 2021-09-02 15:24:08 +03:00
Victor Zalevskii
c0e4bf3d1d Add test 2021-09-01 10:27:46 +03:00
Victor Zalevskii
50755b7215 Fix Bug with mongo connection. Used "Try with resources" 2021-08-30 09:13:08 +03:00
Victor Zalevskii
49039843e5 Fix issues in VALIDATE phase 2021-08-27 15:29:58 +03:00
Victor Zalevskii
dabe4d2022 Fix sonar issues 2021-08-27 13:27:50 +03:00
Victor Zalevskii
aff7ef8782 Fix last errors in checkstyle. 2021-08-23 12:37:24 +03:00
Victor Z
c150e5f38b Fix CacheStore errors from checkstyle plugin 107 left 2021-08-22 11:12:53 +03:00
Victor Z
3c213fcd69 Fix 40 errors from checkstyle plugin run. 139 left)) 2021-08-17 22:05:01 +03:00
Victor Zalevskii
a437f5b2eb Fix Logs 2021-08-17 16:53:44 +03:00
Victor Zalevskii
399ad53b09 Changed database implementation. Removed static objects. 2021-08-17 16:25:59 +03:00
282 changed files with 1397 additions and 1336 deletions

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Abstract Document
folder: abstract-document
permalink: /patterns/abstract-document/
categories: Structural
category: Structural
language: en
tags:
- Extensibility

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Abstract Factory
folder: abstract-factory
permalink: /patterns/abstract-factory/
categories: Creational
category: Creational
language: en
tags:
- Gang of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Active Object
folder: active-object
permalink: /patterns/active-object/
categories: Concurrency
category: Concurrency
language: en
tags:
- Performance

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Acyclic Visitor
folder: acyclic-visitor
permalink: /patterns/acyclic-visitor/
categories: Behavioral
category: Behavioral
language: en
tags:
- Extensibility

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Adapter
folder: adapter
permalink: /patterns/adapter/
categories: Structural
category: Structural
language: en
tags:
- Gang of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Aggregator Microservices
folder: aggregator-microservices
permalink: /patterns/aggregator-microservices/
categories: Architectural
category: Architectural
language: en
tags:
- Cloud distributed

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Ambassador
folder: ambassador
permalink: /patterns/ambassador/
categories: Structural
category: Structural
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: API Gateway
folder: api-gateway
permalink: /patterns/api-gateway/
categories: Architectural
category: Architectural
language: en
tags:
- Cloud distributed

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Arrange/Act/Assert
folder: arrange-act-assert
permalink: /patterns/arrange-act-assert/
categories: Idiom
category: Idiom
language: en
tags:
- Testing

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Async Method Invocation
folder: async-method-invocation
permalink: /patterns/async-method-invocation/
categories: Concurrency
category: Concurrency
language: en
tags:
- Reactive

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Balking
folder: balking
permalink: /patterns/balking/
categories: Concurrency
category: Concurrency
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Bridge
folder: bridge
permalink: /patterns/bridge/
categories: Structural
category: Structural
language: en
tags:
- Gang of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Builder
folder: builder
permalink: /patterns/builder/
categories: Creational
category: Creational
language: en
tags:
- Gang of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Business Delegate
folder: business-delegate
permalink: /patterns/business-delegate/
categories: Structural
category: Structural
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Bytecode
folder: bytecode
permalink: /patterns/bytecode/
categories: Behavioral
category: Behavioral
language: en
tags:
- Game programming

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Caching
folder: caching
permalink: /patterns/caching/
categories: Behavioral
category: Behavioral
language: en
tags:
- Performance
@ -43,39 +40,29 @@ Wikipedia says:
**Programmatic Example**
Let's first look at the data layer of our application. The interesting classes are `UserAccount`
which is a simple Java object containing the user account details, and `DbManager` which handles
reading and writing of these objects to/from MongoDB database.
which is a simple Java object containing the user account details, and `DbManager` interface which handles
reading and writing of these objects to/from database.
```java
@Setter
@Getter
@Data
@AllArgsConstructor
@ToString
@EqualsAndHashCode
public class UserAccount {
private String userId;
private String userName;
private String additionalInfo;
}
@Slf4j
public final class DbManager {
public interface DbManager {
private static MongoClient mongoClient;
private static MongoDatabase db;
private DbManager() { /*...*/ }
public static void createVirtualDb() { /*...*/ }
public static void connect() throws ParseException { /*...*/ }
public static UserAccount readFromDb(String userId) { /*...*/ }
public static void writeToDb(UserAccount userAccount) { /*...*/ }
public static void updateDb(UserAccount userAccount) { /*...*/ }
public static void upsertDb(UserAccount userAccount) { /*...*/ }
void connect();
void disconnect();
UserAccount readFromDb(String userId);
UserAccount writeToDb(UserAccount userAccount);
UserAccount updateDb(UserAccount userAccount);
UserAccount upsertDb(UserAccount userAccount);
}
```
@ -171,30 +158,43 @@ strategies.
@Slf4j
public class CacheStore {
private static final int CAPACITY = 3;
private static LruCache cache;
private final DbManager dbManager;
/* ... details omitted ... */
public static UserAccount readThrough(String userId) {
public UserAccount readThrough(final String userId) {
if (cache.contains(userId)) {
LOGGER.info("# Cache Hit!");
LOGGER.info("# Found in Cache!");
return cache.get(userId);
}
LOGGER.info("# Cache Miss!");
UserAccount userAccount = DbManager.readFromDb(userId);
LOGGER.info("# Not found in cache! Go to DB!!");
UserAccount userAccount = dbManager.readFromDb(userId);
cache.set(userId, userAccount);
return userAccount;
}
public static void writeThrough(UserAccount userAccount) {
public void writeThrough(final UserAccount userAccount) {
if (cache.contains(userAccount.getUserId())) {
DbManager.updateDb(userAccount);
dbManager.updateDb(userAccount);
} else {
DbManager.writeToDb(userAccount);
dbManager.writeToDb(userAccount);
}
cache.set(userAccount.getUserId(), userAccount);
}
public void writeAround(final UserAccount userAccount) {
if (cache.contains(userAccount.getUserId())) {
dbManager.updateDb(userAccount);
// Cache data has been updated -- remove older
cache.invalidate(userAccount.getUserId());
// version from cache.
} else {
dbManager.writeToDb(userAccount);
}
}
public static void clearCache() {
if (cache != null) {
cache.clear();
@ -225,34 +225,39 @@ class.
public final class AppManager {
private static CachingPolicy cachingPolicy;
private final DbManager dbManager;
private final CacheStore cacheStore;
private AppManager() {
}
public static void initDb(boolean useMongoDb) { /* ... */ }
public void initDb() { /* ... */ }
public static void initCachingPolicy(CachingPolicy policy) { /* ... */ }
public static void initCacheCapacity(int capacity) { /* ... */ }
public static UserAccount find(String userId) {
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
return CacheStore.readThrough(userId);
public UserAccount find(final String userId) {
LOGGER.info("Trying to find {} in cache", userId);
if (cachingPolicy == CachingPolicy.THROUGH
|| cachingPolicy == CachingPolicy.AROUND) {
return cacheStore.readThrough(userId);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
return CacheStore.readThroughWithWriteBackPolicy(userId);
return cacheStore.readThroughWithWriteBackPolicy(userId);
} else if (cachingPolicy == CachingPolicy.ASIDE) {
return findAside(userId);
}
return null;
}
public static void save(UserAccount userAccount) {
public void save(final UserAccount userAccount) {
LOGGER.info("Save record!");
if (cachingPolicy == CachingPolicy.THROUGH) {
CacheStore.writeThrough(userAccount);
cacheStore.writeThrough(userAccount);
} else if (cachingPolicy == CachingPolicy.AROUND) {
CacheStore.writeAround(userAccount);
cacheStore.writeAround(userAccount);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
CacheStore.writeBehind(userAccount);
cacheStore.writeBehind(userAccount);
} else if (cachingPolicy == CachingPolicy.ASIDE) {
saveAside(userAccount);
}
@ -272,24 +277,35 @@ Here is what we do in the main class of the application.
@Slf4j
public class App {
public static void main(String[] args) {
AppManager.initDb(false);
AppManager.initCacheCapacity(3);
var app = new App();
public static void main(final String[] args) {
boolean isDbMongo = isDbMongo(args);
if(isDbMongo){
LOGGER.info("Using the Mongo database engine to run the application.");
} else {
LOGGER.info("Using the 'in Memory' database to run the application.");
}
App app = new App(isDbMongo);
app.useReadAndWriteThroughStrategy();
String splitLine = "==============================================";
LOGGER.info(splitLine);
app.useReadThroughAndWriteAroundStrategy();
LOGGER.info(splitLine);
app.useReadThroughAndWriteBehindStrategy();
LOGGER.info(splitLine);
app.useCacheAsideStategy();
LOGGER.info(splitLine);
}
public void useReadAndWriteThroughStrategy() {
LOGGER.info("# CachingPolicy.THROUGH");
AppManager.initCachingPolicy(CachingPolicy.THROUGH);
appManager.initCachingPolicy(CachingPolicy.THROUGH);
var userAccount1 = new UserAccount("001", "John", "He is a boy.");
AppManager.save(userAccount1);
LOGGER.info(AppManager.printCacheContent());
AppManager.find("001");
AppManager.find("001");
appManager.save(userAccount1);
LOGGER.info(appManager.printCacheContent());
appManager.find("001");
appManager.find("001");
}
public void useReadThroughAndWriteAroundStrategy() { /* ... */ }
@ -300,16 +316,6 @@ public class App {
}
```
Finally, here is some of the console output from the program.
```
12:32:53.845 [main] INFO com.iluwatar.caching.App - # CachingPolicy.THROUGH
12:32:53.900 [main] INFO com.iluwatar.caching.App -
--CACHE CONTENT--
UserAccount(userId=001, userName=John, additionalInfo=He is a boy.)
----
```
## Class diagram
![alt text](./etc/caching.png "Caching")

View File

@ -0,0 +1,11 @@
version: '3.7'
services:
mongodb_container:
image: mongo:latest
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: rootpassword
ports:
- 27017:27017
volumes:
- ./mongo-data/:/data/db

View File

@ -39,19 +39,21 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.12.1</version>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</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>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
<!--

View File

@ -1,58 +1,62 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.caching;
import com.iluwatar.caching.database.DbManager;
import com.iluwatar.caching.database.DbManagerFactory;
import lombok.extern.slf4j.Slf4j;
/**
* 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 four 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,
* <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, and <code>cache-aside</code> which pushes the
* responsibility of keeping the data synchronized in both data sources to the application itself.
* The <code>read-through</code> strategy is also included in the mentioned four 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.
* 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 four 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, <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, and <code>cache-aside</code>
* which pushes the responsibility of keeping the data synchronized in both
* data sources to the application itself. The <code>read-through</code>
* strategy is also included in the mentioned four 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 maintainconsistency 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 four
* 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).
* <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 four
* 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).
* <p>
* <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager} </i>
* <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy -->
* DBManager} </i>
* </p>
*
* <p>
* There are 2 ways to launch the application.
* - to use "in Memory" database.
* - to use the MongoDb as a database
*
* To run the application with "in Memory" database, just launch it without parameters
* Example: 'java -jar app.jar'
*
* To run the application with MongoDb you need to be installed the MongoDb
* in your system, or to launch it in the docker container.
* You may launch docker container from the root of current module with command:
* 'docker-compose up'
* Then you can start the application with parameter --mongo
* Example: 'java -jar app.jar --mongo'
* </p>
*
* @see CacheStore
@ -61,23 +65,67 @@ import lombok.extern.slf4j.Slf4j;
*/
@Slf4j
public class App {
/**
* Constant parameter name to use mongoDB.
*/
private static final String USE_MONGO_DB = "--mongo";
/**
* Application manager.
*/
private final AppManager appManager;
/**
* Constructor of current App.
*
* @param isMongo boolean
*/
public App(final boolean isMongo) {
DbManager dbManager = DbManagerFactory.initDb(isMongo);
appManager = new AppManager(dbManager);
appManager.initDb();
}
/**
* 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
public static void main(final String[] args) {
// 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);
var app = new App();
boolean isDbMongo = isDbMongo(args);
if (isDbMongo) {
LOGGER.info("Using the Mongo database engine to run the application.");
} else {
LOGGER.info("Using the 'in Memory' database to run the application.");
}
App app = new App(isDbMongo);
app.useReadAndWriteThroughStrategy();
String splitLine = "==============================================";
LOGGER.info(splitLine);
app.useReadThroughAndWriteAroundStrategy();
LOGGER.info(splitLine);
app.useReadThroughAndWriteBehindStrategy();
LOGGER.info(splitLine);
app.useCacheAsideStategy();
LOGGER.info(splitLine);
}
/**
* Check the input parameters. if
*
* @param args input params
* @return true if there is "--mongo" parameter in arguments
*/
private static boolean isDbMongo(final String[] args) {
for (String arg : args) {
if (arg.equals(USE_MONGO_DB)) {
return true;
}
}
return false;
}
/**
@ -85,14 +133,14 @@ public class App {
*/
public void useReadAndWriteThroughStrategy() {
LOGGER.info("# CachingPolicy.THROUGH");
AppManager.initCachingPolicy(CachingPolicy.THROUGH);
appManager.initCachingPolicy(CachingPolicy.THROUGH);
var userAccount1 = new UserAccount("001", "John", "He is a boy.");
AppManager.save(userAccount1);
LOGGER.info(AppManager.printCacheContent());
AppManager.find("001");
AppManager.find("001");
appManager.save(userAccount1);
LOGGER.info(appManager.printCacheContent());
appManager.find("001");
appManager.find("001");
}
/**
@ -100,21 +148,21 @@ public class App {
*/
public void useReadThroughAndWriteAroundStrategy() {
LOGGER.info("# CachingPolicy.AROUND");
AppManager.initCachingPolicy(CachingPolicy.AROUND);
appManager.initCachingPolicy(CachingPolicy.AROUND);
var userAccount2 = new UserAccount("002", "Jane", "She is a girl.");
AppManager.save(userAccount2);
LOGGER.info(AppManager.printCacheContent());
AppManager.find("002");
LOGGER.info(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
appManager.save(userAccount2);
LOGGER.info(appManager.printCacheContent());
appManager.find("002");
LOGGER.info(appManager.printCacheContent());
userAccount2 = appManager.find("002");
userAccount2.setUserName("Jane G.");
AppManager.save(userAccount2);
LOGGER.info(AppManager.printCacheContent());
AppManager.find("002");
LOGGER.info(AppManager.printCacheContent());
AppManager.find("002");
appManager.save(userAccount2);
LOGGER.info(appManager.printCacheContent());
appManager.find("002");
LOGGER.info(appManager.printCacheContent());
appManager.find("002");
}
/**
@ -122,23 +170,31 @@ public class App {
*/
public void useReadThroughAndWriteBehindStrategy() {
LOGGER.info("# CachingPolicy.BEHIND");
AppManager.initCachingPolicy(CachingPolicy.BEHIND);
appManager.initCachingPolicy(CachingPolicy.BEHIND);
var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
var userAccount3 = new UserAccount("003",
"Adam",
"He likes food.");
var userAccount4 = new UserAccount("004",
"Rita",
"She hates cats.");
var userAccount5 = new UserAccount("005",
"Isaac",
"He is allergic to mustard.");
AppManager.save(userAccount3);
AppManager.save(userAccount4);
AppManager.save(userAccount5);
LOGGER.info(AppManager.printCacheContent());
AppManager.find("003");
LOGGER.info(AppManager.printCacheContent());
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
AppManager.save(userAccount6);
LOGGER.info(AppManager.printCacheContent());
AppManager.find("004");
LOGGER.info(AppManager.printCacheContent());
appManager.save(userAccount3);
appManager.save(userAccount4);
appManager.save(userAccount5);
LOGGER.info(appManager.printCacheContent());
appManager.find("003");
LOGGER.info(appManager.printCacheContent());
UserAccount userAccount6 = new UserAccount("006",
"Yasha",
"She is an only child.");
appManager.save(userAccount6);
LOGGER.info(appManager.printCacheContent());
appManager.find("004");
LOGGER.info(appManager.printCacheContent());
}
/**
@ -146,20 +202,26 @@ public class App {
*/
public void useCacheAsideStategy() {
LOGGER.info("# CachingPolicy.ASIDE");
AppManager.initCachingPolicy(CachingPolicy.ASIDE);
LOGGER.info(AppManager.printCacheContent());
appManager.initCachingPolicy(CachingPolicy.ASIDE);
LOGGER.info(appManager.printCacheContent());
var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
AppManager.save(userAccount3);
AppManager.save(userAccount4);
AppManager.save(userAccount5);
var userAccount3 = new UserAccount("003",
"Adam",
"He likes food.");
var userAccount4 = new UserAccount("004",
"Rita",
"She hates cats.");
var userAccount5 = new UserAccount("005",
"Isaac",
"He is allergic to mustard.");
appManager.save(userAccount3);
appManager.save(userAccount4);
appManager.save(userAccount5);
LOGGER.info(AppManager.printCacheContent());
AppManager.find("003");
LOGGER.info(AppManager.printCacheContent());
AppManager.find("004");
LOGGER.info(AppManager.printCacheContent());
LOGGER.info(appManager.printCacheContent());
appManager.find("003");
LOGGER.info(appManager.printCacheContent());
appManager.find("004");
LOGGER.info(appManager.printCacheContent());
}
}

View File

@ -23,65 +23,80 @@
package com.iluwatar.caching;
import java.text.ParseException;
import com.iluwatar.caching.database.DbManager;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
/**
* 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.
* 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.
*/
@Slf4j
public final class AppManager {
public class AppManager {
/**
* Caching Policy.
*/
private CachingPolicy cachingPolicy;
/**
* Database Manager.
*/
private final DbManager dbManager;
/**
* Cache Store.
*/
private final CacheStore cacheStore;
private static CachingPolicy cachingPolicy;
private AppManager() {
/**
* Constructor.
*
* @param newDbManager database manager
*/
public AppManager(final DbManager newDbManager) {
this.dbManager = newDbManager;
this.cacheStore = new CacheStore(newDbManager);
}
/**
* 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.
* 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) {
LOGGER.error("Error connecting to MongoDB", e);
}
} else {
DbManager.createVirtualDb();
}
public void initDb() {
dbManager.connect();
}
/**
* Initialize caching policy.
*
* @param policy is a {@link CachingPolicy}
*/
public static void initCachingPolicy(CachingPolicy policy) {
public void initCachingPolicy(final CachingPolicy policy) {
cachingPolicy = policy;
if (cachingPolicy == CachingPolicy.BEHIND) {
Runtime.getRuntime().addShutdownHook(new Thread(CacheStore::flushCache));
Runtime.getRuntime().addShutdownHook(new Thread(cacheStore::flushCache));
}
CacheStore.clearCache();
}
public static void initCacheCapacity(int capacity) {
CacheStore.initCapacity(capacity);
cacheStore.clearCache();
}
/**
* Find user account.
*
* @param userId String
* @return {@link UserAccount}
*/
public static UserAccount find(String userId) {
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
return CacheStore.readThrough(userId);
public UserAccount find(final String userId) {
LOGGER.info("Trying to find {} in cache", userId);
if (cachingPolicy == CachingPolicy.THROUGH
|| cachingPolicy == CachingPolicy.AROUND) {
return cacheStore.readThrough(userId);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
return CacheStore.readThroughWithWriteBackPolicy(userId);
return cacheStore.readThroughWithWriteBackPolicy(userId);
} else if (cachingPolicy == CachingPolicy.ASIDE) {
return findAside(userId);
}
@ -90,41 +105,55 @@ public final class AppManager {
/**
* Save user account.
*
* @param userAccount {@link UserAccount}
*/
public static void save(UserAccount userAccount) {
public void save(final UserAccount userAccount) {
LOGGER.info("Save record!");
if (cachingPolicy == CachingPolicy.THROUGH) {
CacheStore.writeThrough(userAccount);
cacheStore.writeThrough(userAccount);
} else if (cachingPolicy == CachingPolicy.AROUND) {
CacheStore.writeAround(userAccount);
cacheStore.writeAround(userAccount);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
CacheStore.writeBehind(userAccount);
cacheStore.writeBehind(userAccount);
} else if (cachingPolicy == CachingPolicy.ASIDE) {
saveAside(userAccount);
}
}
public static String printCacheContent() {
return CacheStore.print();
/**
* Returns String.
*
* @return String
*/
public String printCacheContent() {
return cacheStore.print();
}
/**
* Cache-Aside save user account helper.
*
* @param userAccount {@link UserAccount}
*/
private static void saveAside(UserAccount userAccount) {
DbManager.updateDb(userAccount);
CacheStore.invalidate(userAccount.getUserId());
private void saveAside(final UserAccount userAccount) {
dbManager.updateDb(userAccount);
cacheStore.invalidate(userAccount.getUserId());
}
/**
* Cache-Aside find user account helper.
*
* @param userId String
* @return {@link UserAccount}
*/
private static UserAccount findAside(String userId) {
return Optional.ofNullable(CacheStore.get(userId))
.or(() -> {
Optional<UserAccount> userAccount = Optional.ofNullable(DbManager.readFromDb(userId));
userAccount.ifPresent(account -> CacheStore.set(userId, account));
return userAccount;
})
.orElse(null);
private UserAccount findAside(final String userId) {
return Optional.ofNullable(cacheStore.get(userId))
.or(() -> {
Optional<UserAccount> userAccount =
Optional.ofNullable(dbManager.readFromDb(userId));
userAccount.ifPresent(account -> cacheStore.set(userId, account));
return userAccount;
})
.orElse(null);
}
}

View File

@ -23,9 +23,11 @@
package com.iluwatar.caching;
import com.iluwatar.caching.database.DbManager;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
/**
@ -33,16 +35,34 @@ import lombok.extern.slf4j.Slf4j;
*/
@Slf4j
public class CacheStore {
/**
* Cache capacity.
*/
private static final int CAPACITY = 3;
private static LruCache cache;
/**
* Lru cache see {@link LruCache}.
*/
private LruCache cache;
/**
* DbManager.
*/
private final DbManager dbManager;
private CacheStore() {
/**
* Cache Store.
* @param dataBaseManager {@link DbManager}
*/
public CacheStore(final DbManager dataBaseManager) {
this.dbManager = dataBaseManager;
initCapacity(CAPACITY);
}
/**
* Init cache capacity.
* @param capacity int
*/
public static void initCapacity(int capacity) {
public void initCapacity(final int capacity) {
if (cache == null) {
cache = new LruCache(capacity);
} else {
@ -52,57 +72,64 @@ public class CacheStore {
/**
* Get user account using read-through cache.
* @param userId {@link String}
* @return {@link UserAccount}
*/
public static UserAccount readThrough(String userId) {
public UserAccount readThrough(final String userId) {
if (cache.contains(userId)) {
LOGGER.info("# Cache Hit!");
LOGGER.info("# Found in Cache!");
return cache.get(userId);
}
LOGGER.info("# Cache Miss!");
UserAccount userAccount = DbManager.readFromDb(userId);
LOGGER.info("# Not found in cache! Go to DB!!");
UserAccount userAccount = dbManager.readFromDb(userId);
cache.set(userId, userAccount);
return userAccount;
}
/**
* Get user account using write-through cache.
* @param userAccount {@link UserAccount}
*/
public static void writeThrough(UserAccount userAccount) {
public void writeThrough(final UserAccount userAccount) {
if (cache.contains(userAccount.getUserId())) {
DbManager.updateDb(userAccount);
dbManager.updateDb(userAccount);
} else {
DbManager.writeToDb(userAccount);
dbManager.writeToDb(userAccount);
}
cache.set(userAccount.getUserId(), userAccount);
}
/**
* Get user account using write-around cache.
* @param userAccount {@link UserAccount}
*/
public static void writeAround(UserAccount userAccount) {
public void writeAround(final UserAccount userAccount) {
if (cache.contains(userAccount.getUserId())) {
DbManager.updateDb(userAccount);
cache.invalidate(userAccount.getUserId()); // Cache data has been updated -- remove older
dbManager.updateDb(userAccount);
// Cache data has been updated -- remove older
cache.invalidate(userAccount.getUserId());
// version from cache.
} else {
DbManager.writeToDb(userAccount);
dbManager.writeToDb(userAccount);
}
}
/**
* Get user account using read-through cache with write-back policy.
* @param userId {@link String}
* @return {@link UserAccount}
*/
public static UserAccount readThroughWithWriteBackPolicy(String userId) {
public UserAccount readThroughWithWriteBackPolicy(final String userId) {
if (cache.contains(userId)) {
LOGGER.info("# Cache Hit!");
LOGGER.info("# Found in cache!");
return cache.get(userId);
}
LOGGER.info("# Cache Miss!");
UserAccount userAccount = DbManager.readFromDb(userId);
LOGGER.info("# Not found in Cache!");
UserAccount userAccount = dbManager.readFromDb(userId);
if (cache.isFull()) {
LOGGER.info("# Cache is FULL! Writing LRU data to DB...");
UserAccount toBeWrittenToDb = cache.getLruData();
DbManager.upsertDb(toBeWrittenToDb);
dbManager.upsertDb(toBeWrittenToDb);
}
cache.set(userId, userAccount);
return userAccount;
@ -110,12 +137,13 @@ public class CacheStore {
/**
* Set user account.
* @param userAccount {@link UserAccount}
*/
public static void writeBehind(UserAccount userAccount) {
public void writeBehind(final UserAccount userAccount) {
if (cache.isFull() && !cache.contains(userAccount.getUserId())) {
LOGGER.info("# Cache is FULL! Writing LRU data to DB...");
UserAccount toBeWrittenToDb = cache.getLruData();
DbManager.upsertDb(toBeWrittenToDb);
dbManager.upsertDb(toBeWrittenToDb);
}
cache.set(userAccount.getUserId(), userAccount);
}
@ -123,7 +151,7 @@ public class CacheStore {
/**
* Clears cache.
*/
public static void clearCache() {
public void clearCache() {
if (cache != null) {
cache.clear();
}
@ -132,44 +160,51 @@ public class CacheStore {
/**
* Writes remaining content in the cache into the DB.
*/
public static void flushCache() {
public void flushCache() {
LOGGER.info("# flushCache...");
Optional.ofNullable(cache)
.map(LruCache::getCacheDataInListForm)
.orElse(List.of())
.forEach(DbManager::updateDb);
.forEach(dbManager::updateDb);
dbManager.disconnect();
}
/**
* Print user accounts.
* @return {@link String}
*/
public static String print() {
public String print() {
return Optional.ofNullable(cache)
.map(LruCache::getCacheDataInListForm)
.orElse(List.of())
.stream()
.map(userAccount -> userAccount.toString() + "\n")
.collect(Collectors.joining("", "\n--CACHE CONTENT--\n", "----\n"));
.collect(Collectors.joining("", "\n--CACHE CONTENT--\n", "----"));
}
/**
* Delegate to backing cache store.
* @param userId {@link String}
* @return {@link UserAccount}
*/
public static UserAccount get(String userId) {
public UserAccount get(final String userId) {
return cache.get(userId);
}
/**
* Delegate to backing cache store.
* @param userId {@link String}
* @param userAccount {@link UserAccount}
*/
public static void set(String userId, UserAccount userAccount) {
public void set(final String userId, final UserAccount userAccount) {
cache.set(userId, userAccount);
}
/**
* Delegate to backing cache store.
* @param userId {@link String}
*/
public static void invalidate(String userId) {
public void invalidate(final String userId) {
cache.invalidate(userId);
}
}

View File

@ -32,10 +32,25 @@ import lombok.Getter;
@AllArgsConstructor
@Getter
public enum CachingPolicy {
/**
* Through.
*/
THROUGH("through"),
/**
* AROUND.
*/
AROUND("around"),
/**
* BEHIND.
*/
BEHIND("behind"),
/**
* ASIDE.
*/
ASIDE("aside");
/**
* Policy value.
*/
private final String policy;
}

View File

@ -1,171 +0,0 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.caching;
import com.iluwatar.caching.constants.CachingConstants;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.UpdateOptions;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
/**
* <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>
*/
@Slf4j
public final class DbManager {
private static MongoClient mongoClient;
private static MongoDatabase db;
private static boolean useMongoDB;
private static Map<String, UserAccount> virtualDB;
private DbManager() {
}
/**
* Create DB.
*/
public static void createVirtualDb() {
useMongoDB = false;
virtualDB = new HashMap<>();
}
/**
* Connect to DB.
*/
public static void connect() throws ParseException {
useMongoDB = true;
mongoClient = new MongoClient();
db = mongoClient.getDatabase("test");
}
/**
* Read user account from DB.
*/
public static UserAccount readFromDb(String userId) {
if (!useMongoDB) {
if (virtualDB.containsKey(userId)) {
return virtualDB.get(userId);
}
return null;
}
if (db == null) {
try {
connect();
} catch (ParseException e) {
LOGGER.error("Error connecting to MongoDB", e);
}
}
var iterable = db
.getCollection(CachingConstants.USER_ACCOUNT)
.find(new Document(CachingConstants.USER_ID, userId));
if (iterable == null) {
return null;
}
Document doc = iterable.first();
String userName = doc.getString(CachingConstants.USER_NAME);
String appInfo = doc.getString(CachingConstants.ADD_INFO);
return new UserAccount(userId, userName, appInfo);
}
/**
* Write user account to DB.
*/
public static void writeToDb(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
if (db == null) {
try {
connect();
} catch (ParseException e) {
LOGGER.error("Error connecting to MongoDB", e);
}
}
db.getCollection(CachingConstants.USER_ACCOUNT).insertOne(
new Document(CachingConstants.USER_ID, userAccount.getUserId())
.append(CachingConstants.USER_NAME, userAccount.getUserName())
.append(CachingConstants.ADD_INFO, userAccount.getAdditionalInfo())
);
}
/**
* Update DB.
*/
public static void updateDb(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
if (db == null) {
try {
connect();
} catch (ParseException e) {
LOGGER.error("Error connecting to MongoDB", e);
}
}
db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
new Document(CachingConstants.USER_ID, userAccount.getUserId()),
new Document("$set", new Document(CachingConstants.USER_NAME, userAccount.getUserName())
.append(CachingConstants.ADD_INFO, 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 (db == null) {
try {
connect();
} catch (ParseException e) {
LOGGER.error("Error connecting to MongoDB", e);
}
}
db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
new Document(CachingConstants.USER_ID, userAccount.getUserId()),
new Document("$set",
new Document(CachingConstants.USER_ID, userAccount.getUserId())
.append(CachingConstants.USER_NAME, userAccount.getUserName())
.append(CachingConstants.ADD_INFO, userAccount.getAdditionalInfo())
),
new UpdateOptions().upsert(true)
);
}
}

View File

@ -29,41 +29,83 @@ import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
/**
* 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.
* 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.
*/
@Slf4j
public class LruCache {
/**
* Static class Node.
*/
static class Node {
String userId;
UserAccount userAccount;
Node previous;
Node next;
/**
* user id.
*/
private final String userId;
/**
* User Account.
*/
private UserAccount userAccount;
/**
* previous.
*/
private Node previous;
/**
* next.
*/
private Node next;
public Node(String userId, UserAccount userAccount) {
this.userId = userId;
this.userAccount = userAccount;
/**
* Node definition.
*
* @param id String
* @param account {@link UserAccount}
*/
Node(final String id, final UserAccount account) {
this.userId = id;
this.userAccount = account;
}
}
int capacity;
Map<String, Node> cache = new HashMap<>();
Node head;
Node end;
/**
* Capacity of Cache.
*/
private int capacity;
/**
* Cache {@link HashMap}.
*/
private Map<String, Node> cache = new HashMap<>();
/**
* Head.
*/
private Node head;
/**
* End.
*/
private Node end;
public LruCache(int capacity) {
this.capacity = capacity;
/**
* Constructor.
*
* @param cap Integer.
*/
public LruCache(final int cap) {
this.capacity = cap;
}
/**
* Get user account.
*
* @param userId String
* @return {@link UserAccount}
*/
public UserAccount get(String userId) {
public UserAccount get(final String userId) {
if (cache.containsKey(userId)) {
var node = cache.get(userId);
remove(node);
@ -75,8 +117,10 @@ public class LruCache {
/**
* Remove node from linked list.
*
* @param node {@link Node}
*/
public void remove(Node node) {
public void remove(final Node node) {
if (node.previous != null) {
node.previous.next = node.next;
} else {
@ -91,8 +135,10 @@ public class LruCache {
/**
* Move node to the front of the list.
*
* @param node {@link Node}
*/
public void setHead(Node node) {
public void setHead(final Node node) {
node.next = head;
node.previous = null;
if (head != null) {
@ -106,8 +152,11 @@ public class LruCache {
/**
* Set user account.
*
* @param userAccount {@link UserAccount}
* @param userId {@link String}
*/
public void set(String userId, UserAccount userAccount) {
public void set(final String userId, final UserAccount userAccount) {
if (cache.containsKey(userId)) {
var old = cache.get(userId);
old.userAccount = userAccount;
@ -127,25 +176,43 @@ public class LruCache {
}
}
public boolean contains(String userId) {
/**
* Check if Cache contains the userId.
*
* @param userId {@link String}
* @return boolean
*/
public boolean contains(final String userId) {
return cache.containsKey(userId);
}
/**
* Invalidate cache for user.
*
* @param userId {@link String}
*/
public void invalidate(String userId) {
public void invalidate(final String userId) {
var toBeRemoved = cache.remove(userId);
if (toBeRemoved != null) {
LOGGER.info("# {} has been updated! Removing older version from cache...", userId);
LOGGER.info("# {} has been updated! "
+ "Removing older version from cache...", userId);
remove(toBeRemoved);
}
}
/**
* Check if the cache is full.
* @return boolean
*/
public boolean isFull() {
return cache.size() >= capacity;
}
/**
* Get LRU data.
*
* @return {@link UserAccount}
*/
public UserAccount getLruData() {
return end.userAccount;
}
@ -161,6 +228,8 @@ public class LruCache {
/**
* Returns cache data in list form.
*
* @return {@link List}
*/
public List<UserAccount> getCacheDataInListForm() {
var listOfCacheData = new ArrayList<UserAccount>();
@ -174,10 +243,14 @@ public class LruCache {
/**
* Set cache capacity.
*
* @param newCapacity int
*/
public void setCapacity(int newCapacity) {
public void setCapacity(final int newCapacity) {
if (capacity > newCapacity) {
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll
// Behavior can be modified to accommodate
// for decrease in cache size. For now, we'll
clear();
// just clear the cache.
} else {
this.capacity = newCapacity;

View File

@ -24,19 +24,28 @@
package com.iluwatar.caching;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* Entity class (stored in cache and DB) used in the application.
*/
@Setter
@Getter
@Data
@AllArgsConstructor
@ToString
@EqualsAndHashCode
public class UserAccount {
/**
* User Id.
*/
private String userId;
/**
* User Name.
*/
private String userName;
/**
* Additional Info.
*/
private String additionalInfo;
}

View File

@ -26,11 +26,27 @@ package com.iluwatar.caching.constants;
/**
* Constant class for defining constants.
*/
public class CachingConstants {
public final class CachingConstants {
/**
* User Account.
*/
public static final String USER_ACCOUNT = "user_accounts";
/**
* User ID.
*/
public static final String USER_ID = "userID";
/**
* User Name.
*/
public static final String USER_NAME = "userName";
/**
* Additional Info.
*/
public static final String ADD_INFO = "additionalInfo";
/**
* Constructor.
*/
private CachingConstants() {
}
}

View File

@ -0,0 +1,4 @@
/**
* Constants.
*/
package com.iluwatar.caching.constants;

View File

@ -0,0 +1,52 @@
package com.iluwatar.caching.database;
import com.iluwatar.caching.UserAccount;
/**
* <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>
*/
public interface DbManager {
/**
* Connect to DB.
*/
void connect();
/**
* Disconnect from DB.
*/
void disconnect();
/**
* Read from DB.
*
* @param userId {@link String}
* @return {@link UserAccount}
*/
UserAccount readFromDb(String userId);
/**
* Write to DB.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
UserAccount writeToDb(UserAccount userAccount);
/**
* Update record.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
UserAccount updateDb(UserAccount userAccount);
/**
* Update record or Insert if not exists.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
UserAccount upsertDb(UserAccount userAccount);
}

View File

@ -0,0 +1,25 @@
package com.iluwatar.caching.database;
/**
* Creates the database connection accroding the input parameter.
*/
public final class DbManagerFactory {
/**
* Private constructor.
*/
private DbManagerFactory() {
}
/**
* Init database.
*
* @param isMongo boolean
* @return {@link DbManager}
*/
public static DbManager initDb(final boolean isMongo) {
if (isMongo) {
return new MongoDb();
}
return new VirtualDb();
}
}

View File

@ -0,0 +1,128 @@
package com.iluwatar.caching.database;
import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
import static com.iluwatar.caching.constants.CachingConstants.USER_ACCOUNT;
import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
import com.iluwatar.caching.UserAccount;
import com.iluwatar.caching.constants.CachingConstants;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.UpdateOptions;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
/**
* Implementation of DatabaseManager.
* implements base methods to work with MongoDb.
*/
@Slf4j
public class MongoDb implements DbManager {
private static final String DATABASE_NAME = "admin";
private static final String MONGO_USER = "root";
private static final String MONGO_PASSWORD = "rootpassword";
private MongoClient client;
private MongoDatabase db;
/**
* Connect to Db. Check th connection
*/
@Override
public void connect() {
MongoCredential mongoCredential = MongoCredential.createCredential(MONGO_USER,
DATABASE_NAME,
MONGO_PASSWORD.toCharArray());
MongoClientOptions options = MongoClientOptions.builder().build();
client = new MongoClient(new ServerAddress(), List.of(mongoCredential), options);
db = client.getDatabase(DATABASE_NAME);
}
@Override
public void disconnect() {
client.close();
}
/**
* Read data from DB.
*
* @param userId {@link String}
* @return {@link UserAccount}
*/
@Override
public UserAccount readFromDb(final String userId) {
var iterable = db
.getCollection(CachingConstants.USER_ACCOUNT)
.find(new Document(USER_ID, userId));
if (iterable.first() == null) {
return null;
}
Document doc = iterable.first();
if (doc != null) {
String userName = doc.getString(USER_NAME);
String appInfo = doc.getString(ADD_INFO);
return new UserAccount(userId, userName, appInfo);
} else {
return null;
}
}
/**
* Write data to DB.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
@Override
public UserAccount writeToDb(final UserAccount userAccount) {
db.getCollection(USER_ACCOUNT).insertOne(
new Document(USER_ID, userAccount.getUserId())
.append(USER_NAME, userAccount.getUserName())
.append(ADD_INFO, userAccount.getAdditionalInfo())
);
return userAccount;
}
/**
* Update DB.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
@Override
public UserAccount updateDb(final UserAccount userAccount) {
Document id = new Document(USER_ID, userAccount.getUserId());
Document dataSet = new Document(USER_NAME, userAccount.getUserName())
.append(ADD_INFO, userAccount.getAdditionalInfo());
db.getCollection(CachingConstants.USER_ACCOUNT)
.updateOne(id, new Document("$set", dataSet));
return userAccount;
}
/**
* Update data if exists.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
@Override
public UserAccount upsertDb(final UserAccount userAccount) {
String userId = userAccount.getUserId();
String userName = userAccount.getUserName();
String additionalInfo = userAccount.getAdditionalInfo();
db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
new Document(USER_ID, userId),
new Document("$set",
new Document(USER_ID, userId)
.append(USER_NAME, userName)
.append(ADD_INFO, additionalInfo)
),
new UpdateOptions().upsert(true)
);
return userAccount;
}
}

View File

@ -0,0 +1,78 @@
package com.iluwatar.caching.database;
import com.iluwatar.caching.UserAccount;
import java.util.HashMap;
import java.util.Map;
/**
* Implementation of DatabaseManager.
* implements base methods to work with hashMap as database.
*/
public class VirtualDb implements DbManager {
/**
* Virtual DataBase.
*/
private Map<String, UserAccount> db;
/**
* Creates new HashMap.
*/
@Override
public void connect() {
db = new HashMap<>();
}
@Override
public void disconnect() {
db = null;
}
/**
* Read from Db.
*
* @param userId {@link String}
* @return {@link UserAccount}
*/
@Override
public UserAccount readFromDb(final String userId) {
if (db.containsKey(userId)) {
return db.get(userId);
}
return null;
}
/**
* Write to DB.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
@Override
public UserAccount writeToDb(final UserAccount userAccount) {
db.put(userAccount.getUserId(), userAccount);
return userAccount;
}
/**
* Update reecord in DB.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
@Override
public UserAccount updateDb(final UserAccount userAccount) {
return writeToDb(userAccount);
}
/**
* Update.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
@Override
public UserAccount upsertDb(final UserAccount userAccount) {
return updateDb(userAccount);
}
}

View File

@ -0,0 +1,4 @@
/**
* Database classes.
*/
package com.iluwatar.caching.database;

View File

@ -0,0 +1,20 @@
/**
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.caching;

View File

@ -25,25 +25,21 @@ package com.iluwatar.caching;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
/**
* Tests that Caching example runs without errors.
*/
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
* <p>
* Solution: Inserted assertion to check whether the execution of the main method in {@link App}
* throws an exception.
*/
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}

View File

@ -23,11 +23,11 @@
package com.iluwatar.caching;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* Application test
*/
@ -43,32 +43,30 @@ class CachingTest {
// 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.initDb(false);
AppManager.initCacheCapacity(3);
app = new App();
app = new App(false);
}
@Test
void testReadAndWriteThroughStrategy() {
assertNotNull(app);
assertNotNull(app);
app.useReadAndWriteThroughStrategy();
}
@Test
void testReadThroughAndWriteAroundStrategy() {
assertNotNull(app);
assertNotNull(app);
app.useReadThroughAndWriteAroundStrategy();
}
@Test
void testReadThroughAndWriteBehindStrategy() {
assertNotNull(app);
assertNotNull(app);
app.useReadThroughAndWriteBehindStrategy();
}
@Test
void testCacheAsideStrategy() {
assertNotNull(app);
assertNotNull(app);
app.useCacheAsideStategy();
}
}

View File

@ -0,0 +1,84 @@
package com.iluwatar.caching.database;
import com.iluwatar.caching.UserAccount;
import com.iluwatar.caching.constants.CachingConstants;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.internal.util.reflection.Whitebox;
import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
class MongoDbTest {
private static final String ID = "123";
private static final String NAME = "Some user";
private static final String ADDITIONAL_INFO = "Some app Info";
@Mock
MongoDatabase db;
private MongoDb mongoDb = new MongoDb();
private UserAccount userAccount;
@BeforeEach
void init() {
db = mock(MongoDatabase.class);
Whitebox.setInternalState(mongoDb, "db", db);
userAccount = new UserAccount(ID, NAME, ADDITIONAL_INFO);
}
@Test
void connect() {
assertDoesNotThrow(() -> mongoDb.connect());
}
@Test
void readFromDb() {
Document document = new Document(USER_ID, ID)
.append(USER_NAME, NAME)
.append(ADD_INFO, ADDITIONAL_INFO);
MongoCollection<Document> mongoCollection = mock(MongoCollection.class);
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
FindIterable<Document> findIterable = mock(FindIterable.class);
when(mongoCollection.find(any(Document.class))).thenReturn(findIterable);
when(findIterable.first()).thenReturn(document);
assertEquals(mongoDb.readFromDb(ID),userAccount);
}
@Test
void writeToDb() {
MongoCollection<Document> mongoCollection = mock(MongoCollection.class);
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
doNothing().when(mongoCollection).insertOne(any(Document.class));
assertDoesNotThrow(()-> {mongoDb.writeToDb(userAccount);});
}
@Test
void updateDb() {
MongoCollection<Document> mongoCollection = mock(MongoCollection.class);
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
assertDoesNotThrow(()-> {mongoDb.updateDb(userAccount);});
}
@Test
void upsertDb() {
MongoCollection<Document> mongoCollection = mock(MongoCollection.class);
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
assertDoesNotThrow(()-> {mongoDb.upsertDb(userAccount);});
}
}

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Callback
folder: callback
permalink: /patterns/callback/
categories: Idiom
category: Idiom
language: en
tags:
- Reactive

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Chain of responsibility
folder: chain-of-responsibility
permalink: /patterns/chain-of-responsibility/
categories: Behavioral
category: Behavioral
language: en
tags:
- Gang of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Circuit Breaker
folder: circuit-breaker
permalink: /patterns/circuit-breaker/
categories: Behavioral
category: Behavioral
language: en
tags:
- Performance

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Static Content Hosting
folder: cloud-static-content-hosting
permalink: /patterns/cloud-static-content-hosting/
categories: Cloud
category: Cloud
language: en
tags:
- Cloud distributed

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Collection Pipeline
folder: collection-pipeline
permalink: /patterns/collection-pipeline/
categories: Functional
category: Functional
language: en
tags:
- Reactive

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Combinator
folder: combinator
permalink: /patterns/combinator/
categories: Idiom
category: Idiom
language: en
tags:
- Reactive

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Command
folder: command
permalink: /patterns/command/
categories: Behavioral
category: Behavioral
language: en
tags:
- Gang of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Commander
folder: commander
permalink: /patterns/commander/
categories: Concurrency
category: Concurrency
language: en
tags:
- Cloud distributed

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Composite Entity
folder: composite-entity
permalink: /patterns/composite-entity/
categories: Structural
category: Structural
language: en
tags:
- Enterprise Integration Pattern

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Composite
folder: composite
permalink: /patterns/composite/
categories: Structural
category: Structural
language: en
tags:
- Gang of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Converter
folder: converter
permalink: /patterns/converter/
categories: Creational
category: Creational
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: CQRS
folder: cqrs
permalink: /patterns/cqrs/
categories: Architectural
category: Architectural
language: en
tags:
- Performance

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Data Access Object
folder: dao
permalink: /patterns/dao/
categories: Architectural
category: Architectural
language: en
tags:
- Data access

View File

@ -1,10 +1,6 @@
---
layout: pattern
title: Data Bus
folder: data-bus
permalink: /patterns/data-bus/
categories: Architectural
category: Architectural
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Data Locality
folder: data-locality
permalink: /patterns/data-locality/
categories: Behavioral
category: Behavioral
language: en
tags:
- Game programming

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Data Mapper
folder: data-mapper
permalink: /patterns/data-mapper/
categories: Architectural
category: Architectural
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Data Transfer Object
folder: data-transfer-object
permalink: /patterns/data-transfer-object/
categories: Architectural
category: Architectural
language: en
tags:
- Performance

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Decorator
folder: decorator
permalink: /patterns/decorator/
categories: Structural
category: Structural
language: en
tags:
- Gang of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Delegation
folder: delegation
permalink: /patterns/delegation/
categories: Structural
category: Structural
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Dependency Injection
folder: dependency-injection
permalink: /patterns/dependency-injection/
categories: Creational
category: Creational
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Dirty Flag
folder: dirty-flag
permalink: /patterns/dirty-flag/
categories: Behavioral
category: Behavioral
language: en
tags:
- Game programming

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Domain Model
folder: domain-model
permalink: /patterns/domain-model/
categories: Architectural
category: Architectural
language: en
tags:
- Domain

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Double Buffer
folder: double-buffer
permalink: /patterns/double-buffer/
categories: Behavioral
category: Behavioral
language: en
tags:
- Performance

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Double Checked Locking
folder: double-checked-locking
permalink: /patterns/double-checked-locking/
categories: Idiom
category: Idiom
language: en
tags:
- Performance

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Double Dispatch
folder: double-dispatch
permalink: /patterns/double-dispatch/
categories: Idiom
category: Idiom
language: en
tags:
- Extensibility

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: EIP Aggregator
folder: eip-aggregator
permalink: /patterns/eip-aggregator/
categories: Integration
category: Integration
language: en
tags:
- Enterprise Integration Pattern

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: EIP Message Channel
folder: eip-message-channel
permalink: /patterns/eip-message-channel/
categories: Integration
category: Integration
language: en
tags:
- Enterprise Integration Pattern

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: EIP Publish Subscribe
folder: eip-publish-subscribe
permalink: /patterns/eip-publish-subscribe/
categories: Integration
category: Integration
language: en
tags:
- Enterprise Integration Pattern

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: EIP Splitter
folder: eip-splitter
permalink: /patterns/eip-splitter/
categories: Integration
category: Integration
language: en
tags:
- Enterprise Integration Pattern

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: EIP Wire Tap
folder: eip-wire-tap
permalink: /patterns/eip-wire-tap/
categories: Integration
category: Integration
language: en
tags:
- Enterprise Integration Pattern

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Event Aggregator
folder: event-aggregator
permalink: /patterns/event-aggregator/
categories: Structural
category: Structural
language: en
tags:
- Reactive

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Event-based Asynchronous
folder: event-asynchronous
permalink: /patterns/event-asynchronous/
categories: Concurrency
category: Concurrency
language: en
tags:
- Reactive

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Event Driven Architecture
folder: event-driven-architecture
permalink: /patterns/event-driven-architecture/
categories: Architectural
category: Architectural
language: en
tags:
- Reactive

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Event Queue
folder: event-queue
permalink: /patterns/event-queue/
categories: Concurrency
category: Concurrency
language: en
tags:
- Game programming

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Event Sourcing
folder: event-sourcing
permalink: /patterns/event-sourcing/
categories: Architectural
category: Architectural
language: en
tags:
- Performance

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Execute Around
folder: execute-around
permalink: /patterns/execute-around/
categories: Idiom
category: Idiom
language: en
tags:
- Extensibility

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Extension objects
folder: extension-objects
permalink: /patterns/extension-objects/
categories: Behavioral
category: Behavioral
language: en
tags:
- Extensibility

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Facade
folder: facade
permalink: /patterns/facade/
categories: Structural
category: Structural
language: en
tags:
- Gang Of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Factory Kit
folder: factory-kit
permalink: /patterns/factory-kit/
categories: Creational
category: Creational
language: en
tags:
- Extensibility

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Factory Method
folder: factory-method
permalink: /patterns/factory-method/
categories: Creational
category: Creational
language: en
tags:
- Extensibility

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Factory
folder: factory
permalink: /patterns/factory/
categories: Creational
category: Creational
language: en
tags:
- Gang of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Fan-Out/Fan-In
folder: fanout-fanin
permalink: /patterns/fanout-fanin/
categories: Integration
category: Integration
language: en
tags:
- Microservices

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Feature Toggle
folder: feature-toggle
permalink: /patterns/feature-toggle/
categories: Behavioral
category: Behavioral
language: en
tags:
- Extensibility

View File

@ -1,12 +1,8 @@
---
layout: pattern
title: Filterer
folder: filterer
permalink: /patterns/filterer/
description: Design pattern that helps container-like objects to return filtered version of themselves.# short meta description that shows in Google search results
language: en
categories:
- Functional
category: Functional
tags:
- Extensibility
---

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Fluent Interface
folder: fluentinterface
permalink: /patterns/fluentinterface/
categories: Functional
category: Functional
language: en
tags:
- Reactive

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Flux
folder: flux
permalink: /patterns/flux/
categories: Structural
category: Structural
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Flyweight
folder: flyweight
permalink: /patterns/flyweight/
categories: Structural
category: Structural
language: en
tags:
- Gang of Four

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Front Controller
folder: front-controller
permalink: /patterns/front-controller/
categories: Structural
category: Structural
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Game Loop
folder: game-loop
permalink: /patterns/game-loop/
categories: Behavioral
category: Behavioral
language: en
tags:
- Game programming

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Guarded Suspension
folder: guarded-suspension
permalink: /patterns/guarded-suspension/
categories: Concurrency
category: Concurrency
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Half-Sync/Half-Async
folder: half-sync-half-async
permalink: /patterns/half-sync-half-async/
categories: Concurrency
category: Concurrency
language: en
tags:
- Performance

View File

@ -1,10 +1,6 @@
---
layout: pattern
title: Hexagonal Architecture
folder: hexagonal
permalink: /patterns/hexagonal/
pumlformat: svg
categories: Architectural
category: Architectural
language: en
tags:
- Decoupling

147
index.md Normal file
View File

@ -0,0 +1,147 @@
| Pattern | Category | Tags |
| :--- | :--- | :--- |
| [Abstract Document](abstract-document) | Structural | Extensibility |
| [Abstract Factory](abstract-factory) | Creational | Gang of Four |
| [Active Object](active-object) | Concurrency | Performance |
| [Acyclic Visitor](acyclic-visitor) | Behavioral | Extensibility |
| [Adapter](adapter) | Structural | Gang of Four |
| [Aggregator Microservices](aggregator-microservices) | Architectural | Cloud distributed |
| [Ambassador](ambassador) | Structural | Decoupling |
| [API Gateway](api-gateway) | Architectural | Cloud distributed, Decoupling |
| [Arrange/Act/Assert](arrange-act-assert) | Idiom | Testing |
| [Async Method Invocation](async-method-invocation) | Concurrency | Reactive |
| [Balking](balking) | Concurrency | Decoupling |
| [Bridge](bridge) | Structural | Gang of Four |
| [Builder](builder) | Creational | Gang of Four |
| [Business Delegate](business-delegate) | Structural | Decoupling |
| [Bytecode](bytecode) | Behavioral | Game programming |
| [Caching](caching) | Behavioral | Performance |
| [Callback](callback) | Idiom | Reactive |
| [Chain of Responsibility](chain-of-responsibility) | Behavioral | Gang of Four |
| [Circuit Breaker](circuit-breaker) | Behavioral | Performance, Decoupling |
| [Cloud Static Content Hosting](cloud-static-content-hosting) | Cloud | Cloud distributed |
| [Collection Pipeline](collection-pipeline) | Functional | Reactive |
| [Combinator](combinator) | Idiom | Reactive |
| [Command](command) | Behavioral | Gang of Four |
| [Commander](commander) | Concurrency | Cloud distributed |
| [Composite](composite) | Structural | Gang of Four |
| [Composite Entity](composite-entity) | Structural | Enterprise Integration Pattern |
| [Converter](converter) | Creational | Decoupling |
| [Command Query Responsibility Segregation](command-query-responsibility-segregation) | Architectural | Performance, Cloud distributed |
| [Data Access Object](data-access-object) | Architectural | Data access |
| [Data Bus](data-bus) | Architectural | Decoupling |
| [Data Locality](data-locality) | Behavioral | Performance, Game programming |
| [Data Mapper](data-mapper) | Architectural | Decoupling |
| [Data Transfer Object](data-transfer-object) | Architectural | Performance |
| [Decorator](decorator) | Structural | Gang of Four, Extensibility |
| [Delegation](delegation) | Structural | Decoupling |
| [Dependency Injection](dependency-injection) | Creational | Decoupling |
| [Dirty Flag](dirty-flag) | Behavioral | Performance, Game programming |
| [Double Buffer](double-buffer) | Behavioral | Performance, Game programming |
| [Double Checked Locking](double-checked-locking) | Idiom | Performance |
| [Double Dispatch](double-dispatch) | Idiom | Extensibility |
| [EIP Aggregator](eip-aggregator) | Integration | Enterprise Integration Pattern |
| [EIP Message Channel](eip-message-channel) | Integration | Enterprise Integration Pattern |
| [EIP Publish and Subscribe](eip-publish-and-subscribe) | Integration | Enterprise Integration Pattern |
| [EIP Splitter](eip-splitter) | Integration | Enterprise Integration Pattern |
| [EIP Wire Tap](eip-wire-tap) | Integration | Enterprise Integration Pattern |
| [Event Aggregator](event-aggregator) | Structural | Reactive |
| [Event Based Asynchronous](event-based-asynchronous) | Concurrency | Reactive |
| [Event Driven Architecture](event-driven-architecture) | Architectural | Reactive |
| [Event Queue](event-queue) | Concurrency | Game programming |
| [Event Sourcing](event-sourcing) | Architectural | Performance |
| [Execute Around](execute-around) | Idiom | Extensibility |
| [Extension Objects](extension-objects) | Behavioral | Extensibility |
| [Facade](facade) | Structural | Gang of Four, Decoupling |
| [Factory](factory) | Creational | Gang of Four |
| [Factory kit](factory-kit) | Creational | Extensibility |
| [Factory Method](factory-method) | Creational | Gang of Four, Extensibility |
| [Fan-Out/Fan-In](fanout-fanin) | Integration | Microservices |
| [Feature Toggle](feature-toggle) | Behavioral | Extensibility |
| [Filterer](filterer) | Functional | Extensibility |
| [Fluent Interface](fluent-interface) | Functional | Reactive |
| [Flux](flux) | Structural | Decoupling |
| [Flyweight](flyweight) | Structural | Gang of Four, Performance |
| [Front Controller](front-controller) | Structural | Decoupling |
| [Game Loop](game-loop) | Behavioral | Game programming |
| [Guarded Suspension](guarded-suspension) | Concurrency | Decoupling |
| [Half-Sync Half-Async](half-sync-half-async) | Concurrency | Performance |
| [Hexagonal](hexagonal) | Architectural | Decoupling |
| [Intercepting Filter](intercepting-filter) | Behavioral | Decoupling |
| [Interpreter](interpreter) | Behavioral | Gang of Four |
| [Iterator](iterator) | Behavioral | Gang of Four |
| [Layers](layers) | Architectural | Decoupling |
| [Lazy Loading](lazy-loading) | Idiom | Performance |
| [Leader Election](leader-election) | Behavioral | Cloud distributed |
| [Leader Followers](leader-followers) | Concurrency | Performance |
| [Lockable Object](lockable-object) | Concurrency | Performance |
| [Marker Interface](marker-interface) | Structural | Decoupling |
| [Master Worker](master-worker) | Concurrency | Performance |
| [Mediator](mediator) | Behavioral | Gang of Four, Decoupling |
| [Memento](memento) | Behavioral | Gang of Four |
| [Model View Controller](model-view-controller) | Architectural | Decoupling |
| [Model View Presenter](model-view-presenter) | Architectural | Decoupling |
| [Model View ViewModel](model-view-viewmodel) | Architectural | Decoupling |
| [Module](module) | Structural | Decoupling |
| [Monad](monad) | Functional | Reactive |
| [Monostate](monostate) | Creational | Instantiation |
| [Multiton](multiton) | Creational | Instantiation |
| [Mute Idiom](mute-idiom) | Idiom | Decoupling |
| [Naked Objects](naked-objects) | Architectural | Decoupling |
| [Null Object](null-object) | Behavioral | Extensibility |
| [Object Mother](object-mother) | Creational | Instantiation |
| [Object Pool](object-pool) | Creational | Performance, Game programming |
| [Observer](observer) | Behavioral | Gang of Four, Reactive |
| [Page Object](page-object) | Structural | Decoupling |
| [Parameter Object](parameter-object) | Behavioral | Extensibility |
| [Partial Response](partial-response) | Behavioral | Decoupling |
| [Pipeline](pipeline) | Behavioral | Decoupling |
| [Poison Pill](poison-pill) | Behavioral | Cloud distributed, Reactive |
| [Presentation Model](presentation-model) | Behavioral | Decoupling |
| [Priority Queue](priority-queue) | Behavioral | Decoupling |
| [Private Class Data](private-class-data) | Idiom | Data access |
| [Producer Consumer](producer-consumer) | Concurrency | Reactive |
| [Promise](promise) | Concurrency | Reactive |
| [Property](property) | Creational | Instantiation |
| [Prototype](prototype) | Creational | Gang of Four, Instantiation |
| [Proxy](proxy) | Structural | Gang of Four, Decoupling |
| [Queue Based Load Leveling](queue-based-load-leveling) | Concurrency | Performance, Decoupling |
| [Reactor](reactor) | Concurrency | Performance, Reactive |
| [Reader Writer Lock](reader-writer-lock) | Concurrency | Performance |
| [Registry](registry) | Creational | Instantiation |
| [Repository](repository) | Architectural | Data access |
| [Resource Acquisition Is Initialization](resource-acquisition-is-initialization) | Idiom | Data access |
| [Retry](retry) | Behavioral | Performance |
| [Role Object](role-object) | Structural | Extensibility |
| [Saga](saga) | Concurrency | Cloud distributed |
| [Semaphore](semaphore) | Concurrency | Performance |
| [Separated Interface](separated-interface) | Structural | Decoupling |
| [Servant](servant) | Behavioral | Decoupling |
| [Serverless](serverless) | Architectural | Cloud distributed |
| [Service Layer](service-layer) | Architectural | Data access |
| [Service Locator](service-locator) | Architectural | Performance, Game programming |
| [Sharding](sharding) | Behavioral | Performance, Cloud distributed |
| [Singleton](singleton) | Creational | Gang of Four |
| [Spatial Partition](spatial-partition) | Behavioral | Performance, Game programming |
| [Special Case](special-case) | Behavioral | Extensibility |
| [Specification](specification) | Behavioral | Data access |
| [State](state) | Behavioral | Gang of Four |
| [Step Builder](step-builder) | Creational | Instantiation |
| [Strangler](strangler) | Structural | Cloud distributed, Extensibility |
| [Strategy](strategy) | Behavioral | Gang of Four |
| [Subclass Sandbox](subclass-sandbox) | Behavioral | Game programming |
| [Table Module](table-module) | Structural | Data access |
| [Template Method](table-module) | Behavioral | Gang of Four |
| [Thread Pool](thread-pool) | Concurrency | Performance |
| [Throttling](throttling) | Behavioral | Performance |
| [Thread Local Storage](thread-local-storage) | Idiom | Performance |
| [Tolerant Reader](tolerant-reader) | Integration | Decoupling |
| [Trampoline](trampoline) | Behavioral | Performance |
| [Transaction Script](transaction-script) | Behavioral | Data access |
| [Twin](twin) | Structural | Extensibility |
| [Type Object](type-object) | Behavioral | Game programming, Extensibility |
| [Unit of Work](unit-of-work) | Architectural | Data access |
| [Update Method](update-method) | Behavioral | Game programming |
| [Value Object](value-object) | Creational | Instantiation |
| [Version Number](version-number) | Concurrency | Data access, Microservices |
| [Visitor](visitor) | Behavioral | Gang of Four |

View File

@ -1,8 +1,5 @@
---
layout: pattern
title: Intercepting Filter
folder: intercepting-filter
permalink: /patterns/intercepting-filter/
categories: Behavioral
language: en
tags:

View File

@ -1,8 +1,5 @@
---
layout: pattern
title: Interpreter
folder: interpreter
permalink: /patterns/interpreter/
categories: Behavioral
language: en
tags:

View File

@ -1,8 +1,5 @@
---
layout: pattern
title: Iterator
folder: iterator
permalink: /patterns/iterator/
categories: Behavioral
language: en
tags:

View File

@ -1,10 +1,6 @@
---
layout: pattern
title: Layers
folder: layers
permalink: /patterns/layers/
pumlformat: svg
categories: Architectural
category: Architectural
language: en
tags:
- Decoupling

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Lazy Loading
folder: lazy-loading
permalink: /patterns/lazy-loading/
categories: Idiom
category: Idiom
language: en
tags:
- Performance

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Leader Election
folder: leader-election
permalink: /patterns/leader-election/
categories: Behavioral
category: Behavioral
language: en
tags:
- Cloud distributed

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Leader/Followers
folder: leader-followers
permalink: /patterns/leader-followers/
categories: Concurrency
category: Concurrency
language: en
tags:
- Performance

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Adapter
folder: adapter
permalink: /patterns/adapter/ko
categories: Structural
category: Structural
language: ko
tags:
- Gang of Four
@ -96,7 +93,7 @@ captain.row();
## 클레스 다이어그램
![alt text](https://github.com/warp125/java-design-patterns/blob/master/adapter/etc/adapter.urm.png?raw=true)
![alt text](etc/adapter.urm.png)
## 적용 가능성

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,9 +1,6 @@
---
layout: pattern
title: Factory
folder: factory
permalink: /patterns/factory/ko
categories: Creational
category: Creational
language: ko
tags:
- Gang of Four
@ -107,7 +104,7 @@ This Ferrari.
## 클래스 다이어그램
![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/factory/etc/factory.urm.png)
![alt text](etc/factory.urm.png)
## 적용 가능성

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

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