Compare commits

...

32 Commits

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,6 @@
--- ---
layout: pattern
title: Caching title: Caching
folder: caching category: Behavioral
permalink: /patterns/caching/
categories: Behavioral
language: en language: en
tags: tags:
- Performance - Performance
@ -43,39 +40,29 @@ 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` which handles which is a simple Java object containing the user account details, and `DbManager` interface which handles
reading and writing of these objects to/from MongoDB database. reading and writing of these objects to/from database.
```java ```java
@Setter @Data
@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;
} }
@Slf4j public interface DbManager {
public final class DbManager {
private static MongoClient mongoClient; void connect();
private static MongoDatabase db; void disconnect();
private DbManager() { /*...*/ } UserAccount readFromDb(String userId);
UserAccount writeToDb(UserAccount userAccount);
public static void createVirtualDb() { /*...*/ } UserAccount updateDb(UserAccount userAccount);
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) { /*...*/ }
} }
``` ```
@ -171,30 +158,43 @@ 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 static UserAccount readThrough(String userId) { public UserAccount readThrough(final String userId) {
if (cache.contains(userId)) { if (cache.contains(userId)) {
LOGGER.info("# Cache Hit!"); LOGGER.info("# Found in Cache!");
return cache.get(userId); return cache.get(userId);
} }
LOGGER.info("# Cache Miss!"); LOGGER.info("# Not found in cache! Go to DB!!");
UserAccount userAccount = DbManager.readFromDb(userId); UserAccount userAccount = dbManager.readFromDb(userId);
cache.set(userId, userAccount); cache.set(userId, userAccount);
return userAccount; return userAccount;
} }
public static void writeThrough(UserAccount userAccount) { public void writeThrough(final 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,34 +225,39 @@ 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 static void initDb(boolean useMongoDb) { /* ... */ } public void initDb() { /* ... */ }
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 static UserAccount find(String userId) { public UserAccount find(final String userId) {
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) { LOGGER.info("Trying to find {} in cache", userId);
return CacheStore.readThrough(userId); if (cachingPolicy == CachingPolicy.THROUGH
|| 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 static void save(UserAccount userAccount) { public void save(final 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);
} }
@ -272,24 +277,35 @@ Here is what we do in the main class of the application.
@Slf4j @Slf4j
public class App { public class App {
public static void main(String[] args) { public static void main(final String[] args) {
AppManager.initDb(false); 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);
} }
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);
LOGGER.info(AppManager.printCacheContent()); appManager.save(userAccount1);
AppManager.find("001"); LOGGER.info(appManager.printCacheContent());
AppManager.find("001"); appManager.find("001");
appManager.find("001");
} }
public void useReadThroughAndWriteAroundStrategy() { /* ... */ } public void useReadThroughAndWriteAroundStrategy() { /* ... */ }
@ -300,16 +316,6 @@ public class App {
} }
``` ```
Finally, here is some of the console output from the program.
```
12:32:53.845 [main] INFO com.iluwatar.caching.App - # CachingPolicy.THROUGH
12:32:53.900 [main] INFO com.iluwatar.caching.App -
--CACHE CONTENT--
UserAccount(userId=001, userName=John, additionalInfo=He is a boy.)
----
```
## Class diagram ## Class diagram
![alt text](./etc/caching.png "Caching") ![alt text](./etc/caching.png "Caching")

View File

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

View File

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

View File

@ -1,58 +1,62 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.caching; 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 resources by not releasing * The Caching pattern describes how to avoid expensive re-acquisition of
* the resources immediately after their use. The resources retain their identity, are kept in some * resources by not releasing the resources immediately after their use.
* fast-access storage, and are re-used to avoid having to acquire them again. There are four main * The resources retain their identity, are kept in some fast-access storage,
* caching strategies/techniques in this pattern; each with their own pros and cons. They are; * and are re-used to avoid having to acquire them again. There are four main
* <code>write-through</code> which writes data to the cache and DB in a single transaction, * caching strategies/techniques in this pattern; each with their own pros and
* <code>write-around</code> which writes data immediately into the DB instead of the cache, * cons. They are <code>write-through</code> which writes data to the cache and
* <code>write-behind</code> which writes data into the cache initially whilst the data is only * DB in a single transaction, <code>write-around</code> which writes data
* written into the DB when the cache is full, and <code>cache-aside</code> which pushes the * immediately into the DB instead of the cache, <code>write-behind</code>
* responsibility of keeping the data synchronized in both data sources to the application itself. * which writes data into the cache initially whilst the data is only
* The <code>read-through</code> strategy is also included in the mentioned four strategies -- * written into the DB when the cache is full, and <code>cache-aside</code>
* returns data from the cache to the caller <b>if</b> it exists <b>else</b> queries from DB and * which pushes the responsibility of keeping the data synchronized in both
* stores it into the cache for future use. These strategies determine when the data in the cache * data sources to the application itself. The <code>read-through</code>
* should be written back to the backing store (i.e. Database) and help keep both data sources * strategy is also included in the mentioned four strategies --
* synchronized/up-to-date. This pattern can improve performance and also helps to maintain * returns data from the cache to the caller <b>if</b> it exists <b>else</b>
* consistency between data held in the cache and the data in the underlying data store. * queries from DB and stores it into the cache for future use. These strategies
* determine when the data in the cache should be written back to the backing
* store (i.e. Database) and help keep both data sources
* synchronized/up-to-date. This pattern can improve performance and also helps
* to maintainconsistency between data held in the cache and the data in
* the underlying data store.
* *
* <p>In this example, the user account ({@link UserAccount}) entity is used as the underlying * <p>In this example, the user account ({@link UserAccount}) entity is used
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts * as the underlying application data. The cache itself is implemented as an
* a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The four * internal (Java) data structure. It adopts a Least-Recently-Used (LRU)
* strategies are individually tested. The testing of the cache is restricted towards saving and * strategy for evicting data from itself when its full. The four
* querying of user accounts from the underlying data store ( {@link DbManager}). The main class ( * strategies are individually tested. The testing of the cache is restricted
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and * towards saving and querying of user accounts from the
* whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager * underlying data store( {@link DbManager}). The main class ( {@link App}
* ({@link AppManager}) handles the transaction of data to-and-from the underlying data store * is not aware of the underlying mechanics of the application
* (depending on the preferred caching policy/strategy). * (i.e. save and query) and whether the data is coming from the cache or the
* DB (i.e. separation of concern). The AppManager ({@link AppManager}) handles
* the transaction of data to-and-from the underlying data store (depending on
* the preferred caching policy/strategy).
* <p> * <p>
* <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager} </i> * <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy -->
* DBManager} </i>
* </p>
*
* <p>
* There are 2 ways to launch the application.
* - to use "in Memory" database.
* - to use the MongoDb as a database
*
* To run the application with "in Memory" database, just launch it without parameters
* Example: 'java -jar app.jar'
*
* To run the application with MongoDb you need to be installed the MongoDb
* in your system, or to launch it in the docker container.
* You may launch docker container from the root of current module with command:
* 'docker-compose up'
* Then you can start the application with parameter --mongo
* Example: 'java -jar app.jar --mongo'
* </p> * </p>
* *
* @see CacheStore * @see CacheStore
@ -61,23 +65,67 @@ 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(String[] args) { public static void main(final String[] args) {
AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests // 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).
AppManager.initCacheCapacity(3); boolean isDbMongo = isDbMongo(args);
var app = new App(); if (isDbMongo) {
LOGGER.info("Using the Mongo database engine to run the application.");
} else {
LOGGER.info("Using the 'in Memory' database to run the application.");
}
App app = new App(isDbMongo);
app.useReadAndWriteThroughStrategy(); 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;
} }
/** /**
@ -85,14 +133,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");
} }
/** /**
@ -100,21 +148,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");
} }
/** /**
@ -122,23 +170,31 @@ 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", "Adam", "He likes food."); var userAccount3 = new UserAccount("003",
var userAccount4 = new UserAccount("004", "Rita", "She hates cats."); "Adam",
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard."); "He likes food.");
var userAccount4 = new UserAccount("004",
"Rita",
"She hates cats.");
var userAccount5 = new UserAccount("005",
"Isaac",
"He is allergic to mustard.");
AppManager.save(userAccount3); appManager.save(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", "Yasha", "She is an only child."); UserAccount userAccount6 = new UserAccount("006",
AppManager.save(userAccount6); "Yasha",
LOGGER.info(AppManager.printCacheContent()); "She is an only child.");
AppManager.find("004"); appManager.save(userAccount6);
LOGGER.info(AppManager.printCacheContent()); LOGGER.info(appManager.printCacheContent());
appManager.find("004");
LOGGER.info(appManager.printCacheContent());
} }
/** /**
@ -146,20 +202,26 @@ 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", "Adam", "He likes food."); var userAccount3 = new UserAccount("003",
var userAccount4 = new UserAccount("004", "Rita", "She hates cats."); "Adam",
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard."); "He likes food.");
AppManager.save(userAccount3); var userAccount4 = new UserAccount("004",
AppManager.save(userAccount4); "Rita",
AppManager.save(userAccount5); "She hates cats.");
var userAccount5 = new UserAccount("005",
"Isaac",
"He is allergic to mustard.");
appManager.save(userAccount3);
appManager.save(userAccount4);
appManager.save(userAccount5);
LOGGER.info(AppManager.printCacheContent()); 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,65 +23,80 @@
package com.iluwatar.caching; package com.iluwatar.caching;
import java.text.ParseException; import com.iluwatar.caching.database.DbManager;
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 and the application's * AppManager helps to bridge the gap in communication between the main class
* back-end. DB connection is initialized through this class. The chosen caching strategy/policy is * and the application's back-end. DB connection is initialized through this
* also initialized here. Before the cache can be used, the size of the cache has to be set. * class. The chosen caching strategy/policy is also initialized here.
* Depending on the chosen caching policy, AppManager will call the appropriate function in the * Before the cache can be used, the size of the cache has to be set.
* CacheStore class. * Depending on the chosen caching policy, AppManager will call the
* appropriate function in the CacheStore class.
*/ */
@Slf4j @Slf4j
public final class AppManager { public class AppManager {
/**
* Caching Policy.
*/
private CachingPolicy cachingPolicy;
/**
* Database Manager.
*/
private final DbManager dbManager;
/**
* Cache Store.
*/
private final CacheStore cacheStore;
private static CachingPolicy cachingPolicy; /**
* 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 MongoDB as its underlying * Developer/Tester is able to choose whether the application should use
* data storage or a simple Java data structure to (temporarily) store the data/objects during * MongoDB as its underlying data storage or a simple Java data structure
* runtime. * to (temporarily) store the data/objects during runtime.
*/ */
public static void initDb(boolean useMongoDb) { public void initDb() {
if (useMongoDb) { dbManager.connect();
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 static void initCachingPolicy(CachingPolicy policy) { public void initCachingPolicy(final 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 static UserAccount find(String userId) { public UserAccount find(final String userId) {
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) { LOGGER.info("Trying to find {} in cache", userId);
return CacheStore.readThrough(userId); if (cachingPolicy == CachingPolicy.THROUGH
|| 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);
} }
@ -90,41 +105,55 @@ public final class AppManager {
/** /**
* Save user account. * Save user account.
*
* @param userAccount {@link UserAccount}
*/ */
public static void save(UserAccount userAccount) { public void save(final UserAccount userAccount) {
LOGGER.info("Save record!");
if (cachingPolicy == CachingPolicy.THROUGH) { 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() { /**
return CacheStore.print(); * Returns String.
*
* @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 static void saveAside(UserAccount userAccount) { private void saveAside(final 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 static UserAccount findAside(String userId) { private UserAccount findAside(final String userId) {
return Optional.ofNullable(CacheStore.get(userId)) return Optional.ofNullable(cacheStore.get(userId))
.or(() -> { .or(() -> {
Optional<UserAccount> userAccount = Optional.ofNullable(DbManager.readFromDb(userId)); Optional<UserAccount> userAccount =
userAccount.ifPresent(account -> CacheStore.set(userId, account)); Optional.ofNullable(dbManager.readFromDb(userId));
return userAccount; userAccount.ifPresent(account -> cacheStore.set(userId, account));
}) return userAccount;
.orElse(null); })
.orElse(null);
} }
} }

View File

@ -23,9 +23,11 @@
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;
/** /**
@ -33,16 +35,34 @@ 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 static void initCapacity(int capacity) { public void initCapacity(final int capacity) {
if (cache == null) { if (cache == null) {
cache = new LruCache(capacity); cache = new LruCache(capacity);
} else { } else {
@ -52,57 +72,64 @@ 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 static UserAccount readThrough(String userId) { public UserAccount readThrough(final String userId) {
if (cache.contains(userId)) { if (cache.contains(userId)) {
LOGGER.info("# Cache Hit!"); LOGGER.info("# Found in Cache!");
return cache.get(userId); return cache.get(userId);
} }
LOGGER.info("# Cache Miss!"); LOGGER.info("# Not found in cache! Go to DB!!");
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 static void writeThrough(UserAccount userAccount) { public void writeThrough(final 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 static void writeAround(UserAccount userAccount) { public void writeAround(final UserAccount userAccount) {
if (cache.contains(userAccount.getUserId())) { if (cache.contains(userAccount.getUserId())) {
DbManager.updateDb(userAccount); dbManager.updateDb(userAccount);
cache.invalidate(userAccount.getUserId()); // Cache data has been updated -- remove older // 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 static UserAccount readThroughWithWriteBackPolicy(String userId) { public UserAccount readThroughWithWriteBackPolicy(final String userId) {
if (cache.contains(userId)) { if (cache.contains(userId)) {
LOGGER.info("# Cache Hit!"); LOGGER.info("# Found in cache!");
return cache.get(userId); return cache.get(userId);
} }
LOGGER.info("# Cache Miss!"); LOGGER.info("# Not found in Cache!");
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;
@ -110,12 +137,13 @@ public class CacheStore {
/** /**
* Set user account. * Set user account.
* @param userAccount {@link UserAccount}
*/ */
public static void writeBehind(UserAccount userAccount) { public void writeBehind(final UserAccount userAccount) {
if (cache.isFull() && !cache.contains(userAccount.getUserId())) { 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);
} }
@ -123,7 +151,7 @@ public class CacheStore {
/** /**
* Clears cache. * Clears cache.
*/ */
public static void clearCache() { public void clearCache() {
if (cache != null) { if (cache != null) {
cache.clear(); cache.clear();
} }
@ -132,44 +160,51 @@ public class CacheStore {
/** /**
* Writes remaining content in the cache into the DB. * Writes remaining content in the cache into the DB.
*/ */
public static void flushCache() { public 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 static String print() { public 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", "----\n")); .collect(Collectors.joining("", "\n--CACHE CONTENT--\n", "----"));
} }
/** /**
* Delegate to backing cache store. * Delegate to backing cache store.
* @param userId {@link String}
* @return {@link UserAccount}
*/ */
public static UserAccount get(String userId) { public UserAccount get(final String userId) {
return cache.get(userId); return cache.get(userId);
} }
/** /**
* Delegate to backing cache store. * Delegate to backing cache store.
* @param userId {@link String}
* @param userAccount {@link UserAccount}
*/ */
public static void set(String userId, UserAccount userAccount) { public void set(final String userId, final UserAccount userAccount) {
cache.set(userId, userAccount); cache.set(userId, userAccount);
} }
/** /**
* Delegate to backing cache store. * Delegate to backing cache store.
* @param userId {@link String}
*/ */
public static void invalidate(String userId) { public void invalidate(final String userId) {
cache.invalidate(userId); cache.invalidate(userId);
} }
} }

View File

@ -32,10 +32,25 @@ 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

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

View File

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

View File

@ -24,19 +24,28 @@
package com.iluwatar.caching; package com.iluwatar.caching;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Data;
import lombok.Setter; import lombok.EqualsAndHashCode;
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.
*/ */
@Setter @Data
@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,11 +26,27 @@ package com.iluwatar.caching.constants;
/** /**
* Constant class for defining constants. * Constant class for defining constants.
*/ */
public class CachingConstants { public final 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

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

View File

@ -0,0 +1,52 @@
package com.iluwatar.caching.database;
import com.iluwatar.caching.UserAccount;
/**
* <p>DBManager handles the communication with the underlying data store i.e.
* Database. It contains the implemented methods for querying, inserting,
* and updating data. MongoDB was used as the database for the application.</p>
*/
public interface DbManager {
/**
* Connect to DB.
*/
void connect();
/**
* Disconnect from DB.
*/
void disconnect();
/**
* Read from DB.
*
* @param userId {@link String}
* @return {@link UserAccount}
*/
UserAccount readFromDb(String userId);
/**
* Write to DB.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
UserAccount writeToDb(UserAccount userAccount);
/**
* Update record.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
UserAccount updateDb(UserAccount userAccount);
/**
* Update record or Insert if not exists.
*
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
UserAccount upsertDb(UserAccount userAccount);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,25 +25,21 @@ package com.iluwatar.caching;
import org.junit.jupiter.api.Test; import 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,32 +43,30 @@ 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).
AppManager.initDb(false); app = new App(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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

147
index.md Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

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