Merge remote-tracking branch 'upstream/master'

This commit is contained in:
hoswey 2016-01-09 22:26:18 +08:00
commit 2f84369003
519 changed files with 8471 additions and 1766 deletions

4
CONTRIBUTING.MD Normal file
View 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.

View File

@ -2,11 +2,10 @@
that smart and dearly wants an empty line before a heading to be able to
display it as such, e.g. website) -->
# Design pattern samples in Java
# Design patterns implemented in Java
[![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master)](https://travis-ci.org/iluwatar/java-design-patterns)
[![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5634/badge.svg)](https://scan.coverity.com/projects/5634)
[![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
# Introduction
@ -40,7 +39,7 @@ patterns by any of the following approaches
# 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

View File

@ -7,26 +7,30 @@ categories: Creational
tags:
- Java
- 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.
![alt text](./etc/abstract-factory_1.png "Abstract Factory")
**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 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
* 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)
**Credits**
## Credits
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>abstract-factory</artifactId>
<dependencies>

View File

@ -1,20 +1,18 @@
package com.iluwatar.abstractfactory;
/**
*
* The Abstract Factory pattern provides a way to encapsulate a group of individual factories that
* have a common theme without specifying their concrete classes. In normal usage, the client
* software creates a concrete implementation of the abstract factory and then uses the generic
* interface of the factory to create the concrete objects that are part of the theme. The client
* does not know (or care) which concrete objects it gets from each of these internal factories,
* since it uses only the generic interfaces of their products. This pattern separates the details
* of implementation of a set of objects from their general usage and relies on object composition,
* as object creation is implemented in methods exposed in the factory interface.
* The Abstract Factory pattern provides a way to encapsulate a group of individual factories that have a common theme
* without specifying their concrete classes. In normal usage, the client software creates a concrete implementation of
* the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part
* of the theme. The client does not know (or care) which concrete objects it gets from each of these internal
* factories, since it uses only the generic interfaces of their products. This pattern separates the details of
* implementation of a set of objects from their general usage and relies on object composition, as object creation is
* implemented in methods exposed in the factory interface.
* <p>
* The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) and
* its implementations ({@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses both
* concrete implementations to create a king, a castle and an army.
* The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) and its implementations (
* {@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses both concrete implementations to create a
* king, a castle and an army.
*
*/
public class App {
@ -23,11 +21,8 @@ public class App {
private Castle castle;
private Army army;
/**
* Creates kingdom
*
* @param factory
*/
public void createKingdom(final KingdomFactory factory) {
setKing(factory.createKing());
@ -47,14 +42,6 @@ public class App {
return factory.createKing();
}
Castle getCastle(final KingdomFactory factory) {
return factory.createCastle();
}
Army getArmy(final KingdomFactory factory) {
return factory.createArmy();
}
public King getKing() {
return king;
}
@ -62,6 +49,10 @@ public class App {
private void setKing(final King king) {
this.king = king;
}
Castle getCastle(final KingdomFactory factory) {
return factory.createCastle();
}
public Castle getCastle() {
return castle;
@ -70,6 +61,10 @@ public class App {
private void setCastle(final Castle castle) {
this.castle = castle;
}
Army getArmy(final KingdomFactory factory) {
return factory.createArmy();
}
public Army getArmy() {
return army;
@ -79,32 +74,32 @@ public class App {
this.army = army;
}
/**
* Program entry point
*
* @param args command line args
* @param args
* command line args
*/
public static void main(String[] args) {
App app = new App();
System.out.println("Elf Kingdom");
KingdomFactory elfKingdomFactory;
elfKingdomFactory = app.getElfKingdomFactory();
app.createKingdom(elfKingdomFactory);
System.out.println(app.getArmy().getDescription());
System.out.println(app.getCastle().getDescription());
System.out.println(app.getKing().getDescription());
System.out.println("\nOrc Kingdom");
KingdomFactory orcKingdomFactory;
orcKingdomFactory = app.getOrcKingdomFactory();
app.createKingdom(orcKingdomFactory);
System.out.println(app.getArmy().getDescription());
System.out.println(app.getCastle().getDescription());
System.out.println(app.getKing().getDescription());
App app = new App();
System.out.println("Elf Kingdom");
KingdomFactory elfKingdomFactory;
elfKingdomFactory = app.getElfKingdomFactory();
app.createKingdom(elfKingdomFactory);
System.out.println(app.getArmy().getDescription());
System.out.println(app.getCastle().getDescription());
System.out.println(app.getKing().getDescription());
System.out.println("\nOrc Kingdom");
KingdomFactory orcKingdomFactory;
orcKingdomFactory = app.getOrcKingdomFactory();
app.createKingdom(orcKingdomFactory);
System.out.println(app.getArmy().getDescription());
System.out.println(app.getCastle().getDescription());
System.out.println(app.getKing().getDescription());
}
}

View File

@ -7,26 +7,30 @@ categories: Structural
tags:
- Java
- 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
incompatible interfaces.
![alt text](./etc/adapter.png "Adapter")
**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 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.
**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)
**Credits**
## Credits
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>adapter</artifactId>
<dependencies>

View File

@ -1,18 +1,13 @@
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.Test;
import com.iluwatar.adapter.BattleFishingBoat;
import com.iluwatar.adapter.BattleShip;
import com.iluwatar.adapter.Captain;
import com.iluwatar.adapter.FishingBoat;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
* Test class

View File

@ -4,24 +4,29 @@ title: Async Method Invocation
folder: async-method-invocation
permalink: /patterns/async-method-invocation/
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
processing of multiple independent tasks and retrieving the results via
callbacks or waiting until everything is done.
![alt text](./etc/async-method-invocation.png "Async Method Invocation")
**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 need to improve the performance of a group of sequential tasks
* you have limited amount of processing capacity or long running tasks and the
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)
* [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx) (.NET)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>async-method-invocation</artifactId>
<dependencies>

View File

@ -4,24 +4,23 @@ import java.util.concurrent.Callable;
/**
* 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
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated value,
* <code>AsyncCallback</code> which can be provided to be executed on task completion and <code>AsyncExecutor</code>
* that manages the execution of the async tasks.
* <p>
* The main method shows example flow of async invocations. The main thread starts multiple tasks
* with variable durations and then continues its own work. When the main thread has done it's job
* it collects the results of the async tasks. Two of the tasks are handled with callbacks, meaning
* the callbacks are executed immediately when the tasks complete.
* The main method shows example flow of async invocations. The main thread starts multiple tasks with variable
* durations and then continues its own work. When the main thread has done it's job it collects the results of the
* async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are executed immediately when the
* tasks complete.
* <p>
* Noteworthy difference of thread usage between the async results and callbacks is that the async
* results are collected in the main thread but the callbacks are executed within the worker
* threads. This should be noted when working with thread pools.
* Noteworthy difference of thread usage between the async results and callbacks is that the async results are collected
* in the main thread but the callbacks are executed within the worker threads. This should be noted when working with
* thread pools.
* <p>
* Java provides its own implementations of async method invocation pattern. FutureTask,
* CompletableFuture and ExecutorService are the real world implementations of this pattern. But due
* to the nature of parallel programming, the implementations are not trivial. This example does not
* take all possible scenarios into account but rather provides a simple version that helps to
* understand the pattern.
* Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture and
* ExecutorService are the real world implementations of this pattern. But due to the nature of parallel programming,
* the implementations are not trivial. This example does not take all possible scenarios into account but rather
* provides a simple version that helps to understand the pattern.
*
* @see AsyncResult
* @see AsyncCallback
@ -33,6 +32,9 @@ import java.util.concurrent.Callable;
*/
public class App {
/**
* Program entry point
*/
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
AsyncExecutor executor = new ThreadAsyncExecutor();
@ -41,10 +43,8 @@ public class App {
AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
AsyncResult<Integer> asyncResult4 =
executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
AsyncResult<String> asyncResult5 =
executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
AsyncResult<Integer> asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
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
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.
*
* @param value value to evaluate
* @param delayMillis artificial delay in milliseconds
* @param value
* value to evaluate
* @param delayMillis
* artificial delay in milliseconds
* @return new callable for lazy evaluation
*/
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.
*
* @param name callback name
* @param name
* callback name
* @return new async callback
*/
private static <T> AsyncCallback<T> callback(String name) {

View File

@ -5,8 +5,6 @@ import java.util.concurrent.ExecutionException;
/**
*
* AsyncResult interface
*
* @param <T>
*/
public interface AsyncResult<T> {

View File

@ -29,13 +29,12 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
} catch (Exception ex) {
result.setException(ex);
}
}, "executor-" + idx.incrementAndGet()).start();
} , "executor-" + idx.incrementAndGet()).start();
return result;
}
@Override
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException,
InterruptedException {
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException {
if (asyncResult.isCompleted()) {
return asyncResult.getValue();
} else {
@ -45,9 +44,8 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
}
/**
* Simple implementation of async result that allows completing it successfully with a value or
* exceptionally with an exception. A really simplified version from its real life cousins
* FutureTask and CompletableFuture.
* Simple implementation of async result that allows completing it successfully with a value or exceptionally with an
* exception. A really simplified version from its real life cousins FutureTask and CompletableFuture.
*
* @see java.util.concurrent.FutureTask
* @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
* thread waiting for completion.
* Sets the value from successful execution and executes callback if available. Notifies any thread waiting for
* completion.
*
* @param value value of the evaluated task
* @param value
* value of the evaluated task
*/
void setValue(T 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
* thread waiting for completion.
* Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for
* completion.
*
* @param exception exception of the failed task
* @param exception
* exception of the failed task
*/
void setException(Exception exception) {
this.exception = exception;
@ -102,7 +102,7 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
@Override
public boolean isCompleted() {
return (state > RUNNING);
return state > RUNNING;
}
@Override

View File

@ -8,19 +8,9 @@ import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import static org.junit.Assert.assertEquals;
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.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
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.Mockito.*;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
@ -55,8 +45,7 @@ public class ThreadAsyncExecutorTest {
}
/**
* Test used to verify the happy path of
* {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)}
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)}
*/
@Test(timeout = 3000)
public void testSuccessfulTaskWithCallback() throws Exception {
@ -77,8 +66,7 @@ public class ThreadAsyncExecutorTest {
verify(task, times(1)).call();
// ... same for the callback, we expect our object
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor
.forClass((Class) Optional.class);
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
verify(callback, times(1)).onComplete(eq(result), optionalCaptor.capture());
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
* task takes a while to execute
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while
* to execute
*/
@Test(timeout = 5000)
public void testLongRunningTaskWithoutCallback() throws Exception {
@ -111,8 +99,7 @@ public class ThreadAsyncExecutorTest {
try {
asyncResult.getValue();
fail(
"Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
} catch (IllegalStateException e) {
assertNotNull(e.getMessage());
}
@ -130,9 +117,8 @@ public class ThreadAsyncExecutorTest {
}
/**
* Test used to verify the happy path of
* {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when a task takes a while to
* execute
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when a task
* takes a while to execute
*/
@Test(timeout = 5000)
public void testLongRunningTaskWithCallback() throws Exception {
@ -155,8 +141,7 @@ public class ThreadAsyncExecutorTest {
try {
asyncResult.getValue();
fail(
"Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
} catch (IllegalStateException e) {
assertNotNull(e.getMessage());
}
@ -164,8 +149,7 @@ public class ThreadAsyncExecutorTest {
// Our task should only execute once, but it can take a while ...
verify(task, timeout(3000).times(1)).call();
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor
.forClass((Class) Optional.class);
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
verify(callback, timeout(3000).times(1)).onComplete(eq(result), optionalCaptor.capture());
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
* task takes a while to execute, while waiting on the result using
* {@link ThreadAsyncExecutor#endProcess(AsyncResult)}
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while
* to execute, while waiting on the result using {@link ThreadAsyncExecutor#endProcess(AsyncResult)}
*/
@Test(timeout = 5000)
public void testEndProcess() throws Exception {
@ -204,8 +187,7 @@ public class ThreadAsyncExecutorTest {
try {
asyncResult.getValue();
fail(
"Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
} catch (IllegalStateException e) {
assertNotNull(e.getMessage());
}
@ -220,8 +202,7 @@ public class ThreadAsyncExecutorTest {
}
/**
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when
* the callable is 'null'
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when the callable is 'null'
*/
@Test(timeout = 3000)
public void testNullTask() throws Exception {
@ -229,8 +210,7 @@ public class ThreadAsyncExecutorTest {
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final AsyncResult<Object> asyncResult = executor.startProcess(null);
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.",
asyncResult);
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
@ -246,9 +226,8 @@ public class ThreadAsyncExecutorTest {
}
/**
* Test used to verify the behaviour of
* {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when the callable is 'null',
* but the asynchronous callback is provided
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when the
* callable is 'null', but the asynchronous callback is provided
*/
@Test(timeout = 3000)
public void testNullTaskWithCallback() throws Exception {
@ -257,13 +236,11 @@ public class ThreadAsyncExecutorTest {
final AsyncCallback<Object> callback = mock(AsyncCallback.class);
final AsyncResult<Object> asyncResult = executor.startProcess(null, callback);
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.",
asyncResult);
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor
.forClass((Class) Optional.class);
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
verify(callback, times(1)).onComplete(Matchers.isNull(), optionalCaptor.capture());
final Optional<Exception> optionalException = optionalCaptor.getValue();
@ -286,9 +263,8 @@ public class ThreadAsyncExecutorTest {
}
/**
* Test used to verify the behaviour of
* {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when both the callable and
* the asynchronous callback are 'null'
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when both
* the callable and the asynchronous callback are 'null'
*/
@Test(timeout = 3000)
public void testNullTaskWithNullCallback() throws Exception {
@ -296,9 +272,7 @@ public class ThreadAsyncExecutorTest {
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final AsyncResult<Object> asyncResult = executor.startProcess(null, null);
assertNotNull(
"The AsyncResult should not be 'null', even though the task and callback were 'null'.",
asyncResult);
assertNotNull("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
assertTrue(asyncResult.isCompleted());

View File

@ -7,17 +7,20 @@ categories: Structural
tags:
- Java
- 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.
![alt text](./etc/bridge.png "Bridge")
**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.
* 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 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)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>bridge</artifactId>
<dependencies>

View File

@ -2,8 +2,6 @@ package com.iluwatar.bridge;
import org.junit.Test;
import com.iluwatar.bridge.App;
/**
*
* Application test

View File

@ -7,24 +7,27 @@ categories: Creational
tags:
- Java
- 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
representations.
![alt text](./etc/builder_1.png "Builder")
**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 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)
* [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)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>builder</artifactId>
<dependencies>

View File

@ -93,6 +93,9 @@ public class Hero {
private Armor armor;
private Weapon weapon;
/**
* Constructor
*/
public HeroBuilder(Profession profession, String name) {
if (profession == null || name == null) {
throw new IllegalArgumentException("profession and name can not be null");

View File

@ -2,8 +2,6 @@ package com.iluwatar.builder;
import org.junit.Test;
import com.iluwatar.builder.App;
/**
*
* Application test

View File

@ -4,17 +4,21 @@ title: Business Delegate
folder: business-delegate
permalink: /patterns/business-delegate/
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
between the tiers and encapsulate knowledge about how to locate, connect to,
and interact with the business objects that make up the application.
![alt text](./etc/business-delegate.png "Business Delegate")
**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 to orchestrate calls to multiple business services

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>business-delegate</artifactId>
<dependencies>

View File

@ -5,20 +5,20 @@ package com.iluwatar.business.delegate;
*/
public class BusinessDelegate {
private BusinessLookup lookupService;
private BusinessService businessService;
private ServiceType serviceType;
private BusinessLookup lookupService;
private BusinessService businessService;
private ServiceType serviceType;
public void setLookupService(BusinessLookup businessLookup) {
this.lookupService = businessLookup;
}
public void setLookupService(BusinessLookup businessLookup) {
this.lookupService = businessLookup;
}
public void setServiceType(ServiceType serviceType) {
this.serviceType = serviceType;
}
public void setServiceType(ServiceType serviceType) {
this.serviceType = serviceType;
}
public void doTask() {
businessService = lookupService.getBusinessService(serviceType);
businessService.doProcessing();
}
public void doTask() {
businessService = lookupService.getBusinessService(serviceType);
businessService.doProcessing();
}
}

View File

@ -6,19 +6,23 @@ permalink: /patterns/caching/
categories: Other
tags:
- 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
fast-access storage, and are re-used to avoid having to acquire them again.
![alt text](./etc/caching.png "Caching")
**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.
**Credits**
## Credits
* [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)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>caching</artifactId>
<dependencies>

View File

@ -21,7 +21,7 @@ package com.iluwatar.caching;
* 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
* 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
* 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
@ -43,7 +43,7 @@ public class App {
* @param args command line args
*/
public static void main(String[] args) {
AppManager.initDB(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
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
// true to run the tests with MongoDB (provided that MongoDB is
// installed and socket connection is open).
@ -65,8 +65,8 @@ public class App {
AppManager.save(userAccount1);
System.out.println(AppManager.printCacheContent());
userAccount1 = AppManager.find("001");
userAccount1 = AppManager.find("001");
AppManager.find("001");
AppManager.find("001");
}
/**
@ -80,15 +80,15 @@ public class App {
AppManager.save(userAccount2);
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
AppManager.find("002");
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
userAccount2.setUserName("Jane G.");
AppManager.save(userAccount2);
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
AppManager.find("002");
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
AppManager.find("002");
}
/**
@ -106,12 +106,12 @@ public class App {
AppManager.save(userAccount4);
AppManager.save(userAccount5);
System.out.println(AppManager.printCacheContent());
userAccount3 = AppManager.find("003");
AppManager.find("003");
System.out.println(AppManager.printCacheContent());
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
AppManager.save(userAccount6);
System.out.println(AppManager.printCacheContent());
userAccount4 = AppManager.find("004");
AppManager.find("004");
System.out.println(AppManager.printCacheContent());
}
}

View File

@ -15,24 +15,30 @@ public class AppManager {
private static CachingPolicy cachingPolicy;
private AppManager() {
}
/**
*
* Developer/Tester is able to choose whether the application should use MongoDB as its underlying
* data storage or a simple Java data structure to (temporarily) store the data/objects during
* runtime.
*/
public static void initDB(boolean useMongoDB) {
if (useMongoDB) {
public static void initDb(boolean useMongoDb) {
if (useMongoDb) {
try {
DBManager.connect();
DbManager.connect();
} catch (ParseException e) {
e.printStackTrace();
}
} else {
DBManager.createVirtualDB();
DbManager.createVirtualDb();
}
}
/**
* Initialize caching policy
*/
public static void initCachingPolicy(CachingPolicy policy) {
cachingPolicy = policy;
if (cachingPolicy == CachingPolicy.BEHIND) {
@ -50,15 +56,21 @@ public class AppManager {
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) {
return CacheStore.readThrough(userID);
return CacheStore.readThrough(userId);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
return CacheStore.readThroughWithWriteBackPolicy(userID);
return CacheStore.readThroughWithWriteBackPolicy(userId);
}
return null;
}
/**
* Save user account
*/
public static void save(UserAccount userAccount) {
if (cachingPolicy == CachingPolicy.THROUGH) {
CacheStore.writeThrough(userAccount);

View File

@ -9,73 +9,99 @@ import java.util.ArrayList;
*/
public class CacheStore {
static LRUCache cache = null;
static LruCache cache = null;
public static void initCapacity(int capacity) {
if (null == cache)
cache = new LRUCache(capacity);
else
cache.setCapacity(capacity);
private CacheStore() {
}
public static UserAccount readThrough(String userID) {
if (cache.contains(userID)) {
/**
* Init cache capacity
*/
public static void initCapacity(int capacity) {
if (null == cache) {
cache = new LruCache(capacity);
} else {
cache.setCapacity(capacity);
}
}
/**
* Get user account using read-through cache
*/
public static UserAccount readThrough(String userId) {
if (cache.contains(userId)) {
System.out.println("# Cache Hit!");
return cache.get(userID);
return cache.get(userId);
}
System.out.println("# Cache Miss!");
UserAccount userAccount = DBManager.readFromDB(userID);
cache.set(userID, userAccount);
UserAccount userAccount = DbManager.readFromDb(userId);
cache.set(userId, userAccount);
return userAccount;
}
/**
* Get user account using write-through cache
*/
public static void writeThrough(UserAccount userAccount) {
if (cache.contains(userAccount.getUserID())) {
DBManager.updateDB(userAccount);
if (cache.contains(userAccount.getUserId())) {
DbManager.updateDb(userAccount);
} 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) {
if (cache.contains(userAccount.getUserID())) {
DBManager.updateDB(userAccount);
cache.invalidate(userAccount.getUserID()); // Cache data has been updated -- remove older
if (cache.contains(userAccount.getUserId())) {
DbManager.updateDb(userAccount);
cache.invalidate(userAccount.getUserId()); // Cache data has been updated -- remove older
// version from cache.
} 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!");
return cache.get(userID);
return cache.get(userId);
}
System.out.println("# Cache Miss!");
UserAccount userAccount = DBManager.readFromDB(userID);
UserAccount userAccount = DbManager.readFromDb(userId);
if (cache.isFull()) {
System.out.println("# Cache is FULL! Writing LRU data to DB...");
UserAccount toBeWrittenToDB = cache.getLRUData();
DBManager.upsertDB(toBeWrittenToDB);
UserAccount toBeWrittenToDb = cache.getLruData();
DbManager.upsertDb(toBeWrittenToDb);
}
cache.set(userID, userAccount);
cache.set(userId, userAccount);
return userAccount;
}
/**
* Set user account
*/
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...");
UserAccount toBeWrittenToDB = cache.getLRUData();
DBManager.upsertDB(toBeWrittenToDB);
UserAccount toBeWrittenToDb = cache.getLruData();
DbManager.upsertDb(toBeWrittenToDb);
}
cache.set(userAccount.getUserID(), userAccount);
cache.set(userAccount.getUserId(), userAccount);
}
/**
* Clears cache
*/
public static void clearCache() {
if (null != cache)
if (null != cache) {
cache.clear();
}
}
/**
@ -83,14 +109,18 @@ public class CacheStore {
*/
public static void flushCache() {
System.out.println("# flushCache...");
if (null == cache)
if (null == cache) {
return;
}
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
for (UserAccount userAccount : listOfUserAccounts) {
DBManager.upsertDB(userAccount);
DbManager.upsertDb(userAccount);
}
}
/**
* Print user accounts
*/
public static String print() {
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
StringBuilder sb = new StringBuilder();

View File

@ -21,7 +21,7 @@ import com.mongodb.client.model.UpdateOptions;
* during runtime (createVirtualDB()).</p>
*
*/
public class DBManager {
public class DbManager {
private static MongoClient mongoClient;
private static MongoDatabase db;
@ -29,21 +29,34 @@ public class DBManager {
private static HashMap<String, UserAccount> virtualDB;
public static void createVirtualDB() {
private DbManager() {
}
/**
* Create DB
*/
public static void createVirtualDb() {
useMongoDB = false;
virtualDB = new HashMap<String, UserAccount>();
}
/**
* Connect to DB
*/
public static void connect() throws ParseException {
useMongoDB = true;
mongoClient = new MongoClient();
db = mongoClient.getDatabase("test");
}
public static UserAccount readFromDB(String userID) {
/**
* Read user account from DB
*/
public static UserAccount readFromDb(String userId) {
if (!useMongoDB) {
if (virtualDB.containsKey(userID))
return virtualDB.get(userID);
if (virtualDB.containsKey(userId)) {
return virtualDB.get(userId);
}
return null;
}
if (null == db) {
@ -54,18 +67,22 @@ public class DBManager {
}
}
FindIterable<Document> iterable =
db.getCollection("user_accounts").find(new Document("userID", userID));
if (iterable == null)
db.getCollection("user_accounts").find(new Document("userID", userId));
if (iterable == null) {
return null;
}
Document doc = iterable.first();
UserAccount userAccount =
new UserAccount(userID, doc.getString("userName"), doc.getString("additionalInfo"));
new UserAccount(userId, doc.getString("userName"), doc.getString("additionalInfo"));
return userAccount;
}
public static void writeToDB(UserAccount userAccount) {
/**
* Write user account to DB
*/
public static void writeToDb(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
if (null == db) {
@ -76,13 +93,16 @@ public class DBManager {
}
}
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()));
}
public static void updateDB(UserAccount userAccount) {
/**
* Update DB
*/
public static void updateDb(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
if (null == db) {
@ -93,7 +113,7 @@ public class DBManager {
}
}
db.getCollection("user_accounts").updateOne(
new Document("userID", userAccount.getUserID()),
new Document("userID", userAccount.getUserId()),
new Document("$set", new Document("userName", userAccount.getUserName()).append(
"additionalInfo", userAccount.getAdditionalInfo())));
}
@ -102,9 +122,9 @@ public class DBManager {
*
* 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) {
virtualDB.put(userAccount.getUserID(), userAccount);
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
if (null == db) {
@ -115,8 +135,8 @@ public class DBManager {
}
}
db.getCollection("user_accounts").updateOne(
new Document("userID", userAccount.getUserID()),
new Document("$set", new Document("userID", userAccount.getUserID()).append("userName",
new Document("userID", userAccount.getUserId()),
new Document("$set", new Document("userID", userAccount.getUserId()).append("userName",
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo())),
new UpdateOptions().upsert(true));
}

View File

@ -12,16 +12,16 @@ import java.util.HashMap;
* LRU data is always at the end of the list.
*
*/
public class LRUCache {
public class LruCache {
class Node {
String userID;
String userId;
UserAccount userAccount;
Node previous;
Node next;
public Node(String userID, UserAccount userAccount) {
this.userID = userID;
public Node(String userId, UserAccount userAccount) {
this.userId = userId;
this.userAccount = userAccount;
}
}
@ -31,13 +31,16 @@ public class LRUCache {
Node head = null;
Node end = null;
public LRUCache(int capacity) {
public LruCache(int capacity) {
this.capacity = capacity;
}
public UserAccount get(String userID) {
if (cache.containsKey(userID)) {
Node node = cache.get(userID);
/**
* Get user account
*/
public UserAccount get(String userId) {
if (cache.containsKey(userId)) {
Node node = cache.get(userId);
remove(node);
setHead(node);
return node.userAccount;
@ -69,52 +72,63 @@ public class LRUCache {
public void setHead(Node node) {
node.next = head;
node.previous = null;
if (head != null)
if (head != null) {
head.previous = node;
}
head = node;
if (end == null)
if (end == null) {
end = head;
}
}
public void set(String userID, UserAccount userAccount) {
if (cache.containsKey(userID)) {
Node old = cache.get(userID);
/**
* Set user account
*/
public void set(String userId, UserAccount userAccount) {
if (cache.containsKey(userId)) {
Node old = cache.get(userId);
old.userAccount = userAccount;
remove(old);
setHead(old);
} else {
Node newNode = new Node(userID, userAccount);
Node newNode = new Node(userId, userAccount);
if (cache.size() >= capacity) {
System.out.println("# Cache is FULL! Removing " + end.userID + " from cache...");
cache.remove(end.userID); // remove LRU data from cache.
System.out.println("# Cache is FULL! Removing " + end.userId + " from cache...");
cache.remove(end.userId); // remove LRU data from cache.
remove(end);
setHead(newNode);
} else {
setHead(newNode);
}
cache.put(userID, newNode);
cache.put(userId, newNode);
}
}
public boolean contains(String userID) {
return cache.containsKey(userID);
public boolean contains(String userId) {
return cache.containsKey(userId);
}
public void invalidate(String userID) {
System.out.println("# " + userID + " has been updated! Removing older version from cache...");
Node toBeRemoved = cache.get(userID);
/**
* Invalidate cache for user
*/
public void invalidate(String userId) {
System.out.println("# " + userId + " has been updated! Removing older version from cache...");
Node toBeRemoved = cache.get(userId);
remove(toBeRemoved);
cache.remove(userID);
cache.remove(userId);
}
public boolean isFull() {
return cache.size() >= capacity;
}
public UserAccount getLRUData() {
public UserAccount getLruData() {
return end.userAccount;
}
/**
* Clear cache
*/
public void clear() {
head = null;
end = null;
@ -135,6 +149,9 @@ public class LRUCache {
return listOfCacheData;
}
/**
* Set cache capacity
*/
public void setCapacity(int newCapacity) {
if (capacity > newCapacity) {
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll

View File

@ -6,22 +6,25 @@ package com.iluwatar.caching;
*
*/
public class UserAccount {
private String userID;
private String userId;
private String userName;
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.additionalInfo = additionalInfo;
}
public String getUserID() {
return userID;
public String getUserId() {
return userId;
}
public void setUserID(String userID) {
this.userID = userID;
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
@ -42,6 +45,6 @@ public class UserAccount {
@Override
public String toString() {
return userID + ", " + userName + ", " + additionalInfo;
return userId + ", " + userName + ", " + additionalInfo;
}
}

View File

@ -16,7 +16,7 @@ public class AppTest {
*/
@Before
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
// tests with MongoDB (provided that MongoDB is installed and socket
// connection is open).

View File

@ -4,19 +4,25 @@ title: Callback
folder: callback
permalink: /patterns/callback/
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
at some convenient time.
![alt text](./etc/callback.png "Callback")
**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.
**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.

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>callback</artifactId>
<dependencies>

View File

@ -9,6 +9,9 @@ package com.iluwatar.callback;
*/
public class App {
/**
* Program entry point
*/
public static void main(String[] args) {
Task task = new SimpleTask();
Callback callback = new Callback() {

View File

@ -7,5 +7,5 @@ package com.iluwatar.callback;
*/
public interface Callback {
public void call();
void call();
}

View 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);
}
}

View File

@ -7,6 +7,9 @@ package com.iluwatar.callback;
*/
public abstract class Task {
/**
* Execute with callback
*/
public final void executeWith(Callback callback) {
execute();
if (callback != null) {

View File

@ -36,4 +36,22 @@ public class AppTest {
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);
}
}

View File

@ -7,25 +7,28 @@ categories: Behavioral
tags:
- Java
- 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
objects and pass the request along the chain until an object handles it.
![alt text](./etc/chain_1.png "Chain of Responsibility")
**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
* 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
**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)
* [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)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>chain</artifactId>
<dependencies>

View File

@ -13,6 +13,9 @@ public abstract class RequestHandler {
this.next = next;
}
/**
* Request handler
*/
public void handleRequest(Request req) {
if (next != null) {
next.handleRequest(req);

View File

@ -2,8 +2,6 @@ package com.iluwatar.chain;
import org.junit.Test;
import com.iluwatar.chain.App;
/**
*
* Application test

View 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>

View File

@ -25,8 +25,10 @@
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="fileExtensions" value="java, xml, properties"/>
<property name="severity" value="warning"/>
<property name="severity" value="error"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
@ -48,7 +50,7 @@
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<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://"/>
</module>
<module name="AvoidStarImport"/>
@ -61,7 +63,7 @@
</module>
<module name="NeedBraces"/>
<module name="LeftCurly">
<property name="maxLineLength" value="100"/>
<property name="maxLineLength" value="120"/>
</module>
<module name="RightCurly"/>
<module name="RightCurly">
@ -86,9 +88,6 @@
<module name="FallThrough"/>
<module name="UpperEll"/>
<module name="ModifierOrder"/>
<module name="EmptyLineSeparator">
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="SeparatorWrap">
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
@ -97,42 +96,19 @@
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName">
<message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern"
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>
<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<module name="NoFinalizer"/>
<module name="GenericWhitespace">
<message key="ws.followed"
@ -157,14 +133,6 @@
<property name="allowedAbbreviationLength" value="1"/>
</module>
<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="OperatorWrap">
<property name="option" value="NL"/>
@ -180,11 +148,6 @@
</module>
<module name="NonEmptyAtclauseDescription"/>
<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">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<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="allowMissingReturnTag" value="true"/>
<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"/>
</module>
<module name="MethodName">
@ -205,4 +168,5 @@
</module>
<module name="SingleLineJavadoc"/>
</module>
</module>

View File

@ -7,17 +7,21 @@ categories: Behavioral
tags:
- Java
- 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
support undoable operations.
![alt text](./etc/command.png "Command")
**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.
* 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
* 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
* implement callback 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)
**Credits**
## Credits
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>command</artifactId>
<dependencies>

View File

@ -30,6 +30,9 @@ public abstract class Target {
@Override
public abstract String toString();
/**
* Print status
*/
public void printStatus() {
System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, getSize(),
getVisibility()));

View File

@ -15,12 +15,18 @@ public class Wizard {
public Wizard() {}
/**
* Cast spell
*/
public void castSpell(Command command, Target target) {
System.out.println(this + " casts " + command + " at " + target);
command.execute(target);
undoStack.offerLast(command);
}
/**
* Undo last spell
*/
public void undoLastSpell() {
if (!undoStack.isEmpty()) {
Command previousSpell = undoStack.pollLast();
@ -30,6 +36,9 @@ public class Wizard {
}
}
/**
* Redo last spell
*/
public void redoLastSpell() {
if (!redoStack.isEmpty()) {
Command previousSpell = redoStack.pollLast();

View File

@ -7,24 +7,27 @@ categories: Structural
tags:
- Java
- 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
of objects uniformly.
![alt text](./etc/composite_1.png "Composite")
**Applicability:** Use the Composite pattern when
## Applicability
Use the Composite pattern when
* 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
**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)
* [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)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>composite</artifactId>
<dependencies>

View File

@ -24,6 +24,9 @@ public abstract class LetterComposite {
protected abstract void printThisAfter();
/**
* Print
*/
public void print() {
printThisBefore();
for (LetterComposite letter : children) {

View File

@ -9,6 +9,9 @@ import java.util.List;
*/
public class Sentence extends LetterComposite {
/**
* Constructor
*/
public Sentence(List<Word> words) {
for (Word w : words) {
this.add(w);

View File

@ -9,6 +9,9 @@ import java.util.List;
*/
public class Word extends LetterComposite {
/**
* Constructor
*/
public Word(List<Letter> letters) {
for (Letter l : letters) {
this.add(l);

View File

@ -2,8 +2,6 @@ package com.iluwatar.composite;
import org.junit.Test;
import com.iluwatar.composite.App;
/**
*
* Application test

View File

@ -3,22 +3,24 @@ layout: pattern
title: Data Access Object
folder: dao
permalink: /patterns/dao/
categories: Architectural
categories: Persistence Tier
tags:
- Java
- 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.
![alt text](./etc/dao.png "Data Access Object")
**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 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)

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>dao</artifactId>

View File

@ -21,7 +21,7 @@ import org.apache.log4j.Logger;
*/
public class App {
private static Logger LOGGER = Logger.getLogger(App.class);
private static Logger log = Logger.getLogger(App.class);
/**
* Program entry point.
@ -30,17 +30,17 @@ public class App {
*/
public static void main(final String[] args) {
final CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers());
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
LOGGER.info("customerDao.getCusterById(2): " + customerDao.getCustomerById(2));
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
log.info("customerDao.getCusterById(2): " + customerDao.getCustomerById(2));
final Customer customer = new Customer(4, "Dan", "Danson");
customerDao.addCustomer(customer);
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
customer.setFirstName("Daniel");
customer.setLastName("Danielson");
customerDao.updateCustomer(customer);
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
customerDao.deleteCustomer(customer);
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
}
/**

View File

@ -11,6 +11,9 @@ public class Customer {
private String firstName;
private String lastName;
/**
* Constructor
*/
public Customer(final int id, final String firstName, final String lastName) {
this.id = id;
this.firstName = firstName;
@ -52,10 +55,11 @@ public class Customer {
boolean isEqual = false;
if (this == o) {
isEqual = true;
} else if (o != null && (getClass() == o.getClass())) {
} else if (o != null && getClass() == o.getClass()) {
final Customer customer = (Customer) o;
if (getId() == customer.getId())
if (getId() == customer.getId()) {
isEqual = true;
}
}
return isEqual;
}

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true"
xmlns:log4j='http://jakarta.apache.org/log4j/'>
xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
</layout>
</appender>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="console" />
</root>
<root>
<level value="INFO" />
<appender-ref ref="console" />
</root>
</log4j:configuration>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1,18 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<class-diagram version="1.1.9" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
associations="true" dependencies="false" nesting-relationships="true" router="FAN">
<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">
<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"
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="2" language="java" name="com.iluwatar.decorator.SmartTroll" project="decorator"
file="/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java" binary="false" corner="BOTTOM_RIGHT">
<position height="124" width="134" x="116" y="244"/>
<class id="2" language="java" name="com.iluwatar.decorator.App" project="decorator"
file="/decorator/src/main/java/com/iluwatar/decorator/App.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="117" y="202"/>
<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"/>
@ -21,29 +21,46 @@
</class>
<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">
<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"
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="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="TARGET" refId="4"/>
</dependency>
<realization id="6">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="1"/>
</realization>
<association id="5">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="6" name="decorated"/>
<multiplicity id="7" minimum="0" maximum="1"/>
<association id="7">
<end type="SOURCE" refId="4" navigable="false">
<attribute id="8" name="decorated"/>
<multiplicity id="9" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="8">
<end type="SOURCE" refId="3"/>
<realization id="10">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="1"/>
</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"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -7,22 +7,26 @@ categories: Structural
tags:
- Java
- 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
functionality.
![alt text](./etc/decorator_1.png "Decorator")
**Applicability:** Use Decorator
## Applicability
Use Decorator
* to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects
* 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
**Credits**
## Credits
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>decorator</artifactId>
<dependencies>

View File

@ -8,7 +8,7 @@ package com.iluwatar.decorator;
* runtime.
* <p>
* 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.
*
*/
@ -30,7 +30,7 @@ public class App {
// change the behavior of the simple troll by adding a decorator
System.out.println("\nA smart looking troll surprises you.");
Hostile smart = new SmartTroll(troll);
Hostile smart = new SmartHostile(troll);
smart.attack();
smart.fleeBattle();
System.out.printf("Smart troll power %d.\n", smart.getAttackPower());

View File

@ -1,34 +1,34 @@
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}
* object.
*
*/
public class SmartTroll implements Hostile {
public class SmartHostile implements Hostile {
private Hostile decorated;
public SmartTroll(Hostile decorated) {
public SmartHostile(Hostile decorated) {
this.decorated = decorated;
}
@Override
public void attack() {
System.out.println("The troll throws a rock at you!");
System.out.println("It throws a rock at you!");
decorated.attack();
}
@Override
public int getAttackPower() {
// decorated troll power + 20 because it is smart
// decorated hostile's power + 20 because it is smart
return decorated.getAttackPower() + 20;
}
@Override
public void fleeBattle() {
System.out.println("The troll calls for help!");
System.out.println("It calls for help!");
decorated.fleeBattle();
}
}

View File

@ -2,8 +2,6 @@ package com.iluwatar.decorator;
import org.junit.Test;
import com.iluwatar.decorator.App;
/**
*
* Application test

View File

@ -3,9 +3,7 @@ package com.iluwatar.decorator;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.*;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
@ -13,15 +11,15 @@ import static org.mockito.internal.verification.VerificationModeFactory.times;
*
* @author Jeroen Meulemeester
*/
public class SmartTrollTest {
public class SmartHostileTest {
@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.
final Hostile simpleTroll = spy(new Troll());
// 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());
verify(simpleTroll, times(1)).getAttackPower();

View File

@ -4,15 +4,11 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintStream;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.*;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View 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
View 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.
![alt text](./etc/delegation.png "Delegate")
## 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
View 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>

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -4,10 +4,13 @@ title: Dependency Injection
folder: dependency-injection
permalink: /patterns/dependency-injection/
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
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
@ -16,7 +19,8 @@ inversion of control and single responsibility principles.
![alt text](./etc/dependency-injection.png "Dependency Injection")
**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
* to enable unit testing of classes in isolation using mock objects or stubs

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>dependency-injection</artifactId>
<dependencies>

View File

@ -2,8 +2,6 @@ package com.iluwatar.dependency.injection;
import org.junit.Test;
import com.iluwatar.dependency.injection.App;
/**
*
* Application test

View File

@ -1,15 +1,8 @@
package com.iluwatar.dependency.injection;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.PrintStream;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.*;
/**
* Date: 12/10/15 - 8:26 PM

View File

@ -4,17 +4,22 @@ title: Double Checked Locking
folder: double-checked-locking
permalink: /patterns/double-checked-locking/
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
if the locking criterion check indicates that locking is required does the
actual locking logic proceed.
![alt text](./etc/double_checked_locking_1.png "Double Checked Locking")
**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 on a method where method's behaviour changes according to the some constraints and these constraint change within this method.

View File

@ -3,7 +3,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>double-checked-locking</artifactId>
<dependencies>

View File

@ -28,7 +28,7 @@ public class App {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
executorService.execute(() -> {
while (inventory.addItem(new Item()));
while (inventory.addItem(new Item())) {};
});
}

View File

@ -17,12 +17,18 @@ public class Inventory {
private final List<Item> items;
private final Lock lock;
/**
* Constructor
*/
public Inventory(int inventorySize) {
this.inventorySize = inventorySize;
this.items = new ArrayList<>(inventorySize);
this.lock = new ReentrantLock();
}
/**
* Add item
*/
public boolean addItem(Item item) {
if (items.size() < inventorySize) {
lock.lock();

View File

@ -6,7 +6,4 @@ package com.iluwatar.doublechecked.locking;
*
*/
public class Item {
private String name;
private int level;
}

View File

@ -2,8 +2,6 @@ package com.iluwatar.doublechecked.locking;
import org.junit.Test;
import com.iluwatar.doublechecked.locking.App;
/**
*
* Application test

View File

@ -77,7 +77,7 @@ public class InventoryTest {
final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
executorService.execute(() -> {
while (inventory.addItem(new Item())) ;
while (inventory.addItem(new Item())) {};
});
}

View File

@ -4,18 +4,23 @@ title: Double Dispatch
folder: double-dispatch
permalink: /patterns/double-dispatch/
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.
![alt text](./etc/double-dispatch.png "Double Dispatch")
**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.
**Real world examples:**
## Real world examples
* [ObjectOutputStream](https://docs.oracle.com/javase/8/docs/api/java/io/ObjectOutputStream.html)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
<version>1.10.0-SNAPSHOT</version>
</parent>
<artifactId>double-dispatch</artifactId>
<dependencies>

View File

@ -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
* implementation of that method in the receiver. Sometimes the behaviour must also be determined by
* the type of the parameter.
* When a message with a parameter is sent to an object, the resultant behaviour is defined by the implementation of
* that method in the receiver. Sometimes the behaviour must also be determined by the type of the parameter.
* <p>
* One way to implement this would be to create multiple instanceof-checks for the methods
* parameter. However, this creates a maintenance issue. When new types are added we would also need
* to change the method's implementation and add a new instanceof-check. This violates the single
* responsibility principle - a class should have only one reason to change.
* One way to implement this would be to create multiple instanceof-checks for the methods parameter. However, this
* creates a maintenance issue. When new types are added we would also need to change the method's implementation and
* add a new instanceof-check. This violates the single responsibility principle - a class should have only one reason
* to change.
* <p>
* Instead of the instanceof-checks a better way is to make another virtual call on the parameter
* object. This way new functionality can be easily added without the need to modify existing
* implementation (open-closed principle).
* Instead of the instanceof-checks a better way is to make another virtual call on the parameter object. This way new
* functionality can be easily added without the need to modify existing implementation (open-closed principle).
* <p>
* In this example we have hierarchy of objects ({@link GameObject}) that can collide to each other.
* Each object has its own coordinates which are checked against the other objects' coordinates. If
* there is an overlap, then the objects collide utilizing the Double Dispatch pattern.
* In this example we have hierarchy of objects ({@link GameObject}) that can collide to each other. Each object has its
* own coordinates which are checked against the other objects' coordinates. If there is an overlap, then the objects
* collide utilizing the Double Dispatch pattern.
*
*/
public class App {
@ -28,7 +26,8 @@ public class App {
/**
* Program entry point
*
* @param args command line args
* @param args
* command line args
*/
public static void main(String[] args) {
// initialize game objects and print their status
@ -42,8 +41,9 @@ public class App {
// collision check
objects.stream().forEach(o1 -> objects.stream().forEach(o2 -> {
if (o1 != o2 && o1.intersectsWith(o2))
if (o1 != o2 && o1.intersectsWith(o2)) {
o1.collision(o2);
}
}));
System.out.println("");

View File

@ -12,6 +12,9 @@ public class Rectangle {
private int right;
private int bottom;
/**
* Constructor
*/
public Rectangle(int left, int top, int right, int bottom) {
this.left = left;
this.top = top;

View File

@ -2,8 +2,6 @@ package com.iluwatar.doubledispatch;
import org.junit.Test;
import com.iluwatar.doubledispatch.App;
/**
*
* Application test

View File

@ -107,9 +107,9 @@ public abstract class CollisionTest<O extends GameObject> {
final String targetName = target.getClass().getSimpleName();
final String otherName = other.getClass().getSimpleName();
final String errorMessage = expectTargetOnFire ?
"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!";
final String errorMessage = expectTargetOnFire
? "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!";
assertEquals(errorMessage, expectTargetOnFire, target.isOnFire());
}
@ -126,9 +126,9 @@ public abstract class CollisionTest<O extends GameObject> {
final String targetName = target.getClass().getSimpleName();
final String otherName = other.getClass().getSimpleName();
final String errorMessage = expectedDamage ?
"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!";
final String errorMessage = expectedDamage
? "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!";
assertEquals(errorMessage, expectedDamage, target.isDamaged());
}

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