Compare commits

..

4 Commits

Author SHA1 Message Date
3af7b3f316 docs: update .all-contributorsrc [skip ci] 2021-10-13 07:46:56 +00:00
36336cc04b docs: update README.md [skip ci] 2021-10-13 07:46:55 +00:00
5a644f1092 refactoring: Critical Sonar Issues (#1833)
* Resolve Sonar Code Smell: Define a constant instead of duplicating this literal 'Space rocket <' 4 times.

* Resolve Sonar Critical Code Smell: Define a constant instead of duplicating this literal 'Error connecting to MongoDB' 4 times.

* Fix checkstyle violation.

* Resolve Sonar Critical Code Smell: Define a constant instead of duplicating this literal 'LITERAL 0' 4 times.

Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2021-10-13 13:15:22 +05:30
cab9048e06 docs: fixes for yaml frontmatter (#1851)
Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2021-10-13 12:59:55 +05:30
286 changed files with 1376 additions and 1417 deletions

View File

@ -1631,6 +1631,15 @@
"contributions": [ "contributions": [
"translation" "translation"
] ]
},
{
"login": "sims-keshri",
"name": "Simran Keshri",
"avatar_url": "https://avatars.githubusercontent.com/u/62168475?v=4",
"profile": "https://github.com/sims-keshri",
"contributions": [
"code"
]
} }
], ],
"contributorsPerLine": 4, "contributorsPerLine": 4,

View File

@ -10,7 +10,7 @@
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=coverage)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=coverage)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
[![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --> <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-179-orange.svg?style=flat-square)](#contributors-) [![All Contributors](https://img.shields.io/badge/all_contributors-180-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END --> <!-- ALL-CONTRIBUTORS-BADGE:END -->
<br/> <br/>
@ -343,6 +343,7 @@ This project is licensed under the terms of the MIT license.
<td align="center"><a href="https://github.com/Conhan93"><img src="https://avatars.githubusercontent.com/u/71334757?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Conny Hansson</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Conhan93" title="Documentation">📖</a></td> <td align="center"><a href="https://github.com/Conhan93"><img src="https://avatars.githubusercontent.com/u/71334757?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Conny Hansson</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Conhan93" title="Documentation">📖</a></td>
<td align="center"><a href="http://muklasr.medium.com"><img src="https://avatars.githubusercontent.com/u/43443753?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muklas Rahmanto</b></sub></a><br /><a href="#translation-muklasr" title="Translation">🌍</a></td> <td align="center"><a href="http://muklasr.medium.com"><img src="https://avatars.githubusercontent.com/u/43443753?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muklas Rahmanto</b></sub></a><br /><a href="#translation-muklasr" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/VxDxK"><img src="https://avatars.githubusercontent.com/u/38704817?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vadim</b></sub></a><br /><a href="#translation-VxDxK" title="Translation">🌍</a></td> <td align="center"><a href="https://github.com/VxDxK"><img src="https://avatars.githubusercontent.com/u/38704817?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vadim</b></sub></a><br /><a href="#translation-VxDxK" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/sims-keshri"><img src="https://avatars.githubusercontent.com/u/62168475?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Simran Keshri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=sims-keshri" title="Code">💻</a></td>
</tr> </tr>
</table> </table>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -59,9 +59,12 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class App { public class App {
private static final String ROCKET_LAUNCH_LOG_PATTERN = "Space rocket <%s> launched successfully";
/** /**
* Program entry point. * Program entry point.
*/ */
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks // construct a new executor that will run async tasks
var executor = new ThreadAsyncExecutor(); var executor = new ThreadAsyncExecutor();
@ -87,9 +90,9 @@ public class App {
asyncResult5.await(); asyncResult5.await();
// log the results of the tasks, callbacks log immediately when complete // log the results of the tasks, callbacks log immediately when complete
log("Space rocket <" + result1 + "> launch complete"); log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result1));
log("Space rocket <" + result2 + "> launch complete"); log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result2));
log("Space rocket <" + result3 + "> launch complete"); log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result3));
} }
/** /**
@ -102,7 +105,7 @@ public class App {
private static <T> Callable<T> lazyval(T value, long delayMillis) { private static <T> Callable<T> lazyval(T value, long delayMillis) {
return () -> { return () -> {
Thread.sleep(delayMillis); Thread.sleep(delayMillis);
log("Space rocket <" + value + "> launched successfully"); log(String.format(ROCKET_LAUNCH_LOG_PATTERN, value));
return value; return value;
}; };
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,6 +42,14 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class App { public class App {
private static final String LITERAL_0 = "LITERAL 0";
private static final String HEALTH_PATTERN = "%s_HEALTH";
private static final String GET_AGILITY = "GET_AGILITY";
private static final String GET_WISDOM = "GET_WISDOM";
private static final String ADD = "ADD";
private static final String LITERAL_2 = "LITERAL 2";
private static final String DIVIDE = "DIVIDE";
/** /**
* Main app method. * Main app method.
* *
@ -53,17 +61,17 @@ public class App {
new Wizard(45, 7, 11, 0, 0), new Wizard(45, 7, 11, 0, 0),
new Wizard(36, 18, 8, 0, 0)); new Wizard(36, 18, 8, 0, 0));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH")); vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "GET")));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY")); vm.execute(InstructionConverterUtil.convertToByteCode(GET_AGILITY));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM")); vm.execute(InstructionConverterUtil.convertToByteCode(GET_WISDOM));
vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); vm.execute(InstructionConverterUtil.convertToByteCode(ADD));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2")); vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_2));
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE")); vm.execute(InstructionConverterUtil.convertToByteCode(DIVIDE));
vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); vm.execute(InstructionConverterUtil.convertToByteCode(ADD));
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH")); vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "SET")));
} }
} }

View File

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

View File

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

View File

@ -1,62 +1,58 @@
/*
* 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; package com.iluwatar.caching;
import com.iluwatar.caching.database.DbManager;
import com.iluwatar.caching.database.DbManagerFactory;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
* The Caching pattern describes how to avoid expensive re-acquisition of * The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing
* resources by not releasing the resources immediately after their use. * the resources immediately after their use. The resources retain their identity, are kept in some
* The resources retain their identity, are kept in some fast-access storage, * fast-access storage, and are re-used to avoid having to acquire them again. There are four main
* 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;
* caching strategies/techniques in this pattern; each with their own pros and * <code>write-through</code> which writes data to the cache and DB in a single transaction,
* cons. They are <code>write-through</code> which writes data to the cache and * <code>write-around</code> which writes data immediately into the DB instead of the cache,
* DB in a single transaction, <code>write-around</code> which writes data * <code>write-behind</code> which writes data into the cache initially whilst the data is only
* immediately into the DB instead of the cache, <code>write-behind</code> * written into the DB when the cache is full, and <code>cache-aside</code> which pushes the
* which writes data into the cache initially whilst the data is only * responsibility of keeping the data synchronized in both data sources to the application itself.
* written into the DB when the cache is full, and <code>cache-aside</code> * The <code>read-through</code> strategy is also included in the mentioned four strategies --
* which pushes the responsibility of keeping the data synchronized in both * returns data from the cache to the caller <b>if</b> it exists <b>else</b> queries from DB and
* data sources to the application itself. The <code>read-through</code> * stores it into the cache for future use. These strategies determine when the data in the cache
* strategy is also included in the mentioned four strategies -- * should be written back to the backing store (i.e. Database) and help keep both data sources
* returns data from the cache to the caller <b>if</b> it exists <b>else</b> * synchronized/up-to-date. This pattern can improve performance and also helps to maintain
* queries from DB and stores it into the cache for future use. These strategies * consistency between data held in the cache and the data in the underlying data store.
* 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 * <p>In this example, the user account ({@link UserAccount}) entity is used as the underlying
* as the underlying application data. The cache itself is implemented as an * application data. The cache itself is implemented as an internal (Java) data structure. It adopts
* internal (Java) data structure. It adopts a Least-Recently-Used (LRU) * a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The four
* 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
* strategies are individually tested. The testing of the cache is restricted * querying of user accounts from the underlying data store ( {@link DbManager}). The main class (
* towards saving and querying of user accounts from the * {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
* underlying data store( {@link DbManager}). The main class ( {@link App} * whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
* is not aware of the underlying mechanics of the application * ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
* (i.e. save and query) and whether the data is coming from the cache or the * (depending on the preferred caching policy/strategy).
* 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> * <p>
* <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> * <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager} </i>
* 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> * </p>
* *
* @see CacheStore * @see CacheStore
@ -65,67 +61,23 @@ import lombok.extern.slf4j.Slf4j;
*/ */
@Slf4j @Slf4j
public class App { 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. * Program entry point.
* *
* @param args command line args * @param args command line args
*/ */
public static void main(final String[] args) { public static void main(String[] args) {
// VirtualDB (instead of MongoDB) was used in running the JUnit tests AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
// and the App class to avoid Maven compilation errors. Set flag to // and the App class to avoid Maven compilation errors. Set flag to
// true to run the tests with MongoDB (provided that MongoDB is // true to run the tests with MongoDB (provided that MongoDB is
// installed and socket connection is open). // installed and socket connection is open).
boolean isDbMongo = isDbMongo(args); AppManager.initCacheCapacity(3);
if (isDbMongo) { var app = new App();
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(); app.useReadAndWriteThroughStrategy();
String splitLine = "==============================================";
LOGGER.info(splitLine);
app.useReadThroughAndWriteAroundStrategy(); app.useReadThroughAndWriteAroundStrategy();
LOGGER.info(splitLine);
app.useReadThroughAndWriteBehindStrategy(); app.useReadThroughAndWriteBehindStrategy();
LOGGER.info(splitLine);
app.useCacheAsideStategy(); 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;
} }
/** /**
@ -133,14 +85,14 @@ public class App {
*/ */
public void useReadAndWriteThroughStrategy() { public void useReadAndWriteThroughStrategy() {
LOGGER.info("# CachingPolicy.THROUGH"); LOGGER.info("# CachingPolicy.THROUGH");
appManager.initCachingPolicy(CachingPolicy.THROUGH); AppManager.initCachingPolicy(CachingPolicy.THROUGH);
var userAccount1 = new UserAccount("001", "John", "He is a boy."); var userAccount1 = new UserAccount("001", "John", "He is a boy.");
appManager.save(userAccount1); AppManager.save(userAccount1);
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
appManager.find("001"); AppManager.find("001");
appManager.find("001"); AppManager.find("001");
} }
/** /**
@ -148,21 +100,21 @@ public class App {
*/ */
public void useReadThroughAndWriteAroundStrategy() { public void useReadThroughAndWriteAroundStrategy() {
LOGGER.info("# CachingPolicy.AROUND"); LOGGER.info("# CachingPolicy.AROUND");
appManager.initCachingPolicy(CachingPolicy.AROUND); AppManager.initCachingPolicy(CachingPolicy.AROUND);
var userAccount2 = new UserAccount("002", "Jane", "She is a girl."); var userAccount2 = new UserAccount("002", "Jane", "She is a girl.");
appManager.save(userAccount2); AppManager.save(userAccount2);
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
appManager.find("002"); AppManager.find("002");
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
userAccount2 = appManager.find("002"); userAccount2 = AppManager.find("002");
userAccount2.setUserName("Jane G."); userAccount2.setUserName("Jane G.");
appManager.save(userAccount2); AppManager.save(userAccount2);
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
appManager.find("002"); AppManager.find("002");
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
appManager.find("002"); AppManager.find("002");
} }
/** /**
@ -170,31 +122,23 @@ public class App {
*/ */
public void useReadThroughAndWriteBehindStrategy() { public void useReadThroughAndWriteBehindStrategy() {
LOGGER.info("# CachingPolicy.BEHIND"); LOGGER.info("# CachingPolicy.BEHIND");
appManager.initCachingPolicy(CachingPolicy.BEHIND); AppManager.initCachingPolicy(CachingPolicy.BEHIND);
var userAccount3 = new UserAccount("003", var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
"Adam", var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
"He likes food."); var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
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(userAccount3);
appManager.save(userAccount4); AppManager.save(userAccount4);
appManager.save(userAccount5); AppManager.save(userAccount5);
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
appManager.find("003"); AppManager.find("003");
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
UserAccount userAccount6 = new UserAccount("006", UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
"Yasha", AppManager.save(userAccount6);
"She is an only child."); LOGGER.info(AppManager.printCacheContent());
appManager.save(userAccount6); AppManager.find("004");
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
appManager.find("004");
LOGGER.info(appManager.printCacheContent());
} }
/** /**
@ -202,26 +146,20 @@ public class App {
*/ */
public void useCacheAsideStategy() { public void useCacheAsideStategy() {
LOGGER.info("# CachingPolicy.ASIDE"); LOGGER.info("# CachingPolicy.ASIDE");
appManager.initCachingPolicy(CachingPolicy.ASIDE); AppManager.initCachingPolicy(CachingPolicy.ASIDE);
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
var userAccount3 = new UserAccount("003", var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
"Adam", var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
"He likes food."); var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
var userAccount4 = new UserAccount("004", AppManager.save(userAccount3);
"Rita", AppManager.save(userAccount4);
"She hates cats."); AppManager.save(userAccount5);
var userAccount5 = new UserAccount("005",
"Isaac",
"He is allergic to mustard.");
appManager.save(userAccount3);
appManager.save(userAccount4);
appManager.save(userAccount5);
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
appManager.find("003"); AppManager.find("003");
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
appManager.find("004"); AppManager.find("004");
LOGGER.info(appManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
} }
} }

View File

@ -23,80 +23,65 @@
package com.iluwatar.caching; package com.iluwatar.caching;
import com.iluwatar.caching.database.DbManager; import java.text.ParseException;
import java.util.Optional; import java.util.Optional;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
* AppManager helps to bridge the gap in communication between the main class * AppManager helps to bridge the gap in communication between the main class and the application's
* and the application's back-end. DB connection is initialized through this * back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
* class. The chosen caching strategy/policy is also initialized here. * also initialized here. Before the cache can be used, the size of the cache has to be set.
* 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
* Depending on the chosen caching policy, AppManager will call the * CacheStore class.
* appropriate function in the CacheStore class.
*/ */
@Slf4j @Slf4j
public class AppManager { public final class AppManager {
/**
* Caching Policy.
*/
private CachingPolicy cachingPolicy;
/**
* Database Manager.
*/
private final DbManager dbManager;
/**
* Cache Store.
*/
private final CacheStore cacheStore;
/** private static CachingPolicy cachingPolicy;
* Constructor.
* private AppManager() {
* @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 * Developer/Tester is able to choose whether the application should use MongoDB as its underlying
* MongoDB as its underlying data storage or a simple Java data structure * data storage or a simple Java data structure to (temporarily) store the data/objects during
* to (temporarily) store the data/objects during runtime. * runtime.
*/ */
public void initDb() { public static void initDb(boolean useMongoDb) {
dbManager.connect(); if (useMongoDb) {
try {
DbManager.connect();
} catch (ParseException e) {
LOGGER.error("Error connecting to MongoDB", e);
}
} else {
DbManager.createVirtualDb();
}
} }
/** /**
* Initialize caching policy. * Initialize caching policy.
*
* @param policy is a {@link CachingPolicy}
*/ */
public void initCachingPolicy(final CachingPolicy policy) { public static void initCachingPolicy(CachingPolicy policy) {
cachingPolicy = policy; cachingPolicy = policy;
if (cachingPolicy == CachingPolicy.BEHIND) { if (cachingPolicy == CachingPolicy.BEHIND) {
Runtime.getRuntime().addShutdownHook(new Thread(cacheStore::flushCache)); Runtime.getRuntime().addShutdownHook(new Thread(CacheStore::flushCache));
} }
cacheStore.clearCache(); CacheStore.clearCache();
}
public static void initCacheCapacity(int capacity) {
CacheStore.initCapacity(capacity);
} }
/** /**
* Find user account. * Find user account.
*
* @param userId String
* @return {@link UserAccount}
*/ */
public UserAccount find(final String userId) { public static UserAccount find(String userId) {
LOGGER.info("Trying to find {} in cache", userId); if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
if (cachingPolicy == CachingPolicy.THROUGH return CacheStore.readThrough(userId);
|| cachingPolicy == CachingPolicy.AROUND) {
return cacheStore.readThrough(userId);
} else if (cachingPolicy == CachingPolicy.BEHIND) { } else if (cachingPolicy == CachingPolicy.BEHIND) {
return cacheStore.readThroughWithWriteBackPolicy(userId); return CacheStore.readThroughWithWriteBackPolicy(userId);
} else if (cachingPolicy == CachingPolicy.ASIDE) { } else if (cachingPolicy == CachingPolicy.ASIDE) {
return findAside(userId); return findAside(userId);
} }
@ -105,55 +90,41 @@ public class AppManager {
/** /**
* Save user account. * Save user account.
*
* @param userAccount {@link UserAccount}
*/ */
public void save(final UserAccount userAccount) { public static void save(UserAccount userAccount) {
LOGGER.info("Save record!");
if (cachingPolicy == CachingPolicy.THROUGH) { if (cachingPolicy == CachingPolicy.THROUGH) {
cacheStore.writeThrough(userAccount); CacheStore.writeThrough(userAccount);
} else if (cachingPolicy == CachingPolicy.AROUND) { } else if (cachingPolicy == CachingPolicy.AROUND) {
cacheStore.writeAround(userAccount); CacheStore.writeAround(userAccount);
} else if (cachingPolicy == CachingPolicy.BEHIND) { } else if (cachingPolicy == CachingPolicy.BEHIND) {
cacheStore.writeBehind(userAccount); CacheStore.writeBehind(userAccount);
} else if (cachingPolicy == CachingPolicy.ASIDE) { } else if (cachingPolicy == CachingPolicy.ASIDE) {
saveAside(userAccount); saveAside(userAccount);
} }
} }
/** public static String printCacheContent() {
* Returns String. return CacheStore.print();
*
* @return String
*/
public String printCacheContent() {
return cacheStore.print();
} }
/** /**
* Cache-Aside save user account helper. * Cache-Aside save user account helper.
*
* @param userAccount {@link UserAccount}
*/ */
private void saveAside(final UserAccount userAccount) { private static void saveAside(UserAccount userAccount) {
dbManager.updateDb(userAccount); DbManager.updateDb(userAccount);
cacheStore.invalidate(userAccount.getUserId()); CacheStore.invalidate(userAccount.getUserId());
} }
/** /**
* Cache-Aside find user account helper. * Cache-Aside find user account helper.
*
* @param userId String
* @return {@link UserAccount}
*/ */
private UserAccount findAside(final String userId) { private static UserAccount findAside(String userId) {
return Optional.ofNullable(cacheStore.get(userId)) return Optional.ofNullable(CacheStore.get(userId))
.or(() -> { .or(() -> {
Optional<UserAccount> userAccount = Optional<UserAccount> userAccount = Optional.ofNullable(DbManager.readFromDb(userId));
Optional.ofNullable(dbManager.readFromDb(userId)); userAccount.ifPresent(account -> CacheStore.set(userId, account));
userAccount.ifPresent(account -> cacheStore.set(userId, account)); return userAccount;
return userAccount; })
}) .orElse(null);
.orElse(null);
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,52 +0,0 @@
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

@ -1,25 +0,0 @@
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

@ -1,128 +0,0 @@
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

@ -1,78 +0,0 @@
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

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

View File

@ -1,20 +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;

View File

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

View File

@ -23,11 +23,11 @@
package com.iluwatar.caching; package com.iluwatar.caching;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/** /**
* Application test * Application test
*/ */
@ -43,30 +43,32 @@ class CachingTest {
// to avoid Maven compilation errors. Set flag to true to run the // to avoid Maven compilation errors. Set flag to true to run the
// tests with MongoDB (provided that MongoDB is installed and socket // tests with MongoDB (provided that MongoDB is installed and socket
// connection is open). // connection is open).
app = new App(false); AppManager.initDb(false);
AppManager.initCacheCapacity(3);
app = new App();
} }
@Test @Test
void testReadAndWriteThroughStrategy() { void testReadAndWriteThroughStrategy() {
assertNotNull(app); assertNotNull(app);
app.useReadAndWriteThroughStrategy(); app.useReadAndWriteThroughStrategy();
} }
@Test @Test
void testReadThroughAndWriteAroundStrategy() { void testReadThroughAndWriteAroundStrategy() {
assertNotNull(app); assertNotNull(app);
app.useReadThroughAndWriteAroundStrategy(); app.useReadThroughAndWriteAroundStrategy();
} }
@Test @Test
void testReadThroughAndWriteBehindStrategy() { void testReadThroughAndWriteBehindStrategy() {
assertNotNull(app); assertNotNull(app);
app.useReadThroughAndWriteBehindStrategy(); app.useReadThroughAndWriteBehindStrategy();
} }
@Test @Test
void testCacheAsideStrategy() { void testCacheAsideStrategy() {
assertNotNull(app); assertNotNull(app);
app.useCacheAsideStategy(); app.useCacheAsideStategy();
} }
} }

View File

@ -1,84 +0,0 @@
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,6 +1,9 @@
--- ---
layout: pattern
title: Callback title: Callback
category: Idiom folder: callback
permalink: /patterns/callback/
categories: Idiom
language: en language: en
tags: tags:
- Reactive - Reactive

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

147
index.md
View File

@ -1,147 +0,0 @@
| 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,5 +1,8 @@
--- ---
layout: pattern
title: Intercepting Filter title: Intercepting Filter
folder: intercepting-filter
permalink: /patterns/intercepting-filter/
categories: Behavioral categories: Behavioral
language: en language: en
tags: tags:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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