Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
2f84369003
4
CONTRIBUTING.MD
Normal file
4
CONTRIBUTING.MD
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
This is great you have something to contribute!
|
||||||
|
|
||||||
|
Before going any further please read the [wiki](https://github.com/iluwatar/java-design-patterns/wiki)
|
||||||
|
with conventions and rules we used for this project.
|
@ -2,11 +2,10 @@
|
|||||||
that smart and dearly wants an empty line before a heading to be able to
|
that smart and dearly wants an empty line before a heading to be able to
|
||||||
display it as such, e.g. website) -->
|
display it as such, e.g. website) -->
|
||||||
|
|
||||||
# Design pattern samples in Java
|
# Design patterns implemented in Java
|
||||||
|
|
||||||
[](https://travis-ci.org/iluwatar/java-design-patterns)
|
[](https://travis-ci.org/iluwatar/java-design-patterns)
|
||||||
[](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master)
|
[](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master)
|
||||||
[](https://scan.coverity.com/projects/5634)
|
|
||||||
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
@ -40,7 +39,7 @@ patterns by any of the following approaches
|
|||||||
|
|
||||||
# How to contribute
|
# How to contribute
|
||||||
|
|
||||||
If you are willing to contribute to the project you will find the relevant information in our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki).
|
If you are willing to contribute to the project you will find the relevant information in our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki). We will help you and answer your questions in the [Gitter chatroom](https://gitter.im/iluwatar/java-design-patterns).
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
|
|
||||||
|
@ -7,26 +7,30 @@ categories: Creational
|
|||||||
tags:
|
tags:
|
||||||
- Java
|
- Java
|
||||||
- Gang Of Four
|
- Gang Of Four
|
||||||
|
- Difficulty-Intermediate
|
||||||
---
|
---
|
||||||
|
|
||||||
**Also known as:** Kit
|
## Also known as
|
||||||
|
Kit
|
||||||
|
|
||||||
**Intent:** Provide an interface for creating families of related or dependent
|
## Intent
|
||||||
|
Provide an interface for creating families of related or dependent
|
||||||
objects without specifying their concrete classes.
|
objects without specifying their concrete classes.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Abstract Factory pattern when
|
## Applicability
|
||||||
|
Use the Abstract Factory pattern when
|
||||||
|
|
||||||
* a system should be independent of how its products are created, composed and represented
|
* a system should be independent of how its products are created, composed and represented
|
||||||
* a system should be configured with one of multiple families of products
|
* a system should be configured with one of multiple families of products
|
||||||
* a family of related product objects is designed to be used together, and you need to enforce this constraint
|
* a family of related product objects is designed to be used together, and you need to enforce this constraint
|
||||||
* you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations
|
* you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations
|
||||||
|
|
||||||
**Real world examples:**
|
## Real world examples
|
||||||
|
|
||||||
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
|
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
|
||||||
|
|
||||||
**Credits**
|
## Credits
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>abstract-factory</artifactId>
|
<artifactId>abstract-factory</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* The Abstract Factory pattern provides a way to encapsulate a group of individual factories that
|
* The Abstract Factory pattern provides a way to encapsulate a group of individual factories that have a common theme
|
||||||
* have a common theme without specifying their concrete classes. In normal usage, the client
|
* without specifying their concrete classes. In normal usage, the client software creates a concrete implementation of
|
||||||
* software creates a concrete implementation of the abstract factory and then uses the generic
|
* the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part
|
||||||
* interface of the factory to create the concrete objects that are part of the theme. The client
|
* of the theme. The client does not know (or care) which concrete objects it gets from each of these internal
|
||||||
* does not know (or care) which concrete objects it gets from each of these internal factories,
|
* factories, since it uses only the generic interfaces of their products. This pattern separates the details of
|
||||||
* since it uses only the generic interfaces of their products. This pattern separates the details
|
* implementation of a set of objects from their general usage and relies on object composition, as object creation is
|
||||||
* of implementation of a set of objects from their general usage and relies on object composition,
|
* implemented in methods exposed in the factory interface.
|
||||||
* as object creation is implemented in methods exposed in the factory interface.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) and
|
* The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) and its implementations (
|
||||||
* its implementations ({@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses both
|
* {@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses both concrete implementations to create a
|
||||||
* concrete implementations to create a king, a castle and an army.
|
* king, a castle and an army.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
@ -23,11 +21,8 @@ public class App {
|
|||||||
private Castle castle;
|
private Castle castle;
|
||||||
private Army army;
|
private Army army;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates kingdom
|
* Creates kingdom
|
||||||
*
|
|
||||||
* @param factory
|
|
||||||
*/
|
*/
|
||||||
public void createKingdom(final KingdomFactory factory) {
|
public void createKingdom(final KingdomFactory factory) {
|
||||||
setKing(factory.createKing());
|
setKing(factory.createKing());
|
||||||
@ -47,14 +42,6 @@ public class App {
|
|||||||
return factory.createKing();
|
return factory.createKing();
|
||||||
}
|
}
|
||||||
|
|
||||||
Castle getCastle(final KingdomFactory factory) {
|
|
||||||
return factory.createCastle();
|
|
||||||
}
|
|
||||||
|
|
||||||
Army getArmy(final KingdomFactory factory) {
|
|
||||||
return factory.createArmy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public King getKing() {
|
public King getKing() {
|
||||||
return king;
|
return king;
|
||||||
}
|
}
|
||||||
@ -63,6 +50,10 @@ public class App {
|
|||||||
this.king = king;
|
this.king = king;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Castle getCastle(final KingdomFactory factory) {
|
||||||
|
return factory.createCastle();
|
||||||
|
}
|
||||||
|
|
||||||
public Castle getCastle() {
|
public Castle getCastle() {
|
||||||
return castle;
|
return castle;
|
||||||
}
|
}
|
||||||
@ -71,6 +62,10 @@ public class App {
|
|||||||
this.castle = castle;
|
this.castle = castle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Army getArmy(final KingdomFactory factory) {
|
||||||
|
return factory.createArmy();
|
||||||
|
}
|
||||||
|
|
||||||
public Army getArmy() {
|
public Army getArmy() {
|
||||||
return army;
|
return army;
|
||||||
}
|
}
|
||||||
@ -79,11 +74,11 @@ public class App {
|
|||||||
this.army = army;
|
this.army = army;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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(String[] args) {
|
||||||
|
|
||||||
|
@ -7,26 +7,30 @@ categories: Structural
|
|||||||
tags:
|
tags:
|
||||||
- Java
|
- Java
|
||||||
- Gang Of Four
|
- Gang Of Four
|
||||||
|
- Difficulty-Beginner
|
||||||
---
|
---
|
||||||
|
|
||||||
**Also known as:** Wrapper
|
## Also known as
|
||||||
|
Wrapper
|
||||||
|
|
||||||
**Intent:** Convert the interface of a class into another interface the clients
|
## Intent
|
||||||
|
Convert the interface of a class into another interface the clients
|
||||||
expect. Adapter lets classes work together that couldn't otherwise because of
|
expect. Adapter lets classes work together that couldn't otherwise because of
|
||||||
incompatible interfaces.
|
incompatible interfaces.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Adapter pattern when
|
## Applicability
|
||||||
|
Use the Adapter pattern when
|
||||||
|
|
||||||
* you want to use an existing class, and its interface does not match the one you need
|
* you want to use an existing class, and its interface does not match the one you need
|
||||||
* you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
|
* you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
|
||||||
* you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class.
|
* you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class.
|
||||||
|
|
||||||
**Real world examples:**
|
## Real world examples
|
||||||
|
|
||||||
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
|
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
|
||||||
|
|
||||||
**Credits**
|
## Credits
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>adapter</artifactId>
|
<artifactId>adapter</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
package com.iluwatar.adapter;
|
package com.iluwatar.adapter;
|
||||||
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.iluwatar.adapter.BattleFishingBoat;
|
import java.util.HashMap;
|
||||||
import com.iluwatar.adapter.BattleShip;
|
import java.util.Map;
|
||||||
import com.iluwatar.adapter.Captain;
|
|
||||||
import com.iluwatar.adapter.FishingBoat;
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class
|
* Test class
|
||||||
|
@ -4,24 +4,29 @@ title: Async Method Invocation
|
|||||||
folder: async-method-invocation
|
folder: async-method-invocation
|
||||||
permalink: /patterns/async-method-invocation/
|
permalink: /patterns/async-method-invocation/
|
||||||
categories: Concurrency
|
categories: Concurrency
|
||||||
tags: Java
|
tags:
|
||||||
|
- Java
|
||||||
|
- Difficulty-Intermediate
|
||||||
|
- Functional
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Asynchronous method invocation is pattern where the calling thread
|
## Intent
|
||||||
|
Asynchronous method invocation is pattern where the calling thread
|
||||||
is not blocked while waiting results of tasks. The pattern provides parallel
|
is not blocked while waiting results of tasks. The pattern provides parallel
|
||||||
processing of multiple independent tasks and retrieving the results via
|
processing of multiple independent tasks and retrieving the results via
|
||||||
callbacks or waiting until everything is done.
|
callbacks or waiting until everything is done.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use async method invocation pattern when
|
## Applicability
|
||||||
|
Use async method invocation pattern when
|
||||||
|
|
||||||
* you have multiple independent tasks that can run in parallel
|
* you have multiple independent tasks that can run in parallel
|
||||||
* you need to improve the performance of a group of sequential tasks
|
* you need to improve the performance of a group of sequential tasks
|
||||||
* you have limited amount of processing capacity or long running tasks and the
|
* you have limited amount of processing capacity or long running tasks and the
|
||||||
caller should not wait the tasks to be ready
|
caller should not wait the tasks to be ready
|
||||||
|
|
||||||
**Real world examples:**
|
## Real world examples
|
||||||
|
|
||||||
* [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html), [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) and [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) (Java)
|
* [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html), [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) and [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) (Java)
|
||||||
* [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx) (.NET)
|
* [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx) (.NET)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>async-method-invocation</artifactId>
|
<artifactId>async-method-invocation</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -4,24 +4,23 @@ import java.util.concurrent.Callable;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
|
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
|
||||||
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated
|
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated value,
|
||||||
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and
|
* <code>AsyncCallback</code> which can be provided to be executed on task completion and <code>AsyncExecutor</code>
|
||||||
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
|
* that manages the execution of the async tasks.
|
||||||
* <p>
|
* <p>
|
||||||
* The main method shows example flow of async invocations. The main thread starts multiple tasks
|
* The main method shows example flow of async invocations. The main thread starts multiple tasks with variable
|
||||||
* with variable durations and then continues its own work. When the main thread has done it's job
|
* durations and then continues its own work. When the main thread has done it's job it collects the results of the
|
||||||
* it collects the results of the async tasks. Two of the tasks are handled with callbacks, meaning
|
* async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are executed immediately when the
|
||||||
* the callbacks are executed immediately when the tasks complete.
|
* tasks complete.
|
||||||
* <p>
|
* <p>
|
||||||
* Noteworthy difference of thread usage between the async results and callbacks is that the async
|
* Noteworthy difference of thread usage between the async results and callbacks is that the async results are collected
|
||||||
* results are collected in the main thread but the callbacks are executed within the worker
|
* in the main thread but the callbacks are executed within the worker threads. This should be noted when working with
|
||||||
* threads. This should be noted when working with thread pools.
|
* thread pools.
|
||||||
* <p>
|
* <p>
|
||||||
* Java provides its own implementations of async method invocation pattern. FutureTask,
|
* Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture and
|
||||||
* CompletableFuture and ExecutorService are the real world implementations of this pattern. But due
|
* ExecutorService are the real world implementations of this pattern. But due to the nature of parallel programming,
|
||||||
* to the nature of parallel programming, the implementations are not trivial. This example does not
|
* the implementations are not trivial. This example does not take all possible scenarios into account but rather
|
||||||
* take all possible scenarios into account but rather provides a simple version that helps to
|
* provides a simple version that helps to understand the pattern.
|
||||||
* understand the pattern.
|
|
||||||
*
|
*
|
||||||
* @see AsyncResult
|
* @see AsyncResult
|
||||||
* @see AsyncCallback
|
* @see AsyncCallback
|
||||||
@ -33,6 +32,9 @@ import java.util.concurrent.Callable;
|
|||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Program entry point
|
||||||
|
*/
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
// construct a new executor that will run async tasks
|
// construct a new executor that will run async tasks
|
||||||
AsyncExecutor executor = new ThreadAsyncExecutor();
|
AsyncExecutor executor = new ThreadAsyncExecutor();
|
||||||
@ -41,10 +43,8 @@ public class App {
|
|||||||
AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
|
AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
|
||||||
AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
|
AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
|
||||||
AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
||||||
AsyncResult<Integer> asyncResult4 =
|
AsyncResult<Integer> asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
|
||||||
executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
|
AsyncResult<String> asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
|
||||||
AsyncResult<String> asyncResult5 =
|
|
||||||
executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
|
|
||||||
|
|
||||||
// emulate processing in the current thread while async tasks are running in their own threads
|
// emulate processing in the current thread while async tasks are running in their own threads
|
||||||
Thread.sleep(350); // Oh boy I'm working hard here
|
Thread.sleep(350); // Oh boy I'm working hard here
|
||||||
@ -66,8 +66,10 @@ public class App {
|
|||||||
/**
|
/**
|
||||||
* Creates a callable that lazily evaluates to given value with artificial delay.
|
* Creates a callable that lazily evaluates to given value with artificial delay.
|
||||||
*
|
*
|
||||||
* @param value value to evaluate
|
* @param value
|
||||||
* @param delayMillis artificial delay in milliseconds
|
* value to evaluate
|
||||||
|
* @param delayMillis
|
||||||
|
* artificial delay in milliseconds
|
||||||
* @return new callable for lazy evaluation
|
* @return new callable for lazy evaluation
|
||||||
*/
|
*/
|
||||||
private static <T> Callable<T> lazyval(T value, long delayMillis) {
|
private static <T> Callable<T> lazyval(T value, long delayMillis) {
|
||||||
@ -81,7 +83,8 @@ public class App {
|
|||||||
/**
|
/**
|
||||||
* Creates a simple callback that logs the complete status of the async result.
|
* Creates a simple callback that logs the complete status of the async result.
|
||||||
*
|
*
|
||||||
* @param name callback name
|
* @param name
|
||||||
|
* callback name
|
||||||
* @return new async callback
|
* @return new async callback
|
||||||
*/
|
*/
|
||||||
private static <T> AsyncCallback<T> callback(String name) {
|
private static <T> AsyncCallback<T> callback(String name) {
|
||||||
|
@ -5,8 +5,6 @@ import java.util.concurrent.ExecutionException;
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* AsyncResult interface
|
* AsyncResult interface
|
||||||
*
|
|
||||||
* @param <T>
|
|
||||||
*/
|
*/
|
||||||
public interface AsyncResult<T> {
|
public interface AsyncResult<T> {
|
||||||
|
|
||||||
|
@ -34,8 +34,7 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException,
|
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException {
|
||||||
InterruptedException {
|
|
||||||
if (asyncResult.isCompleted()) {
|
if (asyncResult.isCompleted()) {
|
||||||
return asyncResult.getValue();
|
return asyncResult.getValue();
|
||||||
} else {
|
} else {
|
||||||
@ -45,9 +44,8 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple implementation of async result that allows completing it successfully with a value or
|
* Simple implementation of async result that allows completing it successfully with a value or exceptionally with an
|
||||||
* exceptionally with an exception. A really simplified version from its real life cousins
|
* exception. A really simplified version from its real life cousins FutureTask and CompletableFuture.
|
||||||
* FutureTask and CompletableFuture.
|
|
||||||
*
|
*
|
||||||
* @see java.util.concurrent.FutureTask
|
* @see java.util.concurrent.FutureTask
|
||||||
* @see java.util.concurrent.CompletableFuture
|
* @see java.util.concurrent.CompletableFuture
|
||||||
@ -71,10 +69,11 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value from successful execution and executes callback if available. Notifies any
|
* Sets the value from successful execution and executes callback if available. Notifies any thread waiting for
|
||||||
* thread waiting for completion.
|
* completion.
|
||||||
*
|
*
|
||||||
* @param value value of the evaluated task
|
* @param value
|
||||||
|
* value of the evaluated task
|
||||||
*/
|
*/
|
||||||
void setValue(T value) {
|
void setValue(T value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@ -86,10 +85,11 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the exception from failed execution and executes callback if available. Notifies any
|
* Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for
|
||||||
* thread waiting for completion.
|
* completion.
|
||||||
*
|
*
|
||||||
* @param exception exception of the failed task
|
* @param exception
|
||||||
|
* exception of the failed task
|
||||||
*/
|
*/
|
||||||
void setException(Exception exception) {
|
void setException(Exception exception) {
|
||||||
this.exception = exception;
|
this.exception = exception;
|
||||||
@ -102,7 +102,7 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompleted() {
|
public boolean isCompleted() {
|
||||||
return (state > RUNNING);
|
return state > RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,19 +8,9 @@ import java.util.Optional;
|
|||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertSame;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.mockito.Mockito.timeout;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
import static org.mockito.internal.verification.VerificationModeFactory.times;
|
import static org.mockito.internal.verification.VerificationModeFactory.times;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,8 +45,7 @@ public class ThreadAsyncExecutorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test used to verify the happy path of
|
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)}
|
||||||
* {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)}
|
|
||||||
*/
|
*/
|
||||||
@Test(timeout = 3000)
|
@Test(timeout = 3000)
|
||||||
public void testSuccessfulTaskWithCallback() throws Exception {
|
public void testSuccessfulTaskWithCallback() throws Exception {
|
||||||
@ -77,8 +66,7 @@ public class ThreadAsyncExecutorTest {
|
|||||||
verify(task, times(1)).call();
|
verify(task, times(1)).call();
|
||||||
|
|
||||||
// ... same for the callback, we expect our object
|
// ... same for the callback, we expect our object
|
||||||
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor
|
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
|
||||||
.forClass((Class) Optional.class);
|
|
||||||
verify(callback, times(1)).onComplete(eq(result), optionalCaptor.capture());
|
verify(callback, times(1)).onComplete(eq(result), optionalCaptor.capture());
|
||||||
|
|
||||||
final Optional<Exception> optionalException = optionalCaptor.getValue();
|
final Optional<Exception> optionalException = optionalCaptor.getValue();
|
||||||
@ -90,8 +78,8 @@ public class ThreadAsyncExecutorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a
|
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while
|
||||||
* task takes a while to execute
|
* to execute
|
||||||
*/
|
*/
|
||||||
@Test(timeout = 5000)
|
@Test(timeout = 5000)
|
||||||
public void testLongRunningTaskWithoutCallback() throws Exception {
|
public void testLongRunningTaskWithoutCallback() throws Exception {
|
||||||
@ -111,8 +99,7 @@ public class ThreadAsyncExecutorTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
asyncResult.getValue();
|
asyncResult.getValue();
|
||||||
fail(
|
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
||||||
"Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
assertNotNull(e.getMessage());
|
assertNotNull(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -130,9 +117,8 @@ public class ThreadAsyncExecutorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test used to verify the happy path of
|
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when a task
|
||||||
* {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when a task takes a while to
|
* takes a while to execute
|
||||||
* execute
|
|
||||||
*/
|
*/
|
||||||
@Test(timeout = 5000)
|
@Test(timeout = 5000)
|
||||||
public void testLongRunningTaskWithCallback() throws Exception {
|
public void testLongRunningTaskWithCallback() throws Exception {
|
||||||
@ -155,8 +141,7 @@ public class ThreadAsyncExecutorTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
asyncResult.getValue();
|
asyncResult.getValue();
|
||||||
fail(
|
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
||||||
"Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
assertNotNull(e.getMessage());
|
assertNotNull(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -164,8 +149,7 @@ public class ThreadAsyncExecutorTest {
|
|||||||
// Our task should only execute once, but it can take a while ...
|
// Our task should only execute once, but it can take a while ...
|
||||||
verify(task, timeout(3000).times(1)).call();
|
verify(task, timeout(3000).times(1)).call();
|
||||||
|
|
||||||
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor
|
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
|
||||||
.forClass((Class) Optional.class);
|
|
||||||
verify(callback, timeout(3000).times(1)).onComplete(eq(result), optionalCaptor.capture());
|
verify(callback, timeout(3000).times(1)).onComplete(eq(result), optionalCaptor.capture());
|
||||||
|
|
||||||
final Optional<Exception> optionalException = optionalCaptor.getValue();
|
final Optional<Exception> optionalException = optionalCaptor.getValue();
|
||||||
@ -182,9 +166,8 @@ public class ThreadAsyncExecutorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a
|
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while
|
||||||
* task takes a while to execute, while waiting on the result using
|
* to execute, while waiting on the result using {@link ThreadAsyncExecutor#endProcess(AsyncResult)}
|
||||||
* {@link ThreadAsyncExecutor#endProcess(AsyncResult)}
|
|
||||||
*/
|
*/
|
||||||
@Test(timeout = 5000)
|
@Test(timeout = 5000)
|
||||||
public void testEndProcess() throws Exception {
|
public void testEndProcess() throws Exception {
|
||||||
@ -204,8 +187,7 @@ public class ThreadAsyncExecutorTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
asyncResult.getValue();
|
asyncResult.getValue();
|
||||||
fail(
|
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
||||||
"Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
assertNotNull(e.getMessage());
|
assertNotNull(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -220,8 +202,7 @@ public class ThreadAsyncExecutorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when
|
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when the callable is 'null'
|
||||||
* the callable is 'null'
|
|
||||||
*/
|
*/
|
||||||
@Test(timeout = 3000)
|
@Test(timeout = 3000)
|
||||||
public void testNullTask() throws Exception {
|
public void testNullTask() throws Exception {
|
||||||
@ -229,8 +210,7 @@ public class ThreadAsyncExecutorTest {
|
|||||||
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
|
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
|
||||||
final AsyncResult<Object> asyncResult = executor.startProcess(null);
|
final AsyncResult<Object> asyncResult = executor.startProcess(null);
|
||||||
|
|
||||||
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.",
|
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult);
|
||||||
asyncResult);
|
|
||||||
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
||||||
assertTrue(asyncResult.isCompleted());
|
assertTrue(asyncResult.isCompleted());
|
||||||
|
|
||||||
@ -246,9 +226,8 @@ public class ThreadAsyncExecutorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test used to verify the behaviour of
|
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when the
|
||||||
* {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when the callable is 'null',
|
* callable is 'null', but the asynchronous callback is provided
|
||||||
* but the asynchronous callback is provided
|
|
||||||
*/
|
*/
|
||||||
@Test(timeout = 3000)
|
@Test(timeout = 3000)
|
||||||
public void testNullTaskWithCallback() throws Exception {
|
public void testNullTaskWithCallback() throws Exception {
|
||||||
@ -257,13 +236,11 @@ public class ThreadAsyncExecutorTest {
|
|||||||
final AsyncCallback<Object> callback = mock(AsyncCallback.class);
|
final AsyncCallback<Object> callback = mock(AsyncCallback.class);
|
||||||
final AsyncResult<Object> asyncResult = executor.startProcess(null, callback);
|
final AsyncResult<Object> asyncResult = executor.startProcess(null, callback);
|
||||||
|
|
||||||
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.",
|
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult);
|
||||||
asyncResult);
|
|
||||||
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
||||||
assertTrue(asyncResult.isCompleted());
|
assertTrue(asyncResult.isCompleted());
|
||||||
|
|
||||||
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor
|
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
|
||||||
.forClass((Class) Optional.class);
|
|
||||||
verify(callback, times(1)).onComplete(Matchers.isNull(), optionalCaptor.capture());
|
verify(callback, times(1)).onComplete(Matchers.isNull(), optionalCaptor.capture());
|
||||||
|
|
||||||
final Optional<Exception> optionalException = optionalCaptor.getValue();
|
final Optional<Exception> optionalException = optionalCaptor.getValue();
|
||||||
@ -286,9 +263,8 @@ public class ThreadAsyncExecutorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test used to verify the behaviour of
|
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when both
|
||||||
* {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when both the callable and
|
* the callable and the asynchronous callback are 'null'
|
||||||
* the asynchronous callback are 'null'
|
|
||||||
*/
|
*/
|
||||||
@Test(timeout = 3000)
|
@Test(timeout = 3000)
|
||||||
public void testNullTaskWithNullCallback() throws Exception {
|
public void testNullTaskWithNullCallback() throws Exception {
|
||||||
@ -296,9 +272,7 @@ public class ThreadAsyncExecutorTest {
|
|||||||
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
|
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
|
||||||
final AsyncResult<Object> asyncResult = executor.startProcess(null, null);
|
final AsyncResult<Object> asyncResult = executor.startProcess(null, null);
|
||||||
|
|
||||||
assertNotNull(
|
assertNotNull("The AsyncResult should not be 'null', even though the task and callback were 'null'.", asyncResult);
|
||||||
"The AsyncResult should not be 'null', even though the task and callback were 'null'.",
|
|
||||||
asyncResult);
|
|
||||||
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
||||||
assertTrue(asyncResult.isCompleted());
|
assertTrue(asyncResult.isCompleted());
|
||||||
|
|
||||||
|
@ -7,17 +7,20 @@ categories: Structural
|
|||||||
tags:
|
tags:
|
||||||
- Java
|
- Java
|
||||||
- Gang Of Four
|
- Gang Of Four
|
||||||
|
- Difficulty-Intermediate
|
||||||
---
|
---
|
||||||
|
|
||||||
**Also known as:** Handle/Body
|
## Also known as
|
||||||
|
Handle/Body
|
||||||
|
|
||||||
**Intent:** Decouple an abstraction from its implementation so that the two can
|
## Intent
|
||||||
|
Decouple an abstraction from its implementation so that the two can
|
||||||
vary independently.
|
vary independently.
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Bridge pattern when
|
## Applicability
|
||||||
|
Use the Bridge pattern when
|
||||||
|
|
||||||
* you want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time.
|
* you want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time.
|
||||||
* both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently
|
* both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently
|
||||||
@ -25,6 +28,6 @@ vary independently.
|
|||||||
* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies
|
* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies
|
||||||
* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
|
* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
|
||||||
|
|
||||||
**Credits**
|
## Credits
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>bridge</artifactId>
|
<artifactId>bridge</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -2,8 +2,6 @@ package com.iluwatar.bridge;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.iluwatar.bridge.App;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Application test
|
* Application test
|
||||||
|
@ -7,24 +7,27 @@ categories: Creational
|
|||||||
tags:
|
tags:
|
||||||
- Java
|
- Java
|
||||||
- Gang Of Four
|
- Gang Of Four
|
||||||
|
- Difficulty-Intermediate
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Separate the construction of a complex object from its
|
## Intent
|
||||||
|
Separate the construction of a complex object from its
|
||||||
representation so that the same construction process can create different
|
representation so that the same construction process can create different
|
||||||
representations.
|
representations.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Builder pattern when
|
## Applicability
|
||||||
|
Use the Builder pattern when
|
||||||
|
|
||||||
* the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled
|
* the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled
|
||||||
* the construction process must allow different representations for the object that's constructed
|
* the construction process must allow different representations for the object that's constructed
|
||||||
|
|
||||||
**Real world examples:**
|
## Real world examples
|
||||||
|
|
||||||
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
|
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
|
||||||
* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
|
* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
|
||||||
|
|
||||||
**Credits**
|
## Credits
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>builder</artifactId>
|
<artifactId>builder</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -93,6 +93,9 @@ public class Hero {
|
|||||||
private Armor armor;
|
private Armor armor;
|
||||||
private Weapon weapon;
|
private Weapon weapon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public HeroBuilder(Profession profession, String name) {
|
public HeroBuilder(Profession profession, String name) {
|
||||||
if (profession == null || name == null) {
|
if (profession == null || name == null) {
|
||||||
throw new IllegalArgumentException("profession and name can not be null");
|
throw new IllegalArgumentException("profession and name can not be null");
|
||||||
|
@ -2,8 +2,6 @@ package com.iluwatar.builder;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.iluwatar.builder.App;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Application test
|
* Application test
|
||||||
|
@ -4,17 +4,21 @@ title: Business Delegate
|
|||||||
folder: business-delegate
|
folder: business-delegate
|
||||||
permalink: /patterns/business-delegate/
|
permalink: /patterns/business-delegate/
|
||||||
categories: Business Tier
|
categories: Business Tier
|
||||||
tags: Java
|
tags:
|
||||||
|
- Java
|
||||||
|
- Difficulty-Intermediate
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** The Business Delegate pattern adds an abstraction layer between
|
## Intent
|
||||||
|
The Business Delegate pattern adds an abstraction layer between
|
||||||
presentation and business tiers. By using the pattern we gain loose coupling
|
presentation and business tiers. By using the pattern we gain loose coupling
|
||||||
between the tiers and encapsulate knowledge about how to locate, connect to,
|
between the tiers and encapsulate knowledge about how to locate, connect to,
|
||||||
and interact with the business objects that make up the application.
|
and interact with the business objects that make up the application.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Business Delegate pattern when
|
## Applicability
|
||||||
|
Use the Business Delegate pattern when
|
||||||
|
|
||||||
* you want loose coupling between presentation and business tiers
|
* you want loose coupling between presentation and business tiers
|
||||||
* you want to orchestrate calls to multiple business services
|
* you want to orchestrate calls to multiple business services
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>business-delegate</artifactId>
|
<artifactId>business-delegate</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -6,19 +6,23 @@ permalink: /patterns/caching/
|
|||||||
categories: Other
|
categories: Other
|
||||||
tags:
|
tags:
|
||||||
- Java
|
- Java
|
||||||
|
- Difficulty-Intermediate
|
||||||
|
- Performance
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** To avoid expensive re-acquisition of resources by not releasing
|
## Intent
|
||||||
|
To avoid expensive re-acquisition of resources by not releasing
|
||||||
the resources immediately after their use. The resources retain their identity, are kept in some
|
the resources immediately after their use. The resources retain their identity, are kept in some
|
||||||
fast-access storage, and are re-used to avoid having to acquire them again.
|
fast-access storage, and are re-used to avoid having to acquire them again.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Caching pattern(s) when
|
## Applicability
|
||||||
|
Use the Caching pattern(s) when
|
||||||
|
|
||||||
* Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead.
|
* Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead.
|
||||||
|
|
||||||
**Credits**
|
## Credits
|
||||||
|
|
||||||
* [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained)
|
* [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained)
|
||||||
* [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177)
|
* [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>caching</artifactId>
|
<artifactId>caching</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -21,7 +21,7 @@ package com.iluwatar.caching;
|
|||||||
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts
|
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts
|
||||||
* a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The three
|
* a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The three
|
||||||
* strategies are individually tested. The testing of the cache is restricted towards saving and
|
* strategies are individually tested. The testing of the cache is restricted towards saving and
|
||||||
* querying of user accounts from the underlying data store ( {@link DBManager}). The main class (
|
* querying of user accounts from the underlying data store ( {@link DbManager}). The main class (
|
||||||
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
|
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
|
||||||
* whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
|
* 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
|
* ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
|
||||||
@ -43,7 +43,7 @@ public class App {
|
|||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
AppManager.initDB(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
|
AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
|
||||||
// and the App class to avoid Maven compilation errors. Set flag to
|
// and the App class to avoid Maven compilation errors. Set flag to
|
||||||
// true to run the tests with MongoDB (provided that MongoDB is
|
// true to run the tests with MongoDB (provided that MongoDB is
|
||||||
// installed and socket connection is open).
|
// installed and socket connection is open).
|
||||||
@ -65,8 +65,8 @@ public class App {
|
|||||||
|
|
||||||
AppManager.save(userAccount1);
|
AppManager.save(userAccount1);
|
||||||
System.out.println(AppManager.printCacheContent());
|
System.out.println(AppManager.printCacheContent());
|
||||||
userAccount1 = AppManager.find("001");
|
AppManager.find("001");
|
||||||
userAccount1 = AppManager.find("001");
|
AppManager.find("001");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,15 +80,15 @@ public class App {
|
|||||||
|
|
||||||
AppManager.save(userAccount2);
|
AppManager.save(userAccount2);
|
||||||
System.out.println(AppManager.printCacheContent());
|
System.out.println(AppManager.printCacheContent());
|
||||||
userAccount2 = AppManager.find("002");
|
AppManager.find("002");
|
||||||
System.out.println(AppManager.printCacheContent());
|
System.out.println(AppManager.printCacheContent());
|
||||||
userAccount2 = AppManager.find("002");
|
userAccount2 = AppManager.find("002");
|
||||||
userAccount2.setUserName("Jane G.");
|
userAccount2.setUserName("Jane G.");
|
||||||
AppManager.save(userAccount2);
|
AppManager.save(userAccount2);
|
||||||
System.out.println(AppManager.printCacheContent());
|
System.out.println(AppManager.printCacheContent());
|
||||||
userAccount2 = AppManager.find("002");
|
AppManager.find("002");
|
||||||
System.out.println(AppManager.printCacheContent());
|
System.out.println(AppManager.printCacheContent());
|
||||||
userAccount2 = AppManager.find("002");
|
AppManager.find("002");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,12 +106,12 @@ public class App {
|
|||||||
AppManager.save(userAccount4);
|
AppManager.save(userAccount4);
|
||||||
AppManager.save(userAccount5);
|
AppManager.save(userAccount5);
|
||||||
System.out.println(AppManager.printCacheContent());
|
System.out.println(AppManager.printCacheContent());
|
||||||
userAccount3 = AppManager.find("003");
|
AppManager.find("003");
|
||||||
System.out.println(AppManager.printCacheContent());
|
System.out.println(AppManager.printCacheContent());
|
||||||
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
|
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
|
||||||
AppManager.save(userAccount6);
|
AppManager.save(userAccount6);
|
||||||
System.out.println(AppManager.printCacheContent());
|
System.out.println(AppManager.printCacheContent());
|
||||||
userAccount4 = AppManager.find("004");
|
AppManager.find("004");
|
||||||
System.out.println(AppManager.printCacheContent());
|
System.out.println(AppManager.printCacheContent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,24 +15,30 @@ public class AppManager {
|
|||||||
|
|
||||||
private static CachingPolicy cachingPolicy;
|
private static CachingPolicy cachingPolicy;
|
||||||
|
|
||||||
|
private AppManager() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* 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 MongoDB as its underlying
|
||||||
* data storage or a simple Java data structure to (temporarily) store the data/objects during
|
* data storage or a simple Java data structure to (temporarily) store the data/objects during
|
||||||
* runtime.
|
* runtime.
|
||||||
*/
|
*/
|
||||||
public static void initDB(boolean useMongoDB) {
|
public static void initDb(boolean useMongoDb) {
|
||||||
if (useMongoDB) {
|
if (useMongoDb) {
|
||||||
try {
|
try {
|
||||||
DBManager.connect();
|
DbManager.connect();
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DBManager.createVirtualDB();
|
DbManager.createVirtualDb();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize caching policy
|
||||||
|
*/
|
||||||
public static void initCachingPolicy(CachingPolicy policy) {
|
public static void initCachingPolicy(CachingPolicy policy) {
|
||||||
cachingPolicy = policy;
|
cachingPolicy = policy;
|
||||||
if (cachingPolicy == CachingPolicy.BEHIND) {
|
if (cachingPolicy == CachingPolicy.BEHIND) {
|
||||||
@ -50,15 +56,21 @@ public class AppManager {
|
|||||||
CacheStore.initCapacity(capacity);
|
CacheStore.initCapacity(capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserAccount find(String userID) {
|
/**
|
||||||
|
* Find user account
|
||||||
|
*/
|
||||||
|
public static UserAccount find(String userId) {
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
|
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
|
||||||
return CacheStore.readThrough(userID);
|
return CacheStore.readThrough(userId);
|
||||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
||||||
return CacheStore.readThroughWithWriteBackPolicy(userID);
|
return CacheStore.readThroughWithWriteBackPolicy(userId);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save user account
|
||||||
|
*/
|
||||||
public static void save(UserAccount userAccount) {
|
public static void save(UserAccount userAccount) {
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH) {
|
if (cachingPolicy == CachingPolicy.THROUGH) {
|
||||||
CacheStore.writeThrough(userAccount);
|
CacheStore.writeThrough(userAccount);
|
||||||
|
@ -9,88 +9,118 @@ import java.util.ArrayList;
|
|||||||
*/
|
*/
|
||||||
public class CacheStore {
|
public class CacheStore {
|
||||||
|
|
||||||
static LRUCache cache = null;
|
static LruCache cache = null;
|
||||||
|
|
||||||
|
private CacheStore() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init cache capacity
|
||||||
|
*/
|
||||||
public static void initCapacity(int capacity) {
|
public static void initCapacity(int capacity) {
|
||||||
if (null == cache)
|
if (null == cache) {
|
||||||
cache = new LRUCache(capacity);
|
cache = new LruCache(capacity);
|
||||||
else
|
} else {
|
||||||
cache.setCapacity(capacity);
|
cache.setCapacity(capacity);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static UserAccount readThrough(String userID) {
|
/**
|
||||||
if (cache.contains(userID)) {
|
* Get user account using read-through cache
|
||||||
|
*/
|
||||||
|
public static UserAccount readThrough(String userId) {
|
||||||
|
if (cache.contains(userId)) {
|
||||||
System.out.println("# Cache Hit!");
|
System.out.println("# Cache Hit!");
|
||||||
return cache.get(userID);
|
return cache.get(userId);
|
||||||
}
|
}
|
||||||
System.out.println("# Cache Miss!");
|
System.out.println("# Cache Miss!");
|
||||||
UserAccount userAccount = DBManager.readFromDB(userID);
|
UserAccount userAccount = DbManager.readFromDb(userId);
|
||||||
cache.set(userID, userAccount);
|
cache.set(userId, userAccount);
|
||||||
return userAccount;
|
return userAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user account using write-through cache
|
||||||
|
*/
|
||||||
public static void writeThrough(UserAccount userAccount) {
|
public static void writeThrough(UserAccount userAccount) {
|
||||||
if (cache.contains(userAccount.getUserID())) {
|
if (cache.contains(userAccount.getUserId())) {
|
||||||
DBManager.updateDB(userAccount);
|
DbManager.updateDb(userAccount);
|
||||||
} else {
|
} else {
|
||||||
DBManager.writeToDB(userAccount);
|
DbManager.writeToDb(userAccount);
|
||||||
}
|
}
|
||||||
cache.set(userAccount.getUserID(), userAccount);
|
cache.set(userAccount.getUserId(), userAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user account using write-around cache
|
||||||
|
*/
|
||||||
public static void writeAround(UserAccount userAccount) {
|
public static void writeAround(UserAccount userAccount) {
|
||||||
if (cache.contains(userAccount.getUserID())) {
|
if (cache.contains(userAccount.getUserId())) {
|
||||||
DBManager.updateDB(userAccount);
|
DbManager.updateDb(userAccount);
|
||||||
cache.invalidate(userAccount.getUserID()); // Cache data has been updated -- remove older
|
cache.invalidate(userAccount.getUserId()); // Cache data has been updated -- remove older
|
||||||
// version from cache.
|
// version from cache.
|
||||||
} else {
|
} else {
|
||||||
DBManager.writeToDB(userAccount);
|
DbManager.writeToDb(userAccount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserAccount readThroughWithWriteBackPolicy(String userID) {
|
/**
|
||||||
if (cache.contains(userID)) {
|
* Get user account using read-through cache with write-back policy
|
||||||
|
*/
|
||||||
|
public static UserAccount readThroughWithWriteBackPolicy(String userId) {
|
||||||
|
if (cache.contains(userId)) {
|
||||||
System.out.println("# Cache Hit!");
|
System.out.println("# Cache Hit!");
|
||||||
return cache.get(userID);
|
return cache.get(userId);
|
||||||
}
|
}
|
||||||
System.out.println("# Cache Miss!");
|
System.out.println("# Cache Miss!");
|
||||||
UserAccount userAccount = DBManager.readFromDB(userID);
|
UserAccount userAccount = DbManager.readFromDb(userId);
|
||||||
if (cache.isFull()) {
|
if (cache.isFull()) {
|
||||||
System.out.println("# Cache is FULL! Writing LRU data to DB...");
|
System.out.println("# 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set user account
|
||||||
|
*/
|
||||||
public static void writeBehind(UserAccount userAccount) {
|
public static void writeBehind(UserAccount userAccount) {
|
||||||
if (cache.isFull() && !cache.contains(userAccount.getUserID())) {
|
if (cache.isFull() && !cache.contains(userAccount.getUserId())) {
|
||||||
System.out.println("# Cache is FULL! Writing LRU data to DB...");
|
System.out.println("# 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears cache
|
||||||
|
*/
|
||||||
public static void clearCache() {
|
public static void clearCache() {
|
||||||
if (null != cache)
|
if (null != cache) {
|
||||||
cache.clear();
|
cache.clear();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes remaining content in the cache into the DB.
|
* Writes remaining content in the cache into the DB.
|
||||||
*/
|
*/
|
||||||
public static void flushCache() {
|
public static void flushCache() {
|
||||||
System.out.println("# flushCache...");
|
System.out.println("# flushCache...");
|
||||||
if (null == cache)
|
if (null == cache) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
|
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
|
||||||
for (UserAccount userAccount : listOfUserAccounts) {
|
for (UserAccount userAccount : listOfUserAccounts) {
|
||||||
DBManager.upsertDB(userAccount);
|
DbManager.upsertDb(userAccount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print user accounts
|
||||||
|
*/
|
||||||
public static String print() {
|
public static String print() {
|
||||||
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
|
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
@ -21,7 +21,7 @@ import com.mongodb.client.model.UpdateOptions;
|
|||||||
* during runtime (createVirtualDB()).</p>
|
* during runtime (createVirtualDB()).</p>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class DBManager {
|
public class DbManager {
|
||||||
|
|
||||||
private static MongoClient mongoClient;
|
private static MongoClient mongoClient;
|
||||||
private static MongoDatabase db;
|
private static MongoDatabase db;
|
||||||
@ -29,21 +29,34 @@ public class DBManager {
|
|||||||
|
|
||||||
private static HashMap<String, UserAccount> virtualDB;
|
private static HashMap<String, UserAccount> virtualDB;
|
||||||
|
|
||||||
public static void createVirtualDB() {
|
private DbManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DB
|
||||||
|
*/
|
||||||
|
public static void createVirtualDb() {
|
||||||
useMongoDB = false;
|
useMongoDB = false;
|
||||||
virtualDB = new HashMap<String, UserAccount>();
|
virtualDB = new HashMap<String, UserAccount>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to DB
|
||||||
|
*/
|
||||||
public static void connect() throws ParseException {
|
public static void connect() throws ParseException {
|
||||||
useMongoDB = true;
|
useMongoDB = true;
|
||||||
mongoClient = new MongoClient();
|
mongoClient = new MongoClient();
|
||||||
db = mongoClient.getDatabase("test");
|
db = mongoClient.getDatabase("test");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserAccount readFromDB(String userID) {
|
/**
|
||||||
|
* Read user account from DB
|
||||||
|
*/
|
||||||
|
public static UserAccount readFromDb(String userId) {
|
||||||
if (!useMongoDB) {
|
if (!useMongoDB) {
|
||||||
if (virtualDB.containsKey(userID))
|
if (virtualDB.containsKey(userId)) {
|
||||||
return virtualDB.get(userID);
|
return virtualDB.get(userId);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (null == db) {
|
if (null == db) {
|
||||||
@ -54,18 +67,22 @@ public class DBManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
FindIterable<Document> iterable =
|
FindIterable<Document> iterable =
|
||||||
db.getCollection("user_accounts").find(new Document("userID", userID));
|
db.getCollection("user_accounts").find(new Document("userID", userId));
|
||||||
if (iterable == null)
|
if (iterable == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
Document doc = iterable.first();
|
Document doc = iterable.first();
|
||||||
UserAccount userAccount =
|
UserAccount userAccount =
|
||||||
new UserAccount(userID, doc.getString("userName"), doc.getString("additionalInfo"));
|
new UserAccount(userId, doc.getString("userName"), doc.getString("additionalInfo"));
|
||||||
return userAccount;
|
return userAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeToDB(UserAccount userAccount) {
|
/**
|
||||||
|
* Write user account to DB
|
||||||
|
*/
|
||||||
|
public static void writeToDb(UserAccount userAccount) {
|
||||||
if (!useMongoDB) {
|
if (!useMongoDB) {
|
||||||
virtualDB.put(userAccount.getUserID(), userAccount);
|
virtualDB.put(userAccount.getUserId(), userAccount);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (null == db) {
|
if (null == db) {
|
||||||
@ -76,13 +93,16 @@ public class DBManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
db.getCollection("user_accounts").insertOne(
|
db.getCollection("user_accounts").insertOne(
|
||||||
new Document("userID", userAccount.getUserID()).append("userName",
|
new Document("userID", userAccount.getUserId()).append("userName",
|
||||||
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo()));
|
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateDB(UserAccount userAccount) {
|
/**
|
||||||
|
* Update DB
|
||||||
|
*/
|
||||||
|
public static void updateDb(UserAccount userAccount) {
|
||||||
if (!useMongoDB) {
|
if (!useMongoDB) {
|
||||||
virtualDB.put(userAccount.getUserID(), userAccount);
|
virtualDB.put(userAccount.getUserId(), userAccount);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (null == db) {
|
if (null == db) {
|
||||||
@ -93,7 +113,7 @@ public class DBManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
db.getCollection("user_accounts").updateOne(
|
db.getCollection("user_accounts").updateOne(
|
||||||
new Document("userID", userAccount.getUserID()),
|
new Document("userID", userAccount.getUserId()),
|
||||||
new Document("$set", new Document("userName", userAccount.getUserName()).append(
|
new Document("$set", new Document("userName", userAccount.getUserName()).append(
|
||||||
"additionalInfo", userAccount.getAdditionalInfo())));
|
"additionalInfo", userAccount.getAdditionalInfo())));
|
||||||
}
|
}
|
||||||
@ -102,9 +122,9 @@ public class DBManager {
|
|||||||
*
|
*
|
||||||
* Insert data into DB if it does not exist. Else, update it.
|
* Insert data into DB if it does not exist. Else, update it.
|
||||||
*/
|
*/
|
||||||
public static void upsertDB(UserAccount userAccount) {
|
public static void upsertDb(UserAccount userAccount) {
|
||||||
if (!useMongoDB) {
|
if (!useMongoDB) {
|
||||||
virtualDB.put(userAccount.getUserID(), userAccount);
|
virtualDB.put(userAccount.getUserId(), userAccount);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (null == db) {
|
if (null == db) {
|
||||||
@ -115,8 +135,8 @@ public class DBManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
db.getCollection("user_accounts").updateOne(
|
db.getCollection("user_accounts").updateOne(
|
||||||
new Document("userID", userAccount.getUserID()),
|
new Document("userID", userAccount.getUserId()),
|
||||||
new Document("$set", new Document("userID", userAccount.getUserID()).append("userName",
|
new Document("$set", new Document("userID", userAccount.getUserId()).append("userName",
|
||||||
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo())),
|
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo())),
|
||||||
new UpdateOptions().upsert(true));
|
new UpdateOptions().upsert(true));
|
||||||
}
|
}
|
@ -12,16 +12,16 @@ import java.util.HashMap;
|
|||||||
* LRU data is always at the end of the list.
|
* LRU data is always at the end of the list.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class LRUCache {
|
public class LruCache {
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
String userID;
|
String userId;
|
||||||
UserAccount userAccount;
|
UserAccount userAccount;
|
||||||
Node previous;
|
Node previous;
|
||||||
Node next;
|
Node next;
|
||||||
|
|
||||||
public Node(String userID, UserAccount userAccount) {
|
public Node(String userId, UserAccount userAccount) {
|
||||||
this.userID = userID;
|
this.userId = userId;
|
||||||
this.userAccount = userAccount;
|
this.userAccount = userAccount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,13 +31,16 @@ public class LRUCache {
|
|||||||
Node head = null;
|
Node head = null;
|
||||||
Node end = null;
|
Node end = null;
|
||||||
|
|
||||||
public LRUCache(int capacity) {
|
public LruCache(int capacity) {
|
||||||
this.capacity = capacity;
|
this.capacity = capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserAccount get(String userID) {
|
/**
|
||||||
if (cache.containsKey(userID)) {
|
* Get user account
|
||||||
Node node = cache.get(userID);
|
*/
|
||||||
|
public UserAccount get(String userId) {
|
||||||
|
if (cache.containsKey(userId)) {
|
||||||
|
Node node = cache.get(userId);
|
||||||
remove(node);
|
remove(node);
|
||||||
setHead(node);
|
setHead(node);
|
||||||
return node.userAccount;
|
return node.userAccount;
|
||||||
@ -69,52 +72,63 @@ public class LRUCache {
|
|||||||
public void setHead(Node node) {
|
public void setHead(Node node) {
|
||||||
node.next = head;
|
node.next = head;
|
||||||
node.previous = null;
|
node.previous = null;
|
||||||
if (head != null)
|
if (head != null) {
|
||||||
head.previous = node;
|
head.previous = node;
|
||||||
|
}
|
||||||
head = node;
|
head = node;
|
||||||
if (end == null)
|
if (end == null) {
|
||||||
end = head;
|
end = head;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void set(String userID, UserAccount userAccount) {
|
/**
|
||||||
if (cache.containsKey(userID)) {
|
* Set user account
|
||||||
Node old = cache.get(userID);
|
*/
|
||||||
|
public void set(String userId, UserAccount userAccount) {
|
||||||
|
if (cache.containsKey(userId)) {
|
||||||
|
Node old = cache.get(userId);
|
||||||
old.userAccount = userAccount;
|
old.userAccount = userAccount;
|
||||||
remove(old);
|
remove(old);
|
||||||
setHead(old);
|
setHead(old);
|
||||||
} else {
|
} else {
|
||||||
Node newNode = new Node(userID, userAccount);
|
Node newNode = new Node(userId, userAccount);
|
||||||
if (cache.size() >= capacity) {
|
if (cache.size() >= capacity) {
|
||||||
System.out.println("# Cache is FULL! Removing " + end.userID + " from cache...");
|
System.out.println("# Cache is FULL! Removing " + end.userId + " from cache...");
|
||||||
cache.remove(end.userID); // remove LRU data from cache.
|
cache.remove(end.userId); // remove LRU data from cache.
|
||||||
remove(end);
|
remove(end);
|
||||||
setHead(newNode);
|
setHead(newNode);
|
||||||
} else {
|
} else {
|
||||||
setHead(newNode);
|
setHead(newNode);
|
||||||
}
|
}
|
||||||
cache.put(userID, newNode);
|
cache.put(userId, newNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean contains(String userID) {
|
public boolean contains(String userId) {
|
||||||
return cache.containsKey(userID);
|
return cache.containsKey(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidate(String userID) {
|
/**
|
||||||
System.out.println("# " + userID + " has been updated! Removing older version from cache...");
|
* Invalidate cache for user
|
||||||
Node toBeRemoved = cache.get(userID);
|
*/
|
||||||
|
public void invalidate(String userId) {
|
||||||
|
System.out.println("# " + userId + " has been updated! Removing older version from cache...");
|
||||||
|
Node toBeRemoved = cache.get(userId);
|
||||||
remove(toBeRemoved);
|
remove(toBeRemoved);
|
||||||
cache.remove(userID);
|
cache.remove(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFull() {
|
public boolean isFull() {
|
||||||
return cache.size() >= capacity;
|
return cache.size() >= capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserAccount getLRUData() {
|
public UserAccount getLruData() {
|
||||||
return end.userAccount;
|
return end.userAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear cache
|
||||||
|
*/
|
||||||
public void clear() {
|
public void clear() {
|
||||||
head = null;
|
head = null;
|
||||||
end = null;
|
end = null;
|
||||||
@ -135,6 +149,9 @@ public class LRUCache {
|
|||||||
return listOfCacheData;
|
return listOfCacheData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set cache capacity
|
||||||
|
*/
|
||||||
public void setCapacity(int newCapacity) {
|
public void setCapacity(int newCapacity) {
|
||||||
if (capacity > newCapacity) {
|
if (capacity > newCapacity) {
|
||||||
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll
|
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll
|
@ -6,22 +6,25 @@ package com.iluwatar.caching;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class UserAccount {
|
public class UserAccount {
|
||||||
private String userID;
|
private String userId;
|
||||||
private String userName;
|
private String userName;
|
||||||
private String additionalInfo;
|
private String additionalInfo;
|
||||||
|
|
||||||
public UserAccount(String userID, String userName, String additionalInfo) {
|
/**
|
||||||
this.userID = userID;
|
* Constructor
|
||||||
|
*/
|
||||||
|
public UserAccount(String userId, String userName, String additionalInfo) {
|
||||||
|
this.userId = userId;
|
||||||
this.userName = userName;
|
this.userName = userName;
|
||||||
this.additionalInfo = additionalInfo;
|
this.additionalInfo = additionalInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserID() {
|
public String getUserId() {
|
||||||
return userID;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUserID(String userID) {
|
public void setUserId(String userId) {
|
||||||
this.userID = userID;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserName() {
|
public String getUserName() {
|
||||||
@ -42,6 +45,6 @@ public class UserAccount {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return userID + ", " + userName + ", " + additionalInfo;
|
return userId + ", " + userName + ", " + additionalInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ public class AppTest {
|
|||||||
*/
|
*/
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
AppManager.initDB(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
|
AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
|
||||||
// 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).
|
||||||
|
@ -4,19 +4,25 @@ title: Callback
|
|||||||
folder: callback
|
folder: callback
|
||||||
permalink: /patterns/callback/
|
permalink: /patterns/callback/
|
||||||
categories: Other
|
categories: Other
|
||||||
tags: Java
|
tags:
|
||||||
|
- Java
|
||||||
|
- Difficulty-Beginner
|
||||||
|
- Functional
|
||||||
|
- Idiom
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Callback is a piece of executable code that is passed as an
|
## Intent
|
||||||
|
Callback is a piece of executable code that is passed as an
|
||||||
argument to other code, which is expected to call back (execute) the argument
|
argument to other code, which is expected to call back (execute) the argument
|
||||||
at some convenient time.
|
at some convenient time.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Callback pattern when
|
## Applicability
|
||||||
|
Use the Callback pattern when
|
||||||
|
|
||||||
* when some arbitrary synchronous or asynchronous action must be performed after execution of some defined activity.
|
* when some arbitrary synchronous or asynchronous action must be performed after execution of some defined activity.
|
||||||
|
|
||||||
**Real world examples:**
|
## Real world examples
|
||||||
|
|
||||||
* [CyclicBarrier] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept callback that will be triggered every time when barrier is tripped.
|
* [CyclicBarrier] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept callback that will be triggered every time when barrier is tripped.
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>callback</artifactId>
|
<artifactId>callback</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -9,6 +9,9 @@ package com.iluwatar.callback;
|
|||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Program entry point
|
||||||
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Task task = new SimpleTask();
|
Task task = new SimpleTask();
|
||||||
Callback callback = new Callback() {
|
Callback callback = new Callback() {
|
||||||
|
@ -7,5 +7,5 @@ package com.iluwatar.callback;
|
|||||||
*/
|
*/
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
|
|
||||||
public void call();
|
void call();
|
||||||
}
|
}
|
||||||
|
19
callback/src/main/java/com/iluwatar/callback/LambdasApp.java
Normal file
19
callback/src/main/java/com/iluwatar/callback/LambdasApp.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.iluwatar.callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This example generates the exact same output as {@link App} however the callback has been
|
||||||
|
* defined as a Lambdas expression.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LambdasApp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Program entry point
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Task task = new SimpleTask();
|
||||||
|
Callback c = () -> System.out.println("I'm done now.");
|
||||||
|
task.executeWith(c);
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,9 @@ package com.iluwatar.callback;
|
|||||||
*/
|
*/
|
||||||
public abstract class Task {
|
public abstract class Task {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute with callback
|
||||||
|
*/
|
||||||
public final void executeWith(Callback callback) {
|
public final void executeWith(Callback callback) {
|
||||||
execute();
|
execute();
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
|
@ -36,4 +36,22 @@ public class AppTest {
|
|||||||
assertEquals("Callback called twice", new Integer(2), callingCount);
|
assertEquals("Callback called twice", new Integer(2), callingCount);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithLambdasExample() {
|
||||||
|
Callback callback = () -> callingCount++;
|
||||||
|
|
||||||
|
Task task = new SimpleTask();
|
||||||
|
|
||||||
|
assertEquals("Initial calling count of 0", new Integer(0), callingCount);
|
||||||
|
|
||||||
|
task.executeWith(callback);
|
||||||
|
|
||||||
|
assertEquals("Callback called once", new Integer(1), callingCount);
|
||||||
|
|
||||||
|
task.executeWith(callback);
|
||||||
|
|
||||||
|
assertEquals("Callback called twice", new Integer(2), callingCount);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,25 +7,28 @@ categories: Behavioral
|
|||||||
tags:
|
tags:
|
||||||
- Java
|
- Java
|
||||||
- Gang Of Four
|
- Gang Of Four
|
||||||
|
- Difficulty-Intermediate
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Avoid coupling the sender of a request to its receiver by giving
|
## Intent
|
||||||
|
Avoid coupling the sender of a request to its receiver by giving
|
||||||
more than one object a chance to handle the request. Chain the receiving
|
more than one object a chance to handle the request. Chain the receiving
|
||||||
objects and pass the request along the chain until an object handles it.
|
objects and pass the request along the chain until an object handles it.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use Chain of Responsibility when
|
## Applicability
|
||||||
|
Use Chain of Responsibility when
|
||||||
|
|
||||||
* more than one object may handle a request, and the handler isn't known a priori. The handler should be ascertained automatically
|
* more than one object may handle a request, and the handler isn't known a priori. The handler should be ascertained automatically
|
||||||
* you want to issue a request to one of several objects without specifying the receiver explicitly
|
* you want to issue a request to one of several objects without specifying the receiver explicitly
|
||||||
* the set of objects that can handle a request should be specified dynamically
|
* the set of objects that can handle a request should be specified dynamically
|
||||||
|
|
||||||
**Real world examples:**
|
## Real world examples
|
||||||
|
|
||||||
* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29)
|
* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29)
|
||||||
* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html)
|
* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html)
|
||||||
|
|
||||||
**Credits**
|
## Credits
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>chain</artifactId>
|
<artifactId>chain</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -13,6 +13,9 @@ public abstract class RequestHandler {
|
|||||||
this.next = next;
|
this.next = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request handler
|
||||||
|
*/
|
||||||
public void handleRequest(Request req) {
|
public void handleRequest(Request req) {
|
||||||
if (next != null) {
|
if (next != null) {
|
||||||
next.handleRequest(req);
|
next.handleRequest(req);
|
||||||
|
@ -2,8 +2,6 @@ package com.iluwatar.chain;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.iluwatar.chain.App;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Application test
|
* Application test
|
||||||
|
9
checkstyle-suppressions.xml
Normal file
9
checkstyle-suppressions.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||||
|
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||||
|
<suppressions>
|
||||||
|
<suppress checks="AvoidStarImport" files="[\\/]src[\\/]test[\\/]java[\\/]"/>
|
||||||
|
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]test[\\/]resources[\\/]"/>
|
||||||
|
<suppress checks="[a-zA-Z0-9]*" files="[\\/]build[\\/]generated-sources[\\/]"/>
|
||||||
|
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]main[\\/]resources[\\/]"/>
|
||||||
|
</suppressions>
|
@ -26,7 +26,9 @@
|
|||||||
<module name="Checker">
|
<module name="Checker">
|
||||||
<property name="charset" value="UTF-8"/>
|
<property name="charset" value="UTF-8"/>
|
||||||
|
|
||||||
<property name="severity" value="warning"/>
|
<property name="fileExtensions" value="java, xml, properties"/>
|
||||||
|
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
|
||||||
<!-- Checks for whitespace -->
|
<!-- Checks for whitespace -->
|
||||||
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
||||||
@ -48,7 +50,7 @@
|
|||||||
<property name="allowNonPrintableEscapes" value="true"/>
|
<property name="allowNonPrintableEscapes" value="true"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="LineLength">
|
<module name="LineLength">
|
||||||
<property name="max" value="100"/>
|
<property name="max" value="120"/>
|
||||||
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="AvoidStarImport"/>
|
<module name="AvoidStarImport"/>
|
||||||
@ -61,7 +63,7 @@
|
|||||||
</module>
|
</module>
|
||||||
<module name="NeedBraces"/>
|
<module name="NeedBraces"/>
|
||||||
<module name="LeftCurly">
|
<module name="LeftCurly">
|
||||||
<property name="maxLineLength" value="100"/>
|
<property name="maxLineLength" value="120"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="RightCurly"/>
|
<module name="RightCurly"/>
|
||||||
<module name="RightCurly">
|
<module name="RightCurly">
|
||||||
@ -86,9 +88,6 @@
|
|||||||
<module name="FallThrough"/>
|
<module name="FallThrough"/>
|
||||||
<module name="UpperEll"/>
|
<module name="UpperEll"/>
|
||||||
<module name="ModifierOrder"/>
|
<module name="ModifierOrder"/>
|
||||||
<module name="EmptyLineSeparator">
|
|
||||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
|
||||||
</module>
|
|
||||||
<module name="SeparatorWrap">
|
<module name="SeparatorWrap">
|
||||||
<property name="tokens" value="DOT"/>
|
<property name="tokens" value="DOT"/>
|
||||||
<property name="option" value="nl"/>
|
<property name="option" value="nl"/>
|
||||||
@ -97,42 +96,19 @@
|
|||||||
<property name="tokens" value="COMMA"/>
|
<property name="tokens" value="COMMA"/>
|
||||||
<property name="option" value="EOL"/>
|
<property name="option" value="EOL"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="PackageName">
|
|
||||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
<!-- Checks for Naming Conventions. -->
|
||||||
<message key="name.invalidPattern"
|
<!-- See http://checkstyle.sf.net/config_naming.html -->
|
||||||
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
<module name="ConstantName"/>
|
||||||
</module>
|
<module name="LocalFinalVariableName"/>
|
||||||
<module name="TypeName">
|
<module name="LocalVariableName"/>
|
||||||
<message key="name.invalidPattern"
|
<module name="MemberName"/>
|
||||||
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
<module name="MethodName"/>
|
||||||
</module>
|
<module name="PackageName"/>
|
||||||
<module name="MemberName">
|
<module name="ParameterName"/>
|
||||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
<module name="StaticVariableName"/>
|
||||||
<message key="name.invalidPattern"
|
<module name="TypeName"/>
|
||||||
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
|
||||||
</module>
|
|
||||||
<module name="ParameterName">
|
|
||||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
|
||||||
<message key="name.invalidPattern"
|
|
||||||
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
|
||||||
</module>
|
|
||||||
<module name="LocalVariableName">
|
|
||||||
<property name="tokens" value="VARIABLE_DEF"/>
|
|
||||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
|
||||||
<property name="allowOneCharVarInForLoop" value="true"/>
|
|
||||||
<message key="name.invalidPattern"
|
|
||||||
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
|
||||||
</module>
|
|
||||||
<module name="ClassTypeParameterName">
|
|
||||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
|
||||||
<message key="name.invalidPattern"
|
|
||||||
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
|
||||||
</module>
|
|
||||||
<module name="MethodTypeParameterName">
|
|
||||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
|
||||||
<message key="name.invalidPattern"
|
|
||||||
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
|
||||||
</module>
|
|
||||||
<module name="NoFinalizer"/>
|
<module name="NoFinalizer"/>
|
||||||
<module name="GenericWhitespace">
|
<module name="GenericWhitespace">
|
||||||
<message key="ws.followed"
|
<message key="ws.followed"
|
||||||
@ -157,14 +133,6 @@
|
|||||||
<property name="allowedAbbreviationLength" value="1"/>
|
<property name="allowedAbbreviationLength" value="1"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="OverloadMethodsDeclarationOrder"/>
|
<module name="OverloadMethodsDeclarationOrder"/>
|
||||||
<module name="VariableDeclarationUsageDistance"/>
|
|
||||||
<module name="CustomImportOrder">
|
|
||||||
<property name="thirdPartyPackageRegExp" value=".*"/>
|
|
||||||
<property name="specialImportsRegExp" value="com.google"/>
|
|
||||||
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
|
||||||
<property name="customImportOrderRules"
|
|
||||||
value="STATIC###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE"/>
|
|
||||||
</module>
|
|
||||||
<module name="MethodParamPad"/>
|
<module name="MethodParamPad"/>
|
||||||
<module name="OperatorWrap">
|
<module name="OperatorWrap">
|
||||||
<property name="option" value="NL"/>
|
<property name="option" value="NL"/>
|
||||||
@ -180,11 +148,6 @@
|
|||||||
</module>
|
</module>
|
||||||
<module name="NonEmptyAtclauseDescription"/>
|
<module name="NonEmptyAtclauseDescription"/>
|
||||||
<module name="JavadocTagContinuationIndentation"/>
|
<module name="JavadocTagContinuationIndentation"/>
|
||||||
<module name="SummaryJavadocCheck">
|
|
||||||
<property name="forbiddenSummaryFragments"
|
|
||||||
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
|
||||||
</module>
|
|
||||||
<module name="JavadocParagraph"/>
|
|
||||||
<module name="AtclauseOrder">
|
<module name="AtclauseOrder">
|
||||||
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||||
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||||
@ -195,7 +158,7 @@
|
|||||||
<property name="allowMissingThrowsTags" value="true"/>
|
<property name="allowMissingThrowsTags" value="true"/>
|
||||||
<property name="allowMissingReturnTag" value="true"/>
|
<property name="allowMissingReturnTag" value="true"/>
|
||||||
<property name="minLineCount" value="2"/>
|
<property name="minLineCount" value="2"/>
|
||||||
<property name="allowedAnnotations" value="Override, Test"/>
|
<property name="allowedAnnotations" value="Override, Test, Before, After, Parameters, Given, When, BeforeClass, AfterClass, Parameterized"/>
|
||||||
<property name="allowThrowsTagsForSubclasses" value="true"/>
|
<property name="allowThrowsTagsForSubclasses" value="true"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="MethodName">
|
<module name="MethodName">
|
||||||
@ -205,4 +168,5 @@
|
|||||||
</module>
|
</module>
|
||||||
<module name="SingleLineJavadoc"/>
|
<module name="SingleLineJavadoc"/>
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
</module>
|
</module>
|
||||||
|
@ -7,17 +7,21 @@ categories: Behavioral
|
|||||||
tags:
|
tags:
|
||||||
- Java
|
- Java
|
||||||
- Gang Of Four
|
- Gang Of Four
|
||||||
|
- Difficulty-Intermediate
|
||||||
---
|
---
|
||||||
|
|
||||||
**Also known as:** Action, Transaction
|
## Also known as
|
||||||
|
Action, Transaction
|
||||||
|
|
||||||
**Intent:** Encapsulate a request as an object, thereby letting you
|
## Intent
|
||||||
|
Encapsulate a request as an object, thereby letting you
|
||||||
parameterize clients with different requests, queue or log requests, and
|
parameterize clients with different requests, queue or log requests, and
|
||||||
support undoable operations.
|
support undoable operations.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Command pattern when you want to
|
## Applicability
|
||||||
|
Use the Command pattern when you want to
|
||||||
|
|
||||||
* parameterize objects by an action to perform. You can express such parameterization in a procedural language with a callback function, that is, a function that's registered somewhere to be called at a later point. Commands are an object-oriented replacement for callbacks.
|
* parameterize objects by an action to perform. You can express such parameterization in a procedural language with a callback function, that is, a function that's registered somewhere to be called at a later point. Commands are an object-oriented replacement for callbacks.
|
||||||
* specify, queue, and execute requests at different times. A Command object can have a lifetime independent of the original request. If the receiver of a request can be represented in an address space-independent way, then you can transfer a command object for the request to a different process and fulfill the request there
|
* specify, queue, and execute requests at different times. A Command object can have a lifetime independent of the original request. If the receiver of a request can be represented in an address space-independent way, then you can transfer a command object for the request to a different process and fulfill the request there
|
||||||
@ -25,16 +29,16 @@ support undoable operations.
|
|||||||
* support logging changes so that they can be reapplied in case of a system crash. By augmenting the Command interface with load and store operations, you can keep a persistent log of changes. Recovering from a crash involves reloading logged commands from disk and re-executing them with the execute operation
|
* support logging changes so that they can be reapplied in case of a system crash. By augmenting the Command interface with load and store operations, you can keep a persistent log of changes. Recovering from a crash involves reloading logged commands from disk and re-executing them with the execute operation
|
||||||
* structure a system around high-level operations build on primitive operations. Such a structure is common in information systems that support transactions. A transaction encapsulates a set of changes to data. The Command pattern offers a way to model transactions. Commands have a common interface, letting you invoke all transactions the same way. The pattern also makes it easy to extend the system with new transactions
|
* structure a system around high-level operations build on primitive operations. Such a structure is common in information systems that support transactions. A transaction encapsulates a set of changes to data. The Command pattern offers a way to model transactions. Commands have a common interface, letting you invoke all transactions the same way. The pattern also makes it easy to extend the system with new transactions
|
||||||
|
|
||||||
**Typical Use Case:**
|
## Typical Use Case
|
||||||
|
|
||||||
* to keep a history of requests
|
* to keep a history of requests
|
||||||
* implement callback functionality
|
* implement callback functionality
|
||||||
* implement the undo functionality
|
* implement the undo functionality
|
||||||
|
|
||||||
**Real world examples:**
|
## Real world examples
|
||||||
|
|
||||||
* [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)
|
* [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)
|
||||||
|
|
||||||
**Credits**
|
## Credits
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>command</artifactId>
|
<artifactId>command</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -30,6 +30,9 @@ public abstract class Target {
|
|||||||
@Override
|
@Override
|
||||||
public abstract String toString();
|
public abstract String toString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print status
|
||||||
|
*/
|
||||||
public void printStatus() {
|
public void printStatus() {
|
||||||
System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, getSize(),
|
System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, getSize(),
|
||||||
getVisibility()));
|
getVisibility()));
|
||||||
|
@ -15,12 +15,18 @@ public class Wizard {
|
|||||||
|
|
||||||
public Wizard() {}
|
public Wizard() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast spell
|
||||||
|
*/
|
||||||
public void castSpell(Command command, Target target) {
|
public void castSpell(Command command, Target target) {
|
||||||
System.out.println(this + " casts " + command + " at " + target);
|
System.out.println(this + " casts " + command + " at " + target);
|
||||||
command.execute(target);
|
command.execute(target);
|
||||||
undoStack.offerLast(command);
|
undoStack.offerLast(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo last spell
|
||||||
|
*/
|
||||||
public void undoLastSpell() {
|
public void undoLastSpell() {
|
||||||
if (!undoStack.isEmpty()) {
|
if (!undoStack.isEmpty()) {
|
||||||
Command previousSpell = undoStack.pollLast();
|
Command previousSpell = undoStack.pollLast();
|
||||||
@ -30,6 +36,9 @@ public class Wizard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redo last spell
|
||||||
|
*/
|
||||||
public void redoLastSpell() {
|
public void redoLastSpell() {
|
||||||
if (!redoStack.isEmpty()) {
|
if (!redoStack.isEmpty()) {
|
||||||
Command previousSpell = redoStack.pollLast();
|
Command previousSpell = redoStack.pollLast();
|
||||||
|
@ -7,24 +7,27 @@ categories: Structural
|
|||||||
tags:
|
tags:
|
||||||
- Java
|
- Java
|
||||||
- Gang Of Four
|
- Gang Of Four
|
||||||
|
- Difficulty-Intermediate
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Compose objects into tree structures to represent part-whole
|
## Intent
|
||||||
|
Compose objects into tree structures to represent part-whole
|
||||||
hierarchies. Composite lets clients treat individual objects and compositions
|
hierarchies. Composite lets clients treat individual objects and compositions
|
||||||
of objects uniformly.
|
of objects uniformly.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Composite pattern when
|
## Applicability
|
||||||
|
Use the Composite pattern when
|
||||||
|
|
||||||
* you want to represent part-whole hierarchies of objects
|
* you want to represent part-whole hierarchies of objects
|
||||||
* you want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly
|
* you want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly
|
||||||
|
|
||||||
**Real world examples:**
|
## Real world examples
|
||||||
|
|
||||||
* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html)
|
* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html)
|
||||||
* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java)
|
* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java)
|
||||||
|
|
||||||
**Credits**
|
## Credits
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>composite</artifactId>
|
<artifactId>composite</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -24,6 +24,9 @@ public abstract class LetterComposite {
|
|||||||
|
|
||||||
protected abstract void printThisAfter();
|
protected abstract void printThisAfter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print
|
||||||
|
*/
|
||||||
public void print() {
|
public void print() {
|
||||||
printThisBefore();
|
printThisBefore();
|
||||||
for (LetterComposite letter : children) {
|
for (LetterComposite letter : children) {
|
||||||
|
@ -9,6 +9,9 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class Sentence extends LetterComposite {
|
public class Sentence extends LetterComposite {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public Sentence(List<Word> words) {
|
public Sentence(List<Word> words) {
|
||||||
for (Word w : words) {
|
for (Word w : words) {
|
||||||
this.add(w);
|
this.add(w);
|
||||||
|
@ -9,6 +9,9 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class Word extends LetterComposite {
|
public class Word extends LetterComposite {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public Word(List<Letter> letters) {
|
public Word(List<Letter> letters) {
|
||||||
for (Letter l : letters) {
|
for (Letter l : letters) {
|
||||||
this.add(l);
|
this.add(l);
|
||||||
|
@ -2,8 +2,6 @@ package com.iluwatar.composite;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.iluwatar.composite.App;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Application test
|
* Application test
|
||||||
|
10
dao/index.md
10
dao/index.md
@ -3,22 +3,24 @@ layout: pattern
|
|||||||
title: Data Access Object
|
title: Data Access Object
|
||||||
folder: dao
|
folder: dao
|
||||||
permalink: /patterns/dao/
|
permalink: /patterns/dao/
|
||||||
categories: Architectural
|
categories: Persistence Tier
|
||||||
tags:
|
tags:
|
||||||
- Java
|
- Java
|
||||||
- Difficulty-Beginner
|
- Difficulty-Beginner
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Object provides an abstract interface to some type of database or
|
## Intent
|
||||||
|
Object provides an abstract interface to some type of database or
|
||||||
other persistence mechanism.
|
other persistence mechanism.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Data Access Object in any of the following situations
|
## Applicability
|
||||||
|
Use the Data Access Object in any of the following situations
|
||||||
|
|
||||||
* when you want to consolidate how the data layer is accessed
|
* when you want to consolidate how the data layer is accessed
|
||||||
* when you want to avoid writing multiple data retrieval/persistence layers
|
* when you want to avoid writing multiple data retrieval/persistence layers
|
||||||
|
|
||||||
**Credits:**
|
## Credits
|
||||||
|
|
||||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>dao</artifactId>
|
<artifactId>dao</artifactId>
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import org.apache.log4j.Logger;
|
|||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
private static Logger LOGGER = Logger.getLogger(App.class);
|
private static Logger log = Logger.getLogger(App.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point.
|
* Program entry point.
|
||||||
@ -30,17 +30,17 @@ public class App {
|
|||||||
*/
|
*/
|
||||||
public static void main(final String[] args) {
|
public static void main(final String[] args) {
|
||||||
final CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers());
|
final CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers());
|
||||||
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
||||||
LOGGER.info("customerDao.getCusterById(2): " + customerDao.getCustomerById(2));
|
log.info("customerDao.getCusterById(2): " + customerDao.getCustomerById(2));
|
||||||
final Customer customer = new Customer(4, "Dan", "Danson");
|
final Customer customer = new Customer(4, "Dan", "Danson");
|
||||||
customerDao.addCustomer(customer);
|
customerDao.addCustomer(customer);
|
||||||
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
||||||
customer.setFirstName("Daniel");
|
customer.setFirstName("Daniel");
|
||||||
customer.setLastName("Danielson");
|
customer.setLastName("Danielson");
|
||||||
customerDao.updateCustomer(customer);
|
customerDao.updateCustomer(customer);
|
||||||
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
||||||
customerDao.deleteCustomer(customer);
|
customerDao.deleteCustomer(customer);
|
||||||
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,6 +11,9 @@ public class Customer {
|
|||||||
private String firstName;
|
private String firstName;
|
||||||
private String lastName;
|
private String lastName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public Customer(final int id, final String firstName, final String lastName) {
|
public Customer(final int id, final String firstName, final String lastName) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.firstName = firstName;
|
this.firstName = firstName;
|
||||||
@ -52,11 +55,12 @@ public class Customer {
|
|||||||
boolean isEqual = false;
|
boolean isEqual = false;
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
isEqual = true;
|
isEqual = true;
|
||||||
} else if (o != null && (getClass() == o.getClass())) {
|
} else if (o != null && getClass() == o.getClass()) {
|
||||||
final Customer customer = (Customer) o;
|
final Customer customer = (Customer) o;
|
||||||
if (getId() == customer.getId())
|
if (getId() == customer.getId()) {
|
||||||
isEqual = true;
|
isEqual = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return isEqual;
|
return isEqual;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 29 KiB |
@ -1,18 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
|
<class-diagram version="1.1.9" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
|
||||||
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
|
associations="true" dependencies="false" nesting-relationships="true" router="FAN">
|
||||||
<interface id="1" language="java" name="com.iluwatar.decorator.Hostile" project="decorator"
|
<interface id="1" language="java" name="com.iluwatar.decorator.Hostile" project="decorator"
|
||||||
file="/decorator/src/main/java/com/iluwatar/decorator/Hostile.java" binary="false" corner="BOTTOM_RIGHT">
|
file="/decorator/src/main/java/com/iluwatar/decorator/Hostile.java" binary="false" corner="BOTTOM_RIGHT">
|
||||||
<position height="106" width="134" x="116" y="408"/>
|
<position height="-1" width="-1" x="554" y="188"/>
|
||||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
sort-features="false" accessors="true" visibility="true">
|
sort-features="false" accessors="true" visibility="true">
|
||||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
</display>
|
</display>
|
||||||
</interface>
|
</interface>
|
||||||
<class id="2" language="java" name="com.iluwatar.decorator.SmartTroll" project="decorator"
|
<class id="2" language="java" name="com.iluwatar.decorator.App" project="decorator"
|
||||||
file="/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java" binary="false" corner="BOTTOM_RIGHT">
|
file="/decorator/src/main/java/com/iluwatar/decorator/App.java" binary="false" corner="BOTTOM_RIGHT">
|
||||||
<position height="124" width="134" x="116" y="244"/>
|
<position height="-1" width="-1" x="117" y="202"/>
|
||||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
sort-features="false" accessors="true" visibility="true">
|
sort-features="false" accessors="true" visibility="true">
|
||||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
@ -21,29 +21,46 @@
|
|||||||
</class>
|
</class>
|
||||||
<class id="3" language="java" name="com.iluwatar.decorator.Troll" project="decorator"
|
<class id="3" language="java" name="com.iluwatar.decorator.Troll" project="decorator"
|
||||||
file="/decorator/src/main/java/com/iluwatar/decorator/Troll.java" binary="false" corner="BOTTOM_RIGHT">
|
file="/decorator/src/main/java/com/iluwatar/decorator/Troll.java" binary="false" corner="BOTTOM_RIGHT">
|
||||||
<position height="124" width="134" x="290" y="244"/>
|
<position height="-1" width="-1" x="315" y="100"/>
|
||||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
sort-features="false" accessors="true" visibility="true">
|
sort-features="false" accessors="true" visibility="true">
|
||||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
</display>
|
</display>
|
||||||
</class>
|
</class>
|
||||||
<realization id="4">
|
<class id="4" language="java" name="com.iluwatar.decorator.SmartHostile" project="decorator"
|
||||||
|
file="/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java" binary="false" corner="BOTTOM_RIGHT">
|
||||||
|
<position height="-1" width="-1" x="318" y="315"/>
|
||||||
|
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
|
sort-features="false" accessors="true" visibility="true">
|
||||||
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
</display>
|
||||||
|
</class>
|
||||||
|
<dependency id="5">
|
||||||
<end type="SOURCE" refId="2"/>
|
<end type="SOURCE" refId="2"/>
|
||||||
|
<end type="TARGET" refId="4"/>
|
||||||
|
</dependency>
|
||||||
|
<realization id="6">
|
||||||
|
<end type="SOURCE" refId="3"/>
|
||||||
<end type="TARGET" refId="1"/>
|
<end type="TARGET" refId="1"/>
|
||||||
</realization>
|
</realization>
|
||||||
<association id="5">
|
<association id="7">
|
||||||
<end type="SOURCE" refId="2" navigable="false">
|
<end type="SOURCE" refId="4" navigable="false">
|
||||||
<attribute id="6" name="decorated"/>
|
<attribute id="8" name="decorated"/>
|
||||||
<multiplicity id="7" minimum="0" maximum="1"/>
|
<multiplicity id="9" minimum="0" maximum="1"/>
|
||||||
</end>
|
</end>
|
||||||
<end type="TARGET" refId="1" navigable="true"/>
|
<end type="TARGET" refId="1" navigable="true"/>
|
||||||
<display labels="true" multiplicity="true"/>
|
<display labels="true" multiplicity="true"/>
|
||||||
</association>
|
</association>
|
||||||
<realization id="8">
|
<realization id="10">
|
||||||
<end type="SOURCE" refId="3"/>
|
<end type="SOURCE" refId="4"/>
|
||||||
<end type="TARGET" refId="1"/>
|
<end type="TARGET" refId="1"/>
|
||||||
</realization>
|
</realization>
|
||||||
|
<dependency id="11">
|
||||||
|
<end type="SOURCE" refId="2"/>
|
||||||
|
<end type="TARGET" refId="3"/>
|
||||||
|
</dependency>
|
||||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
sort-features="false" accessors="true" visibility="true">
|
sort-features="false" accessors="true" visibility="true">
|
||||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
@ -7,22 +7,26 @@ categories: Structural
|
|||||||
tags:
|
tags:
|
||||||
- Java
|
- Java
|
||||||
- Gang Of Four
|
- Gang Of Four
|
||||||
|
- Difficulty-Beginner
|
||||||
---
|
---
|
||||||
|
|
||||||
**Also known as:** Wrapper
|
## Also known as
|
||||||
|
Wrapper
|
||||||
|
|
||||||
**Intent:** Attach additional responsibilities to an object dynamically.
|
## Intent
|
||||||
|
Attach additional responsibilities to an object dynamically.
|
||||||
Decorators provide a flexible alternative to subclassing for extending
|
Decorators provide a flexible alternative to subclassing for extending
|
||||||
functionality.
|
functionality.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use Decorator
|
## Applicability
|
||||||
|
Use Decorator
|
||||||
|
|
||||||
* to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects
|
* to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects
|
||||||
* for responsibilities that can be withdrawn
|
* for responsibilities that can be withdrawn
|
||||||
* when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing
|
* when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing
|
||||||
|
|
||||||
**Credits**
|
## Credits
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>decorator</artifactId>
|
<artifactId>decorator</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -8,7 +8,7 @@ package com.iluwatar.decorator;
|
|||||||
* runtime.
|
* runtime.
|
||||||
* <p>
|
* <p>
|
||||||
* In this example we show how the simple {@link Troll} first attacks and then flees the battle.
|
* In this example we show how the simple {@link Troll} first attacks and then flees the battle.
|
||||||
* Then we decorate the {@link Troll} with a {@link SmartTroll} and perform the attack again. You
|
* Then we decorate the {@link Troll} with a {@link SmartHostile} and perform the attack again. You
|
||||||
* can see how the behavior changes after the decoration.
|
* can see how the behavior changes after the decoration.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -30,7 +30,7 @@ public class App {
|
|||||||
|
|
||||||
// change the behavior of the simple troll by adding a decorator
|
// change the behavior of the simple troll by adding a decorator
|
||||||
System.out.println("\nA smart looking troll surprises you.");
|
System.out.println("\nA smart looking troll surprises you.");
|
||||||
Hostile smart = new SmartTroll(troll);
|
Hostile smart = new SmartHostile(troll);
|
||||||
smart.attack();
|
smart.attack();
|
||||||
smart.fleeBattle();
|
smart.fleeBattle();
|
||||||
System.out.printf("Smart troll power %d.\n", smart.getAttackPower());
|
System.out.printf("Smart troll power %d.\n", smart.getAttackPower());
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
package com.iluwatar.decorator;
|
package com.iluwatar.decorator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SmartTroll is a decorator for {@link Hostile} objects. The calls to the {@link Hostile} interface
|
* SmartHostile is a decorator for {@link Hostile} objects. The calls to the {@link Hostile} interface
|
||||||
* are intercepted and decorated. Finally the calls are delegated to the decorated {@link Hostile}
|
* are intercepted and decorated. Finally the calls are delegated to the decorated {@link Hostile}
|
||||||
* object.
|
* object.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SmartTroll implements Hostile {
|
public class SmartHostile implements Hostile {
|
||||||
|
|
||||||
private Hostile decorated;
|
private Hostile decorated;
|
||||||
|
|
||||||
public SmartTroll(Hostile decorated) {
|
public SmartHostile(Hostile decorated) {
|
||||||
this.decorated = decorated;
|
this.decorated = decorated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void attack() {
|
public void attack() {
|
||||||
System.out.println("The troll throws a rock at you!");
|
System.out.println("It throws a rock at you!");
|
||||||
decorated.attack();
|
decorated.attack();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAttackPower() {
|
public int getAttackPower() {
|
||||||
// decorated troll power + 20 because it is smart
|
// decorated hostile's power + 20 because it is smart
|
||||||
return decorated.getAttackPower() + 20;
|
return decorated.getAttackPower() + 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fleeBattle() {
|
public void fleeBattle() {
|
||||||
System.out.println("The troll calls for help!");
|
System.out.println("It calls for help!");
|
||||||
decorated.fleeBattle();
|
decorated.fleeBattle();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,8 +2,6 @@ package com.iluwatar.decorator;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.iluwatar.decorator.App;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Application test
|
* Application test
|
||||||
|
@ -3,9 +3,7 @@ package com.iluwatar.decorator;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
import static org.mockito.internal.verification.VerificationModeFactory.times;
|
import static org.mockito.internal.verification.VerificationModeFactory.times;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,15 +11,15 @@ import static org.mockito.internal.verification.VerificationModeFactory.times;
|
|||||||
*
|
*
|
||||||
* @author Jeroen Meulemeester
|
* @author Jeroen Meulemeester
|
||||||
*/
|
*/
|
||||||
public class SmartTrollTest {
|
public class SmartHostileTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSmartTroll() throws Exception {
|
public void testSmartHostile() throws Exception {
|
||||||
// Create a normal troll first, but make sure we can spy on it later on.
|
// Create a normal troll first, but make sure we can spy on it later on.
|
||||||
final Hostile simpleTroll = spy(new Troll());
|
final Hostile simpleTroll = spy(new Troll());
|
||||||
|
|
||||||
// Now we want to decorate the troll to make it smarter ...
|
// Now we want to decorate the troll to make it smarter ...
|
||||||
final Hostile smartTroll = new SmartTroll(simpleTroll);
|
final Hostile smartTroll = new SmartHostile(simpleTroll);
|
||||||
assertEquals(30, smartTroll.getAttackPower());
|
assertEquals(30, smartTroll.getAttackPower());
|
||||||
verify(simpleTroll, times(1)).getAttackPower();
|
verify(simpleTroll, times(1)).getAttackPower();
|
||||||
|
|
@ -4,15 +4,11 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
import static org.mockito.internal.verification.VerificationModeFactory.times;
|
import static org.mockito.internal.verification.VerificationModeFactory.times;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
BIN
delegation/etc/delegation.png
Normal file
BIN
delegation/etc/delegation.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
83
delegation/etc/delegation.ucls
Normal file
83
delegation/etc/delegation.ucls
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<class-diagram version="1.1.9" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
|
||||||
|
realizations="true" associations="true" dependencies="false" nesting-relationships="true" router="FAN">
|
||||||
|
<class id="1" language="java" name="com.iluwatar.delegation.simple.printers.HpPrinter" project="delegation"
|
||||||
|
file="/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java" binary="false"
|
||||||
|
corner="BOTTOM_RIGHT">
|
||||||
|
<position height="-1" width="-1" x="764" y="272"/>
|
||||||
|
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
|
sort-features="false" accessors="true" visibility="true">
|
||||||
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
</display>
|
||||||
|
</class>
|
||||||
|
<class id="2" language="java" name="com.iluwatar.delegation.simple.printers.EpsonPrinter" project="delegation"
|
||||||
|
file="/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java" binary="false"
|
||||||
|
corner="BOTTOM_RIGHT">
|
||||||
|
<position height="-1" width="-1" x="511" y="270"/>
|
||||||
|
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
|
sort-features="false" accessors="true" visibility="true">
|
||||||
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
</display>
|
||||||
|
</class>
|
||||||
|
<class id="3" language="java" name="com.iluwatar.delegation.simple.printers.CanonPrinter" project="delegation"
|
||||||
|
file="/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java" binary="false"
|
||||||
|
corner="BOTTOM_RIGHT">
|
||||||
|
<position height="-1" width="-1" x="252" y="275"/>
|
||||||
|
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
|
sort-features="false" accessors="true" visibility="true">
|
||||||
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
</display>
|
||||||
|
</class>
|
||||||
|
<interface id="4" language="java" name="com.iluwatar.delegation.simple.Printer" project="delegation"
|
||||||
|
file="/delegation/src/main/java/com/iluwatar/delegation/simple/Printer.java" binary="false" corner="BOTTOM_RIGHT">
|
||||||
|
<position height="-1" width="-1" x="524" y="89"/>
|
||||||
|
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
|
sort-features="false" accessors="true" visibility="true">
|
||||||
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
</display>
|
||||||
|
</interface>
|
||||||
|
<class id="5" language="java" name="com.iluwatar.delegation.simple.PrinterController" project="delegation"
|
||||||
|
file="/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java" binary="false"
|
||||||
|
corner="BOTTOM_RIGHT">
|
||||||
|
<position height="-1" width="-1" x="112" y="90"/>
|
||||||
|
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
|
sort-features="false" accessors="true" visibility="true">
|
||||||
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
</display>
|
||||||
|
</class>
|
||||||
|
<realization id="6">
|
||||||
|
<end type="SOURCE" refId="2"/>
|
||||||
|
<end type="TARGET" refId="4"/>
|
||||||
|
</realization>
|
||||||
|
<association id="7">
|
||||||
|
<end type="SOURCE" refId="5" navigable="false">
|
||||||
|
<attribute id="8" name="printer"/>
|
||||||
|
<multiplicity id="9" minimum="0" maximum="1"/>
|
||||||
|
</end>
|
||||||
|
<end type="TARGET" refId="4" navigable="true"/>
|
||||||
|
<display labels="true" multiplicity="true"/>
|
||||||
|
</association>
|
||||||
|
<realization id="10">
|
||||||
|
<end type="SOURCE" refId="5"/>
|
||||||
|
<end type="TARGET" refId="4"/>
|
||||||
|
</realization>
|
||||||
|
<realization id="11">
|
||||||
|
<end type="SOURCE" refId="3"/>
|
||||||
|
<end type="TARGET" refId="4"/>
|
||||||
|
</realization>
|
||||||
|
<realization id="12">
|
||||||
|
<end type="SOURCE" refId="1"/>
|
||||||
|
<end type="TARGET" refId="4"/>
|
||||||
|
</realization>
|
||||||
|
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||||
|
sort-features="false" accessors="true" visibility="true">
|
||||||
|
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||||
|
</classifier-display>
|
||||||
|
<association-display labels="true" multiplicity="true"/>
|
||||||
|
</class-diagram>
|
30
delegation/index.md
Normal file
30
delegation/index.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
layout: pattern
|
||||||
|
title: Delegation
|
||||||
|
folder: delegation
|
||||||
|
permalink: /patterns/delegation/
|
||||||
|
categories: Behavioral
|
||||||
|
tags:
|
||||||
|
- Java
|
||||||
|
- Difficulty-Beginner
|
||||||
|
---
|
||||||
|
|
||||||
|
## Also known as
|
||||||
|
Proxy Pattern
|
||||||
|
|
||||||
|
## Intent
|
||||||
|
It is a technique where an object expresses certain behavior to the outside but in
|
||||||
|
reality delegates responsibility for implementing that behaviour to an associated object.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Applicability
|
||||||
|
Use the Delegate pattern in order to achieve the following
|
||||||
|
|
||||||
|
* Reduce the coupling of methods to their class
|
||||||
|
* Components that behave identically, but realize that this situation can change in the future.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* [Delegate Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Delegation_pattern)
|
||||||
|
* [Proxy Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Proxy_pattern)
|
27
delegation/pom.xml
Normal file
27
delegation/pom.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>java-design-patterns</artifactId>
|
||||||
|
<groupId>com.iluwatar</groupId>
|
||||||
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>delegation</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.stefanbirkner</groupId>
|
||||||
|
<artifactId>system-rules</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.iluwatar.delegation.simple;
|
||||||
|
|
||||||
|
import com.iluwatar.delegation.simple.printers.CanonPrinter;
|
||||||
|
import com.iluwatar.delegation.simple.printers.EpsonPrinter;
|
||||||
|
import com.iluwatar.delegation.simple.printers.HpPrinter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The delegate pattern provides a mechanism to abstract away the implementation and control of the desired action.
|
||||||
|
* The class being called in this case {@link PrinterController} is not responsible for the actual desired action,
|
||||||
|
* but is actually delegated to a helper class either {@link CanonPrinter}, {@link EpsonPrinter} or {@link HpPrinter}.
|
||||||
|
* The consumer does not have or require knowledge of the actual class carrying out the action, only the
|
||||||
|
* container on which they are calling.
|
||||||
|
*
|
||||||
|
* In this example the delegates are {@link EpsonPrinter}, {@link HpPrinter} and {@link CanonPrinter} they all implement
|
||||||
|
* {@link Printer}. The {@link PrinterController} class also implements {@link Printer}. However neither provide the
|
||||||
|
* functionality of {@link Printer} by printing to the screen, they actually call upon the instance of {@link Printer}
|
||||||
|
* that they were instantiated with. Therefore delegating the behaviour to another class.
|
||||||
|
*/
|
||||||
|
public class App {
|
||||||
|
|
||||||
|
public static final String MESSAGE_TO_PRINT = "hello world";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Program entry point
|
||||||
|
*
|
||||||
|
* @param args command line args
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
PrinterController hpPrinterController = new PrinterController(new HpPrinter());
|
||||||
|
PrinterController canonPrinterController = new PrinterController(new CanonPrinter());
|
||||||
|
PrinterController epsonPrinterController = new PrinterController(new EpsonPrinter());
|
||||||
|
|
||||||
|
hpPrinterController.print(MESSAGE_TO_PRINT);
|
||||||
|
canonPrinterController.print(MESSAGE_TO_PRINT);
|
||||||
|
epsonPrinterController.print(MESSAGE_TO_PRINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.iluwatar.delegation.simple;
|
||||||
|
|
||||||
|
import com.iluwatar.delegation.simple.printers.CanonPrinter;
|
||||||
|
import com.iluwatar.delegation.simple.printers.EpsonPrinter;
|
||||||
|
import com.iluwatar.delegation.simple.printers.HpPrinter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that both the Controller and the Delegate will implement.
|
||||||
|
*
|
||||||
|
* @see CanonPrinter
|
||||||
|
* @see EpsonPrinter
|
||||||
|
* @see HpPrinter
|
||||||
|
*/
|
||||||
|
public interface Printer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that takes a String to print to the screen. This will be implemented on both the
|
||||||
|
* controller and the delegate allowing the controller to call the same method on the delegate class.
|
||||||
|
*
|
||||||
|
* @param message to be printed to the screen
|
||||||
|
*/
|
||||||
|
void print(final String message);
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.iluwatar.delegation.simple;
|
||||||
|
|
||||||
|
public class PrinterController implements Printer {
|
||||||
|
|
||||||
|
private final Printer printer;
|
||||||
|
|
||||||
|
public PrinterController(Printer printer) {
|
||||||
|
this.printer = printer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is implemented from {@link Printer} however instead on providing an
|
||||||
|
* implementation, it instead calls upon the class passed through the constructor. This is the delegate,
|
||||||
|
* hence the pattern. Therefore meaning that the caller does not care of the implementing class only the owning
|
||||||
|
* controller.
|
||||||
|
*
|
||||||
|
* @param message to be printed to the screen
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void print(String message) {
|
||||||
|
printer.print(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.iluwatar.delegation.simple.printers;
|
||||||
|
|
||||||
|
import com.iluwatar.delegation.simple.Printer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialised Implementation of {@link Printer} for a Canon Printer, in
|
||||||
|
* this case the message to be printed is appended to "Canon Printer : "
|
||||||
|
*
|
||||||
|
* @see Printer
|
||||||
|
*/
|
||||||
|
public class CanonPrinter implements Printer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void print(String message) {
|
||||||
|
System.out.print("Canon Printer : " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.iluwatar.delegation.simple.printers;
|
||||||
|
|
||||||
|
import com.iluwatar.delegation.simple.Printer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialised Implementation of {@link Printer} for a Epson Printer, in
|
||||||
|
* this case the message to be printed is appended to "Epson Printer : "
|
||||||
|
*
|
||||||
|
* @see Printer
|
||||||
|
*/
|
||||||
|
public class EpsonPrinter implements Printer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void print(String message) {
|
||||||
|
System.out.print("Epson Printer : " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.iluwatar.delegation.simple.printers;
|
||||||
|
|
||||||
|
import com.iluwatar.delegation.simple.Printer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialised Implementation of {@link Printer} for a HP Printer, in
|
||||||
|
* this case the message to be printed is appended to "HP Printer : "
|
||||||
|
*
|
||||||
|
* @see Printer
|
||||||
|
*/
|
||||||
|
public class HpPrinter implements Printer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void print(String message) {
|
||||||
|
System.out.print("HP Printer : " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.iluwatar.delegation.simple;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class AppTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
String[] args = {};
|
||||||
|
App.main(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.iluwatar.delegation.simple;
|
||||||
|
|
||||||
|
import com.iluwatar.delegation.simple.printers.CanonPrinter;
|
||||||
|
import com.iluwatar.delegation.simple.printers.EpsonPrinter;
|
||||||
|
import com.iluwatar.delegation.simple.printers.HpPrinter;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.contrib.java.lang.system.SystemOutRule;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class DelegateTest {
|
||||||
|
|
||||||
|
private static final String MESSAGE = "Test Message Printed";
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCanonPrinter() throws Exception {
|
||||||
|
PrinterController printerController = new PrinterController(new CanonPrinter());
|
||||||
|
printerController.print(MESSAGE);
|
||||||
|
|
||||||
|
assertEquals("Canon Printer : Test Message Printed", systemOutRule.getLog());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHpPrinter() throws Exception {
|
||||||
|
PrinterController printerController = new PrinterController(new HpPrinter());
|
||||||
|
printerController.print(MESSAGE);
|
||||||
|
|
||||||
|
assertEquals("HP Printer : Test Message Printed", systemOutRule.getLog());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEpsonPrinter() throws Exception {
|
||||||
|
PrinterController printerController = new PrinterController(new EpsonPrinter());
|
||||||
|
printerController.print(MESSAGE);
|
||||||
|
|
||||||
|
assertEquals("Epson Printer : Test Message Printed", systemOutRule.getLog());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,10 +4,13 @@ title: Dependency Injection
|
|||||||
folder: dependency-injection
|
folder: dependency-injection
|
||||||
permalink: /patterns/dependency-injection/
|
permalink: /patterns/dependency-injection/
|
||||||
categories: Behavioral
|
categories: Behavioral
|
||||||
tags: Java
|
tags:
|
||||||
|
- Java
|
||||||
|
- Difficulty-Beginner
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Dependency Injection is a software design pattern in which one or
|
## Intent
|
||||||
|
Dependency Injection is a software design pattern in which one or
|
||||||
more dependencies (or services) are injected, or passed by reference, into a
|
more dependencies (or services) are injected, or passed by reference, into a
|
||||||
dependent object (or client) and are made part of the client's state. The
|
dependent object (or client) and are made part of the client's state. The
|
||||||
pattern separates the creation of a client's dependencies from its own
|
pattern separates the creation of a client's dependencies from its own
|
||||||
@ -16,7 +19,8 @@ inversion of control and single responsibility principles.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Dependency Injection pattern when
|
## Applicability
|
||||||
|
Use the Dependency Injection pattern when
|
||||||
|
|
||||||
* when you need to remove knowledge of concrete implementation from object
|
* when you need to remove knowledge of concrete implementation from object
|
||||||
* to enable unit testing of classes in isolation using mock objects or stubs
|
* to enable unit testing of classes in isolation using mock objects or stubs
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>dependency-injection</artifactId>
|
<artifactId>dependency-injection</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -2,8 +2,6 @@ package com.iluwatar.dependency.injection;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.iluwatar.dependency.injection.App;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Application test
|
* Application test
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
package com.iluwatar.dependency.injection;
|
package com.iluwatar.dependency.injection;
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.PrintStream;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date: 12/10/15 - 8:26 PM
|
* Date: 12/10/15 - 8:26 PM
|
||||||
|
@ -4,17 +4,22 @@ title: Double Checked Locking
|
|||||||
folder: double-checked-locking
|
folder: double-checked-locking
|
||||||
permalink: /patterns/double-checked-locking/
|
permalink: /patterns/double-checked-locking/
|
||||||
categories: Concurrency
|
categories: Concurrency
|
||||||
tags: Java
|
tags:
|
||||||
|
- Java
|
||||||
|
- Difficulty-Beginner
|
||||||
|
- Idiom
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Reduce the overhead of acquiring a lock by first testing the
|
## Intent
|
||||||
|
Reduce the overhead of acquiring a lock by first testing the
|
||||||
locking criterion (the "lock hint") without actually acquiring the lock. Only
|
locking criterion (the "lock hint") without actually acquiring the lock. Only
|
||||||
if the locking criterion check indicates that locking is required does the
|
if the locking criterion check indicates that locking is required does the
|
||||||
actual locking logic proceed.
|
actual locking logic proceed.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Double Checked Locking pattern when
|
## Applicability
|
||||||
|
Use the Double Checked Locking pattern when
|
||||||
|
|
||||||
* there is a concurrent access in object creation, e.g. singleton, where you want to create single instance of the same class and checking if it's null or not maybe not be enough when there are two or more threads that checks if instance is null or not.
|
* there is a concurrent access in object creation, e.g. singleton, where you want to create single instance of the same class and checking if it's null or not maybe not be enough when there are two or more threads that checks if instance is null or not.
|
||||||
* there is a concurrent access on a method where method's behaviour changes according to the some constraints and these constraint change within this method.
|
* there is a concurrent access on a method where method's behaviour changes according to the some constraints and these constraint change within this method.
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>double-checked-locking</artifactId>
|
<artifactId>double-checked-locking</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -28,7 +28,7 @@ public class App {
|
|||||||
ExecutorService executorService = Executors.newFixedThreadPool(3);
|
ExecutorService executorService = Executors.newFixedThreadPool(3);
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
executorService.execute(() -> {
|
executorService.execute(() -> {
|
||||||
while (inventory.addItem(new Item()));
|
while (inventory.addItem(new Item())) {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,12 +17,18 @@ public class Inventory {
|
|||||||
private final List<Item> items;
|
private final List<Item> items;
|
||||||
private final Lock lock;
|
private final Lock lock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public Inventory(int inventorySize) {
|
public Inventory(int inventorySize) {
|
||||||
this.inventorySize = inventorySize;
|
this.inventorySize = inventorySize;
|
||||||
this.items = new ArrayList<>(inventorySize);
|
this.items = new ArrayList<>(inventorySize);
|
||||||
this.lock = new ReentrantLock();
|
this.lock = new ReentrantLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add item
|
||||||
|
*/
|
||||||
public boolean addItem(Item item) {
|
public boolean addItem(Item item) {
|
||||||
if (items.size() < inventorySize) {
|
if (items.size() < inventorySize) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
|
@ -6,7 +6,4 @@ package com.iluwatar.doublechecked.locking;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Item {
|
public class Item {
|
||||||
|
|
||||||
private String name;
|
|
||||||
private int level;
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package com.iluwatar.doublechecked.locking;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.iluwatar.doublechecked.locking.App;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Application test
|
* Application test
|
||||||
|
@ -77,7 +77,7 @@ public class InventoryTest {
|
|||||||
final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
|
final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
|
||||||
for (int i = 0; i < THREAD_COUNT; i++) {
|
for (int i = 0; i < THREAD_COUNT; i++) {
|
||||||
executorService.execute(() -> {
|
executorService.execute(() -> {
|
||||||
while (inventory.addItem(new Item())) ;
|
while (inventory.addItem(new Item())) {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,18 +4,23 @@ title: Double Dispatch
|
|||||||
folder: double-dispatch
|
folder: double-dispatch
|
||||||
permalink: /patterns/double-dispatch/
|
permalink: /patterns/double-dispatch/
|
||||||
categories: Other
|
categories: Other
|
||||||
tags: Java
|
tags:
|
||||||
|
- Java
|
||||||
|
- Difficulty-Intermediate
|
||||||
|
- Idiom
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Double Dispatch pattern is a way to create maintainable dynamic
|
## Intent
|
||||||
|
Double Dispatch pattern is a way to create maintainable dynamic
|
||||||
behavior based on receiver and parameter types.
|
behavior based on receiver and parameter types.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Double Dispatch pattern when
|
## Applicability
|
||||||
|
Use the Double Dispatch pattern when
|
||||||
|
|
||||||
* the dynamic behavior is not defined only based on receiving object's type but also on the receiving method's parameter type.
|
* the dynamic behavior is not defined only based on receiving object's type but also on the receiving method's parameter type.
|
||||||
|
|
||||||
**Real world examples:**
|
## Real world examples
|
||||||
|
|
||||||
* [ObjectOutputStream](https://docs.oracle.com/javase/8/docs/api/java/io/ObjectOutputStream.html)
|
* [ObjectOutputStream](https://docs.oracle.com/javase/8/docs/api/java/io/ObjectOutputStream.html)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>double-dispatch</artifactId>
|
<artifactId>double-dispatch</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -5,22 +5,20 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* When a message with a parameter is sent to an object, the resultant behaviour is defined by the
|
* When a message with a parameter is sent to an object, the resultant behaviour is defined by the implementation of
|
||||||
* implementation of that method in the receiver. Sometimes the behaviour must also be determined by
|
* that method in the receiver. Sometimes the behaviour must also be determined by the type of the parameter.
|
||||||
* the type of the parameter.
|
|
||||||
* <p>
|
* <p>
|
||||||
* One way to implement this would be to create multiple instanceof-checks for the methods
|
* One way to implement this would be to create multiple instanceof-checks for the methods parameter. However, this
|
||||||
* parameter. However, this creates a maintenance issue. When new types are added we would also need
|
* creates a maintenance issue. When new types are added we would also need to change the method's implementation and
|
||||||
* to change the method's implementation and add a new instanceof-check. This violates the single
|
* add a new instanceof-check. This violates the single responsibility principle - a class should have only one reason
|
||||||
* responsibility principle - a class should have only one reason to change.
|
* to change.
|
||||||
* <p>
|
* <p>
|
||||||
* Instead of the instanceof-checks a better way is to make another virtual call on the parameter
|
* Instead of the instanceof-checks a better way is to make another virtual call on the parameter object. This way new
|
||||||
* object. This way new functionality can be easily added without the need to modify existing
|
* functionality can be easily added without the need to modify existing implementation (open-closed principle).
|
||||||
* implementation (open-closed principle).
|
|
||||||
* <p>
|
* <p>
|
||||||
* In this example we have hierarchy of objects ({@link GameObject}) that can collide to each other.
|
* In this example we have hierarchy of objects ({@link GameObject}) that can collide to each other. Each object has its
|
||||||
* Each object has its own coordinates which are checked against the other objects' coordinates. If
|
* own coordinates which are checked against the other objects' coordinates. If there is an overlap, then the objects
|
||||||
* there is an overlap, then the objects collide utilizing the Double Dispatch pattern.
|
* collide utilizing the Double Dispatch pattern.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
@ -28,7 +26,8 @@ public class App {
|
|||||||
/**
|
/**
|
||||||
* 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(String[] args) {
|
||||||
// initialize game objects and print their status
|
// initialize game objects and print their status
|
||||||
@ -42,8 +41,9 @@ public class App {
|
|||||||
|
|
||||||
// collision check
|
// collision check
|
||||||
objects.stream().forEach(o1 -> objects.stream().forEach(o2 -> {
|
objects.stream().forEach(o1 -> objects.stream().forEach(o2 -> {
|
||||||
if (o1 != o2 && o1.intersectsWith(o2))
|
if (o1 != o2 && o1.intersectsWith(o2)) {
|
||||||
o1.collision(o2);
|
o1.collision(o2);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
System.out.println("");
|
System.out.println("");
|
||||||
|
|
||||||
|
@ -12,6 +12,9 @@ public class Rectangle {
|
|||||||
private int right;
|
private int right;
|
||||||
private int bottom;
|
private int bottom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public Rectangle(int left, int top, int right, int bottom) {
|
public Rectangle(int left, int top, int right, int bottom) {
|
||||||
this.left = left;
|
this.left = left;
|
||||||
this.top = top;
|
this.top = top;
|
||||||
|
@ -2,8 +2,6 @@ package com.iluwatar.doubledispatch;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.iluwatar.doubledispatch.App;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Application test
|
* Application test
|
||||||
|
@ -107,9 +107,9 @@ public abstract class CollisionTest<O extends GameObject> {
|
|||||||
final String targetName = target.getClass().getSimpleName();
|
final String targetName = target.getClass().getSimpleName();
|
||||||
final String otherName = other.getClass().getSimpleName();
|
final String otherName = other.getClass().getSimpleName();
|
||||||
|
|
||||||
final String errorMessage = expectTargetOnFire ?
|
final String errorMessage = expectTargetOnFire
|
||||||
"Expected [" + targetName + "] to be on fire after colliding with [" + otherName + "] but it was not!" :
|
? "Expected [" + targetName + "] to be on fire after colliding with [" + otherName + "] but it was not!"
|
||||||
"Expected [" + targetName + "] not to be on fire after colliding with [" + otherName + "] but it was!";
|
: "Expected [" + targetName + "] not to be on fire after colliding with [" + otherName + "] but it was!";
|
||||||
|
|
||||||
assertEquals(errorMessage, expectTargetOnFire, target.isOnFire());
|
assertEquals(errorMessage, expectTargetOnFire, target.isOnFire());
|
||||||
}
|
}
|
||||||
@ -126,9 +126,9 @@ public abstract class CollisionTest<O extends GameObject> {
|
|||||||
final String targetName = target.getClass().getSimpleName();
|
final String targetName = target.getClass().getSimpleName();
|
||||||
final String otherName = other.getClass().getSimpleName();
|
final String otherName = other.getClass().getSimpleName();
|
||||||
|
|
||||||
final String errorMessage = expectedDamage ?
|
final String errorMessage = expectedDamage
|
||||||
"Expected [" + targetName + "] to be damaged after colliding with [" + otherName + "] but it was not!" :
|
? "Expected [" + targetName + "] to be damaged after colliding with [" + otherName + "] but it was not!"
|
||||||
"Expected [" + targetName + "] not to be damaged after colliding with [" + otherName + "] but it was!";
|
: "Expected [" + targetName + "] not to be damaged after colliding with [" + otherName + "] but it was!";
|
||||||
|
|
||||||
assertEquals(errorMessage, expectedDamage, target.isDamaged());
|
assertEquals(errorMessage, expectedDamage, target.isDamaged());
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,13 @@ title: Event Aggregator
|
|||||||
folder: event-aggregator
|
folder: event-aggregator
|
||||||
permalink: /patterns/event-aggregator/
|
permalink: /patterns/event-aggregator/
|
||||||
categories: Structural
|
categories: Structural
|
||||||
tags: Java
|
tags:
|
||||||
|
- Java
|
||||||
|
- Difficulty-Beginner
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** A system with lots of objects can lead to complexities when a
|
## Intent
|
||||||
|
A system with lots of objects can lead to complexities when a
|
||||||
client wants to subscribe to events. The client has to find and register for
|
client wants to subscribe to events. The client has to find and register for
|
||||||
each object individually, if each object has multiple events then each event
|
each object individually, if each object has multiple events then each event
|
||||||
requires a separate subscription. An Event Aggregator acts as a single source
|
requires a separate subscription. An Event Aggregator acts as a single source
|
||||||
@ -16,7 +19,8 @@ allowing clients to register with just the aggregator.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Applicability:** Use the Event Aggregator pattern when
|
## Applicability
|
||||||
|
Use the Event Aggregator pattern when
|
||||||
|
|
||||||
* Event Aggregator is a good choice when you have lots of objects that are
|
* Event Aggregator is a good choice when you have lots of objects that are
|
||||||
potential event sources. Rather than have the observer deal with registering
|
potential event sources. Rather than have the observer deal with registering
|
||||||
@ -24,6 +28,6 @@ allowing clients to register with just the aggregator.
|
|||||||
Aggregator. As well as simplifying registration, a Event Aggregator also
|
Aggregator. As well as simplifying registration, a Event Aggregator also
|
||||||
simplifies the memory management issues in using observers.
|
simplifies the memory management issues in using observers.
|
||||||
|
|
||||||
**Credits:**
|
## Credits
|
||||||
|
|
||||||
* [Martin Fowler - Event Aggregator](http://martinfowler.com/eaaDev/EventAggregator.html)
|
* [Martin Fowler - Event Aggregator](http://martinfowler.com/eaaDev/EventAggregator.html)
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.9.0-SNAPSHOT</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>event-aggregator</artifactId>
|
<artifactId>event-aggregator</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user