Compare commits

..

11 Commits
1.8.0 ... urm

619 changed files with 9720 additions and 12917 deletions

View File

@ -1,18 +1,20 @@
language: java language: java
jdk: jdk:
- oraclejdk8 - oraclejdk8
env: env:
global: global:
- GH_REF: github.com/iluwatar/java-design-patterns.git - GH_REF: github.com/iluwatar/java-design-patterns.git
- secure: LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg= - secure: "LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg="
before_install: before_install:
- export DISPLAY=:99.0 - "export DISPLAY=:99.0"
- sh -e /etc/init.d/xvfb start - "sh -e /etc/init.d/xvfb start"
after_success: after_success:
- mvn clean test jacoco:report coveralls:report - mvn clean test jacoco:report coveralls:report
- bash update-ghpages.sh - bash update-ghpages.sh
# Migration to container-based infrastructure
sudo: false sudo: false

View File

@ -1,13 +0,0 @@
# Code Coverage Report generation
To generate the code coverage report, execute the following command:
> mvn clean verify
This will generate code coverage report in each of the modules. In order to view the same, open the following file in your browser.
> target/site/jacoco/index.html
Please note that the above folder is created under each of the modules. For example:
* adapter/target/site/jacoco/index.html
* busniess-delegate/target/site/jacoco/index.html

View File

@ -26,13 +26,7 @@ are familiar with the patterns.
Before you dive into the material, you should be familiar with various Before you dive into the material, you should be familiar with various
[Programming/Software Design Principles](http://webpro.github.io/programming-principles/). [Programming/Software Design Principles](http://webpro.github.io/programming-principles/).
All designs should be as simple as possible. You should start with KISS, YAGNI, Once you are familiar with these concepts you can start drilling down into patterns by any of the following approaches
and Do The Simplest Thing That Could Possibly Work principles. Complexity and
patterns should only be introduced when they are needed for practical
extensibility.
Once you are familiar with these concepts you can start drilling down into
patterns by any of the following approaches
- Using difficulty tags, `Difficulty-Beginner`, `Difficulty-Intermediate` & `Difficulty-Expert`. - Using difficulty tags, `Difficulty-Beginner`, `Difficulty-Intermediate` & `Difficulty-Expert`.
- Using pattern categories, `Creational`, `Behavioral` and others. - Using pattern categories, `Creational`, `Behavioral` and others.
@ -44,6 +38,7 @@ If you are willing to contribute to the project you will find the relevant infor
# Credits # Credits
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683) * [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683)
* [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/) * [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/)
* [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf) * [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf)
@ -53,6 +48,7 @@ If you are willing to contribute to the project you will find the relevant infor
* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) * [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)
* [Spring Data](http://www.amazon.com/Spring-Data-Mark-Pollack/dp/1449323952/ref=sr_1_1) * [Spring Data](http://www.amazon.com/Spring-Data-Mark-Pollack/dp/1449323952/ref=sr_1_1)
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697)
# License # License

View File

@ -4,13 +4,9 @@ title: Abstract Factory
folder: abstract-factory folder: abstract-factory
permalink: /patterns/abstract-factory/ permalink: /patterns/abstract-factory/
categories: Creational categories: Creational
tags: tags: Java
- Java
- Gang Of Four
--- ---
**Also known as:** Kit
**Intent:** Provide an interface for creating families of related or dependent **Intent:** Provide an interface for creating families of related or dependent
objects without specifying their concrete classes. objects without specifying their concrete classes.
@ -26,7 +22,3 @@ objects without specifying their concrete classes.
**Real world examples:** **Real world examples:**
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html) * [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>abstract-factory</artifactId> <artifactId>abstract-factory</artifactId>
<dependencies> <dependencies>

View File

@ -3,18 +3,12 @@ package com.iluwatar.abstractfactory;
/** /**
* *
* The Abstract Factory pattern provides a way to encapsulate a group of individual factories that * The essence of the Abstract Factory pattern is a factory interface
* have a common theme without specifying their concrete classes. In normal usage, the client * ({@link KingdomFactory}) and its implementations ({@link ElfKingdomFactory},
* software creates a concrete implementation of the abstract factory and then uses the generic * {@link OrcKingdomFactory}).
* 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> * <p>
* The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) and * The example uses both concrete implementations to create a king, a castle and
* its implementations ({@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses both * an army.
* concrete implementations to create a king, a castle and an army.
* *
*/ */
public class App { public class App {
@ -23,10 +17,8 @@ public class App {
private Castle castle; private Castle castle;
private Army army; private Army army;
/** /**
* Creates kingdom * Creates kingdom
*
* @param factory * @param factory
*/ */
public void createKingdom(final KingdomFactory factory) { public void createKingdom(final KingdomFactory factory) {
@ -78,33 +70,4 @@ public class App {
private void setArmy(final Army army) { private void setArmy(final Army army) {
this.army = army; this.army = army;
} }
/**
* Program entry point
*
* @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());
}
} }

View File

@ -7,7 +7,7 @@ package com.iluwatar.abstractfactory;
*/ */
public class OrcKing implements King { public class OrcKing implements King {
static final String DESCRIPTION = "This is the Orc king!"; static final String DESCRIPTION = "This is the Orc king!";
@Override @Override
public String getDescription() { public String getDescription() {

View File

@ -18,4 +18,5 @@ public class OrcKingdomFactory implements KingdomFactory {
public Army createArmy() { public Army createArmy() {
return new OrcArmy(); return new OrcArmy();
} }
} }

View File

@ -1,5 +1,4 @@
package com.iluwatar.abstractfactory; package com.iluwatar.abstractfactory;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -8,7 +7,7 @@ import org.junit.Test;
public class AppTest { public class AppTest {
private App app = new App(); private App app = new App();;
private KingdomFactory elfFactory; private KingdomFactory elfFactory;
private KingdomFactory orcFactory; private KingdomFactory orcFactory;

View File

@ -4,13 +4,9 @@ title: Adapter
folder: adapter folder: adapter
permalink: /patterns/adapter/ permalink: /patterns/adapter/
categories: Structural categories: Structural
tags: tags: Java
- Java
- Gang Of Four
--- ---
**Also known as:** Wrapper
**Intent:** Convert the interface of a class into another interface the clients **Intent:** Convert the interface of a class into another interface the clients
expect. Adapter lets classes work together that couldn't otherwise because of expect. Adapter lets classes work together that couldn't otherwise because of
incompatible interfaces. incompatible interfaces.
@ -26,7 +22,3 @@ incompatible interfaces.
**Real world examples:** **Real world examples:**
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) * [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>adapter</artifactId> <artifactId>adapter</artifactId>
<dependencies> <dependencies>
@ -14,10 +15,5 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,28 +1,25 @@
package com.iluwatar.adapter; package com.iluwatar.adapter;
/** /**
* An adapter helps two incompatible interfaces to work together. This is the real world definition
* for an adapter. Interfaces may be incompatible but the inner functionality should suit the need.
* The Adapter design pattern allows otherwise incompatible classes to work together by converting
* the interface of one class into an interface expected by the clients.
* *
* <p>There are two variations of the Adapter pattern: The class adapter implements the adaptee's * There are two variations of the Adapter pattern: The class adapter implements
* interface whereas the object adapter uses composition to contain the adaptee in the adapter * the adaptee's interface whereas the object adapter uses composition to
* object. This example uses the object adapter approach. * contain the adaptee in the adapter object. This example uses the object
* adapter approach.
* <p>
* The Adapter ({@link GnomeEngineer}) converts the interface of the target class
* ({@link GoblinGlider}) into a suitable one expected by the client
* ({@link GnomeEngineeringManager}).
* *
* <p>The Adapter ({@link GnomeEngineer}) converts the interface of the target class (
* {@link GoblinGlider}) into a suitable one expected by the client ({@link GnomeEngineeringManager}
* ).
*/ */
public class App { public class App {
/** /**
* Program entry point. * Program entry point
*
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {
Engineer manager = new GnomeEngineeringManager(new GnomeEngineer()); Engineer manager = new GnomeEngineeringManager();
manager.operateDevice(); manager.operateDevice();
} }
} }

View File

@ -8,4 +8,5 @@ package com.iluwatar.adapter;
public interface Engineer { public interface Engineer {
void operateDevice(); void operateDevice();
} }

View File

@ -2,8 +2,8 @@ package com.iluwatar.adapter;
/** /**
* *
* Adapter class. Adapts the interface of the device ({@link GoblinGlider}) into {@link Engineer} * Adapter class. Adapts the interface of the device ({@link GoblinGlider}) into
* interface expected by the client ({@link GnomeEngineeringManager}). * {@link Engineer} interface expected by the client ({@link GnomeEngineeringManager}).
* *
*/ */
public class GnomeEngineer implements Engineer { public class GnomeEngineer implements Engineer {
@ -20,4 +20,5 @@ public class GnomeEngineer implements Engineer {
glider.gainSpeed(); glider.gainSpeed();
glider.takeOff(); glider.takeOff();
} }
} }

View File

@ -1,26 +1,20 @@
package com.iluwatar.adapter; package com.iluwatar.adapter;
/** /**
*
* GnomeEngineering manager uses {@link Engineer} to operate devices. * GnomeEngineering manager uses {@link Engineer} to operate devices.
*
*/ */
public class GnomeEngineeringManager implements Engineer { public class GnomeEngineeringManager implements Engineer {
private Engineer engineer; private Engineer engineer;
public GnomeEngineeringManager() { public GnomeEngineeringManager() {
engineer = new GnomeEngineer();
}
public GnomeEngineeringManager(Engineer engineer) {
this.engineer = engineer;
} }
@Override @Override
public void operateDevice() { public void operateDevice() {
engineer.operateDevice(); engineer.operateDevice();
} }
public void setEngineer(Engineer engineer) {
this.engineer = engineer;
}
} }

View File

@ -1,68 +0,0 @@
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;
/**
* An adapter helps two incompatible interfaces to work together. This is the real world definition
* for an adapter. Interfaces may be incompatible but the inner functionality should suit the need.
* The Adapter design pattern allows otherwise incompatible classes to work together by converting
* the interface of one class into an interface expected by the clients.
*
* <p>There are two variations of the Adapter pattern:
* The class adapter implements the adaptee's
* interface whereas the object adapter uses composition to contain the adaptee in the adapter
* object. This example uses the object adapter approach.
*
* <p>The Adapter ({@link GnomeEngineer}) converts the interface
* of the target class ({@link GoblinGlider}) into a suitable one expected by
* the client ({@link GnomeEngineeringManager}
* ).
*/
public class AdapterPatternTest {
private Map<String, Object> beans;
private static final String ENGINEER_BEAN = "engineer";
private static final String MANAGER_BEAN = "manager";
/**
* This method runs before the test execution and sets the bean objects in the beans Map.
*/
@Before
public void setup() {
beans = new HashMap<>();
GnomeEngineer gnomeEngineer = spy(new GnomeEngineer());
beans.put(ENGINEER_BEAN, gnomeEngineer);
GnomeEngineeringManager manager = new GnomeEngineeringManager();
manager.setEngineer((GnomeEngineer) beans.get(ENGINEER_BEAN));
beans.put(MANAGER_BEAN, manager);
}
/**
* This test asserts that when we call operateDevice() method on a manager bean, it is internally
* calling operateDevice method on the engineer object. The Adapter ({@link GnomeEngineer})
* converts the interface of the target class ( {@link GoblinGlider}) into a suitable one expected
* by the client ({@link GnomeEngineeringManager} ).
*/
@Test
public void testAdapter() {
Engineer manager = (Engineer) beans.get(MANAGER_BEAN);
// when manager is asked to operate device
manager.operateDevice();
// Manager internally calls the engineer object to operateDevice
Engineer engineer = (Engineer) beans.get(ENGINEER_BEAN);
verify(engineer).operateDevice();
}
}

View File

@ -0,0 +1,19 @@
package com.iluwatar.adapter;
import org.junit.Test;
import com.iluwatar.adapter.App;
/**
*
* Application test
*
*/
public class AppTest {
@Test
public void test() {
String[] args = {};
App.main(args);
}
}

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>async-method-invocation</artifactId> <artifactId>async-method-invocation</artifactId>
<dependencies> <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 * This application demonstrates the async method invocation pattern. Key parts of the pattern are
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated * <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated value,
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and * <code>AsyncCallback</code> which can be provided to be executed on task completion and
* <code>AsyncExecutor</code> that manages the execution of the async tasks. * <code>AsyncExecutor</code> that manages the execution of the async tasks.
* <p> * <p>
* The main method shows example flow of async invocations. The main thread starts multiple tasks * The main method shows example flow of async invocations. The main thread starts multiple tasks with
* with variable durations and then continues its own work. When the main thread has done it's job * variable durations and then continues its own work. When the main thread has done it's job it collects
* it collects the results of the async tasks. Two of the tasks are handled with callbacks, meaning * the results of the async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are
* the callbacks are executed immediately when the tasks complete. * executed immediately when the tasks complete.
* <p> * <p>
* Noteworthy difference of thread usage between the async results and callbacks is that the async * Noteworthy difference of thread usage between the async results and callbacks is that the async results
* results are collected in the main thread but the callbacks are executed within the worker * are collected in the main thread but the callbacks are executed within the worker threads. This should be
* threads. This should be noted when working with thread pools. * noted when working with thread pools.
* <p> * <p>
* Java provides its own implementations of async method invocation pattern. FutureTask, * Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture
* CompletableFuture and ExecutorService are the real world implementations of this pattern. But due * and ExecutorService are the real world implementations of this pattern. But due to the nature of parallel
* to the nature of parallel programming, the implementations are not trivial. This example does not * programming, the implementations are not trivial. This example does not take all possible scenarios into
* take all possible scenarios into account but rather provides a simple version that helps to * account but rather provides a simple version that helps to understand the pattern.
* understand the pattern.
* *
* @see AsyncResult * @see AsyncResult
* @see AsyncCallback * @see AsyncCallback
@ -41,10 +40,8 @@ public class App {
AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500)); AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300)); AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700)); AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
AsyncResult<Integer> asyncResult4 = AsyncResult<Integer> asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
executor.startProcess(lazyval(20, 400), callback("Callback result 4")); AsyncResult<String> asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
AsyncResult<String> asyncResult5 =
executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
// emulate processing in the current thread while async tasks are running in their own threads // emulate processing in the current thread while async tasks are running in their own threads
Thread.sleep(350); // Oh boy I'm working hard here Thread.sleep(350); // Oh boy I'm working hard here

View File

@ -18,4 +18,5 @@ public interface AsyncCallback<T> {
* @param ex empty value if execution succeeds, some exception if executions fails * @param ex empty value if execution succeeds, some exception if executions fails
*/ */
void onComplete(T value, Optional<Exception> ex); void onComplete(T value, Optional<Exception> ex);
} }

View File

@ -38,4 +38,5 @@ public interface AsyncExecutor {
* @throws InterruptedException if the execution is interrupted * @throws InterruptedException if the execution is interrupted
*/ */
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException; <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
} }

View File

@ -34,8 +34,7 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
} }
@Override @Override
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException {
InterruptedException {
if (asyncResult.isCompleted()) { if (asyncResult.isCompleted()) {
return asyncResult.getValue(); return asyncResult.getValue();
} else { } else {
@ -45,8 +44,8 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
} }
/** /**
* Simple implementation of async result that allows completing it successfully with a value or * Simple implementation of async result that allows completing it successfully with a value
* exceptionally with an exception. A really simplified version from its real life cousins * or exceptionally with an exception. A really simplified version from its real life cousins
* FutureTask and CompletableFuture. * FutureTask and CompletableFuture.
* *
* @see java.util.concurrent.FutureTask * @see java.util.concurrent.FutureTask
@ -71,8 +70,8 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
} }
/** /**
* Sets the value from successful execution and executes callback if available. Notifies any * Sets the value from successful execution and executes callback if available. Notifies
* thread waiting for completion. * any thread waiting for completion.
* *
* @param value value of the evaluated task * @param value value of the evaluated task
*/ */
@ -86,8 +85,8 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
} }
/** /**
* Sets the exception from failed execution and executes callback if available. Notifies any * Sets the exception from failed execution and executes callback if available. Notifies
* thread waiting for completion. * any thread waiting for completion.
* *
* @param exception exception of the failed task * @param exception exception of the failed task
*/ */

View File

@ -14,4 +14,5 @@ public class AppTest {
String[] args = {}; String[] args = {};
App.main(args); App.main(args);
} }
} }

View File

@ -4,13 +4,9 @@ title: Bridge
folder: bridge folder: bridge
permalink: /patterns/bridge/ permalink: /patterns/bridge/
categories: Structural categories: Structural
tags: tags: Java
- Java
- Gang Of Four
--- ---
**Also known as:** Handle/Body
**Intent:** Decouple an abstraction from its implementation so that the two can **Intent:** Decouple an abstraction from its implementation so that the two can
vary independently. vary independently.
@ -24,7 +20,3 @@ vary independently.
* changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled. * changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled.
* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies * you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies
* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation. * you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>bridge</artifactId> <artifactId>bridge</artifactId>
<dependencies> <dependencies>

View File

@ -2,38 +2,38 @@ package com.iluwatar.bridge;
/** /**
* *
* The Bridge pattern can also be thought of as two layers of abstraction. With Bridge, you can * In Bridge pattern both abstraction ({@link MagicWeapon}) and implementation
* decouple an abstraction from its implementation so that the two can vary independently. * ({@link MagicWeaponImpl}) have their own class hierarchies. The interface of the
* <p> * implementations can be changed without affecting the clients.
* In Bridge pattern both abstraction ({@link MagicWeapon}) and implementation (
* {@link MagicWeaponImpl}) have their own class hierarchies. The interface of the implementations
* can be changed without affecting the clients.
* *
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point
*
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {
BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(new Excalibur()); BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(
new Excalibur());
blindingMagicWeapon.wield(); blindingMagicWeapon.wield();
blindingMagicWeapon.blind(); blindingMagicWeapon.blind();
blindingMagicWeapon.swing(); blindingMagicWeapon.swing();
blindingMagicWeapon.unwield(); blindingMagicWeapon.unwield();
FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(new Mjollnir()); FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(
new Mjollnir());
flyingMagicWeapon.wield(); flyingMagicWeapon.wield();
flyingMagicWeapon.fly(); flyingMagicWeapon.fly();
flyingMagicWeapon.swing(); flyingMagicWeapon.swing();
flyingMagicWeapon.unwield(); flyingMagicWeapon.unwield();
SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(new Stormbringer()); SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(
new Stormbringer());
soulEatingMagicWeapon.wield(); soulEatingMagicWeapon.wield();
soulEatingMagicWeapon.swing(); soulEatingMagicWeapon.swing();
soulEatingMagicWeapon.eatSoul(); soulEatingMagicWeapon.eatSoul();
soulEatingMagicWeapon.unwield(); soulEatingMagicWeapon.unwield();
} }
} }

View File

@ -34,4 +34,5 @@ public class BlindingMagicWeapon extends MagicWeapon {
public void blind() { public void blind() {
getImp().blindImp(); getImp().blindImp();
} }
} }

View File

@ -24,6 +24,8 @@ public class Excalibur extends BlindingMagicWeaponImpl {
@Override @Override
public void blindImp() { public void blindImp() {
System.out.println("bright light streams from Excalibur blinding the enemy"); System.out
.println("bright light streams from Excalibur blinding the enemy");
} }
} }

View File

@ -22,4 +22,5 @@ public abstract class MagicWeapon {
public MagicWeaponImpl getImp() { public MagicWeaponImpl getImp() {
return imp; return imp;
} }
} }

View File

@ -24,6 +24,8 @@ public class Mjollnir extends FlyingMagicWeaponImpl {
@Override @Override
public void flyImp() { public void flyImp() {
System.out.println("Mjollnir hits the enemy in the air and returns back to the owner's hand"); System.out
.println("Mjollnir hits the enemy in the air and returns back to the owner's hand");
} }
} }

View File

@ -26,4 +26,5 @@ public class Stormbringer extends SoulEatingMagicWeaponImpl {
public void eatSoulImp() { public void eatSoulImp() {
System.out.println("Stormbringer devours the enemy's soul"); System.out.println("Stormbringer devours the enemy's soul");
} }
} }

View File

@ -4,9 +4,7 @@ title: Builder
folder: builder folder: builder
permalink: /patterns/builder/ permalink: /patterns/builder/
categories: Creational categories: Creational
tags: tags: Java
- Java
- Gang Of Four
--- ---
**Intent:** Separate the construction of a complex object from its **Intent:** Separate the construction of a complex object from its
@ -24,7 +22,3 @@ representations.
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) * [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) * [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>builder</artifactId> <artifactId>builder</artifactId>
<dependencies> <dependencies>

View File

@ -1,54 +1,41 @@
package com.iluwatar.builder; package com.iluwatar.builder;
import com.iluwatar.builder.Hero.HeroBuilder; import com.iluwatar. builder.Hero.HeroBuilder;
/** /**
* *
* The intention of the Builder pattern is to find a solution to the telescoping constructor * This is the Builder pattern variation as described by Joshua Bloch in
* anti-pattern. The telescoping constructor anti-pattern occurs when the increase of object * Effective Java 2nd Edition.
* constructor parameter combination leads to an exponential list of constructors. Instead of using
* numerous constructors, the builder pattern uses another object, a builder, that receives each
* initialization parameter step by step and then returns the resulting constructed object at once.
* <p> * <p>
* The Builder pattern has another benefit. It can be used for objects that contain flat data (html * We want to build {@link Hero} objects, but its construction is complex because of the
* code, SQL query, X.509 certificate...), that is to say, data that can't be easily edited. This * many parameters needed. To aid the user we introduce {@link HeroBuilder} class.
* type of data cannot be edited step by step and must be edited at once. The best way to construct * {@link HeroBuilder} takes the minimum parameters to build {@link Hero} object in its
* such an object is to use a builder class. * constructor. After that additional configuration for the {@link Hero} object can be
* <p> * done using the fluent {@link HeroBuilder} interface. When configuration is ready the
* In this example we have the Builder pattern variation as described by Joshua Bloch in Effective * build method is called to receive the final {@link Hero} object.
* Java 2nd Edition.
* <p>
* We want to build {@link Hero} objects, but its construction is complex because of the many
* parameters needed. To aid the user we introduce {@link HeroBuilder} class. {@link HeroBuilder}
* takes the minimum parameters to build {@link Hero} object in its constructor. After that
* additional configuration for the {@link Hero} object can be done using the fluent
* {@link HeroBuilder} interface. When configuration is ready the build method is called to receive
* the final {@link Hero} object.
* *
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point
*
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {
Hero mage = Hero mage = new HeroBuilder(Profession.MAGE, "Riobard")
new HeroBuilder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK) .withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER)
.withWeapon(Weapon.DAGGER).build(); .build();
System.out.println(mage); System.out.println(mage);
Hero warrior = Hero warrior = new HeroBuilder(Profession.WARRIOR, "Amberjill")
new HeroBuilder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND) .withHairColor(HairColor.BLOND)
.withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD) .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL)
.build(); .withWeapon(Weapon.SWORD).build();
System.out.println(warrior); System.out.println(warrior);
Hero thief = Hero thief = new HeroBuilder(Profession.THIEF, "Desmond")
new HeroBuilder(Profession.THIEF, "Desmond").withHairType(HairType.BALD) .withHairType(HairType.BALD).withWeapon(Weapon.BOW).build();
.withWeapon(Weapon.BOW).build();
System.out.println(thief); System.out.println(thief);
} }

View File

@ -7,8 +7,7 @@ package com.iluwatar.builder;
*/ */
public enum HairType { public enum HairType {
BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY( BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY("long curly");
"long curly");
private String title; private String title;

View File

@ -95,7 +95,8 @@ public class Hero {
public HeroBuilder(Profession profession, String name) { public HeroBuilder(Profession profession, String name) {
if (profession == null || name == null) { if (profession == null || name == null) {
throw new IllegalArgumentException("profession and name can not be null"); throw new IllegalArgumentException(
"profession and name can not be null");
} }
this.profession = profession; this.profession = profession;
this.name = name; this.name = name;

View File

@ -13,4 +13,5 @@ public enum Profession {
public String toString() { public String toString() {
return name().toLowerCase(); return name().toLowerCase();
} }
} }

View File

@ -13,4 +13,5 @@ public enum Weapon {
public String toString() { public String toString() {
return name().toLowerCase(); return name().toLowerCase();
} }
} }

View File

@ -2,7 +2,7 @@ package com.iluwatar.builder;
import org.junit.Test; import org.junit.Test;
import com.iluwatar.builder.App; import com.iluwatar. builder.App;
/** /**
* *

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>business-delegate</artifactId> <artifactId>business-delegate</artifactId>
<dependencies> <dependencies>
@ -15,10 +15,5 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,34 +1,29 @@
package com.iluwatar.business.delegate; package com.iluwatar.business.delegate;
/** /**
* The Business Delegate pattern adds an abstraction layer between the presentation and business
* tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate
* encapsulates knowledge about how to locate, connect to, and interact with the business objects
* that make up the application.
* *
* <p>Some of the services the Business Delegate uses are instantiated directly, and some can be * The Business Delegate pattern adds an abstraction layer between the presentation and business tiers.
* retrieved through service lookups. The Business Delegate itself may contain business logic too * By using the pattern we gain loose coupling between the tiers. The Business Delegate encapsulates
* potentially tying together multiple service calls, exception handling, retrying etc. * knowledge about how to locate, connect to, and interact with the business objects that make up
* the application.
* <p>
* Some of the services the Business Delegate uses are instantiated directly, and some can be retrieved
* through service lookups. The Business Delegate itself may contain business logic too potentially tying
* together multiple service calls, exception handling, retrying etc.
* <p>
* In this example the client ({@link Client}) utilizes a business delegate ({@link BusinessDelegate}) to execute a task.
* The Business Delegate then selects the appropriate service and makes the service call.
* *
* <p>In this example the client ({@link Client}) utilizes a business delegate (
* {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
* service and makes the service call.
*/ */
public class App { public class App {
/** /**
* Program entry point. * Program entry point
*
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {
BusinessDelegate businessDelegate = new BusinessDelegate(); BusinessDelegate businessDelegate = new BusinessDelegate();
BusinessLookup businessLookup = new BusinessLookup();
businessLookup.setEjbService(new EjbService());
businessLookup.setJmsService(new JmsService());
businessDelegate.setLookupService(businessLookup);
businessDelegate.setServiceType(ServiceType.EJB); businessDelegate.setServiceType(ServiceType.EJB);
Client client = new Client(businessDelegate); Client client = new Client(businessDelegate);

View File

@ -1,18 +1,16 @@
package com.iluwatar.business.delegate; package com.iluwatar.business.delegate;
/** /**
*
* BusinessDelegate separates the presentation and business tiers * BusinessDelegate separates the presentation and business tiers
*
*/ */
public class BusinessDelegate { public class BusinessDelegate {
private BusinessLookup lookupService; private BusinessLookup lookupService = new BusinessLookup();
private BusinessService businessService; private BusinessService businessService;
private ServiceType serviceType; private ServiceType serviceType;
public void setLookupService(BusinessLookup businessLookup) {
this.lookupService = businessLookup;
}
public void setServiceType(ServiceType serviceType) { public void setServiceType(ServiceType serviceType) {
this.serviceType = serviceType; this.serviceType = serviceType;
} }

View File

@ -1,31 +1,17 @@
package com.iluwatar.business.delegate; package com.iluwatar.business.delegate;
/** /**
* Class for performing service lookups. *
* Class for performing service lookups
*
*/ */
public class BusinessLookup { public class BusinessLookup {
private EjbService ejbService;
private JmsService jmsService;
/**
* @param serviceType Type of service instance to be returned.
* @return Service instance.
*/
public BusinessService getBusinessService(ServiceType serviceType) { public BusinessService getBusinessService(ServiceType serviceType) {
if (serviceType.equals(ServiceType.EJB)) { if (serviceType.equals(ServiceType.EJB)) {
return ejbService; return new EjbService();
} else { } else {
return jmsService; return new JmsService();
} }
} }
public void setJmsService(JmsService jmsService) {
this.jmsService = jmsService;
}
public void setEjbService(EjbService ejbService) {
this.ejbService = ejbService;
}
} }

View File

@ -0,0 +1,19 @@
package com.iluwatar.business.delegate;
import org.junit.Test;
import com.iluwatar.business.delegate.App;
/**
*
* Application test
*
*/
public class AppTest {
@Test
public void test() {
String[] args = {};
App.main(args);
}
}

View File

@ -1,78 +0,0 @@
package com.iluwatar.business.delegate;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.junit.Before;
import org.junit.Test;
/**
* The Business Delegate pattern adds an abstraction layer between the presentation and business
* tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate
* encapsulates knowledge about how to locate, connect to, and interact with the business objects
* that make up the application.
*
* <p>Some of the services the Business Delegate uses are instantiated directly, and some can be
* retrieved through service lookups. The Business Delegate itself may contain business logic too
* potentially tying together multiple service calls, exception handling, retrying etc.
*/
public class BusinessDelegateTest {
private EjbService ejbService;
private JmsService jmsService;
private BusinessLookup businessLookup;
private BusinessDelegate businessDelegate;
/**
* This method sets up the instance variables of this test class. It is executed before the
* execution of every test.
*/
@Before
public void setup() {
ejbService = spy(new EjbService());
jmsService = spy(new JmsService());
businessLookup = spy(new BusinessLookup());
businessLookup.setEjbService(ejbService);
businessLookup.setJmsService(jmsService);
businessDelegate = spy(new BusinessDelegate());
businessDelegate.setLookupService(businessLookup);
}
/**
* In this example the client ({@link Client}) utilizes a business delegate (
* {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
* service and makes the service call.
*/
@Test
public void testBusinessDelegate() {
// setup a client object
Client client = new Client(businessDelegate);
// set the service type
businessDelegate.setServiceType(ServiceType.EJB);
// action
client.doTask();
// verifying that the businessDelegate was used by client during doTask() method.
verify(businessDelegate).doTask();
verify(ejbService).doProcessing();
// set the service type
businessDelegate.setServiceType(ServiceType.JMS);
// action
client.doTask();
// verifying that the businessDelegate was used by client during doTask() method.
verify(businessDelegate, times(2)).doTask();
verify(jmsService).doProcessing();
}
}

1
caching/.gitignore vendored
View File

@ -1 +0,0 @@
/target/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

View File

@ -1,106 +0,0 @@
<?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 id="1" language="java" name="main.java.com.wssia.caching.App" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/App.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="249" y="150"/>
<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="main.java.com.wssia.caching.AppManager" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/AppManager.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="502" y="163"/>
<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="main.java.com.wssia.caching.CacheStore" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/CacheStore.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="537" y="436"/>
<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>
<enumeration id="4" language="java" name="main.java.com.wssia.caching.CachingPolicy" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/CachingPolicy.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="789" y="162"/>
<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>
</enumeration>
<class id="5" language="java" name="main.java.com.wssia.caching.DBManager" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/DBManager.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1137" y="134"/>
<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="6" language="java" name="main.java.com.wssia.caching.LRUCache" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/LRUCache.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="884" y="435"/>
<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="7" language="java" name="main.java.com.wssia.caching.UserAccount" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/UserAccount.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1140" y="405"/>
<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="8" language="java" name="test.java.com.wssia.caching.AppTest" project="CachingPatterns"
file="/CachingPatterns/src/test/java/com/wssia/caching/AppTest.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="251" y="374"/>
<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>
<association id="9">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="10" name="cachingPolicy"/>
<multiplicity id="11" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="12">
<end type="SOURCE" refId="8" navigable="false">
<attribute id="13" name="app"/>
<multiplicity id="14" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="15">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="16" name="cache"/>
<multiplicity id="17" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="6" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<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>

View File

@ -1,24 +0,0 @@
---
layout: pattern
title: Caching
folder: caching
permalink: /patterns/caching/
categories: Other
tags:
- Java
---
**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
* Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead.
**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

@ -1,51 +0,0 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.8.0</version>
</parent>
<artifactId>caching</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-core</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>bson</artifactId>
<version>3.0.4</version>
</dependency>
</dependencies>
<!--
Due to the use of MongoDB in the test of this pattern, TRAVIS and/or MAVEN might fail if the DB connection is
not open for the JUnit test. To avoid disrupting the compilation process, the surefire plug-in was used
to SKIP the running of the JUnit tests for this pattern. To ACTIVATE the running of the tests, change the
skipTests (below) flag to 'false' and vice-versa.
-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<skipTests>false</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,117 +0,0 @@
package com.iluwatar.caching;
/**
*
* The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing
* the resources immediately after their use. The resources retain their identity, are kept in some
* fast-access storage, and are re-used to avoid having to acquire them again. There are three main
* caching strategies/techniques in this pattern; each with their own pros and cons. They are:
* <code>write-through</code> which writes data to the cache and DB in a single transaction,
* <code>write-around</code> which writes data immediately into the DB instead of the cache, and
* <code>write-behind</code> which writes data into the cache initially whilst the data is only
* written into the DB when the cache is full. The <code>read-through</code> strategy is also
* included in the mentioned three strategies -- returns data from the cache to the caller <b>if</b>
* it exists <b>else</b> queries from DB and stores it into the cache for future use. These
* strategies determine when the data in the cache should be written back to the backing store (i.e.
* Database) and help keep both data sources synchronized/up-to-date. This pattern can improve
* performance and also helps to maintain consistency between data held in the cache and the data in
* the underlying data store.
* <p>
* In this example, the user account ({@link UserAccount}) entity is used as the underlying
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts
* a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The 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 (
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
* whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
* ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
* (depending on the preferred caching policy/strategy).
*
* <i>App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager</i>
* </p>
*
* @see CacheStore
* @See LRUCache
* @see CachingPolicy
*
*/
public class App {
/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {
AppManager.initDB(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
// and the App class to avoid Maven compilation errors. Set flag to
// true to run the tests with MongoDB (provided that MongoDB is
// installed and socket connection is open).
AppManager.initCacheCapacity(3);
App app = new App();
app.useReadAndWriteThroughStrategy();
app.useReadThroughAndWriteAroundStrategy();
app.useReadThroughAndWriteBehindStrategy();
}
/**
* Read-through and write-through
*/
public void useReadAndWriteThroughStrategy() {
System.out.println("# CachingPolicy.THROUGH");
AppManager.initCachingPolicy(CachingPolicy.THROUGH);
UserAccount userAccount1 = new UserAccount("001", "John", "He is a boy.");
AppManager.save(userAccount1);
System.out.println(AppManager.printCacheContent());
userAccount1 = AppManager.find("001");
userAccount1 = AppManager.find("001");
}
/**
* Read-through and write-around
*/
public void useReadThroughAndWriteAroundStrategy() {
System.out.println("# CachingPolicy.AROUND");
AppManager.initCachingPolicy(CachingPolicy.AROUND);
UserAccount userAccount2 = new UserAccount("002", "Jane", "She is a girl.");
AppManager.save(userAccount2);
System.out.println(AppManager.printCacheContent());
userAccount2 = 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");
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
}
/**
* Read-through and write-behind
*/
public void useReadThroughAndWriteBehindStrategy() {
System.out.println("# CachingPolicy.BEHIND");
AppManager.initCachingPolicy(CachingPolicy.BEHIND);
UserAccount userAccount3 = new UserAccount("003", "Adam", "He likes food.");
UserAccount userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
UserAccount userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
AppManager.save(userAccount3);
AppManager.save(userAccount4);
AppManager.save(userAccount5);
System.out.println(AppManager.printCacheContent());
userAccount3 = 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");
System.out.println(AppManager.printCacheContent());
}
}

View File

@ -1,75 +0,0 @@
package com.iluwatar.caching;
import java.text.ParseException;
/**
*
* AppManager helps to bridge the gap in communication between the main class and the application's
* back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
* also initialized here. Before the cache can be used, the size of the cache has to be set.
* Depending on the chosen caching policy, AppManager will call the appropriate function in the
* CacheStore class.
*
*/
public class AppManager {
private static CachingPolicy cachingPolicy;
/**
*
* Developer/Tester is able to choose whether the application should use MongoDB as its underlying
* data storage or a simple Java data structure to (temporarily) store the data/objects during
* runtime.
*/
public static void initDB(boolean useMongoDB) {
if (useMongoDB) {
try {
DBManager.connect();
} catch (ParseException e) {
e.printStackTrace();
}
} else {
DBManager.createVirtualDB();
}
}
public static void initCachingPolicy(CachingPolicy policy) {
cachingPolicy = policy;
if (cachingPolicy == CachingPolicy.BEHIND) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
CacheStore.flushCache();
}
}));
}
CacheStore.clearCache();
}
public static void initCacheCapacity(int capacity) {
CacheStore.initCapacity(capacity);
}
public static UserAccount find(String userID) {
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
return CacheStore.readThrough(userID);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
return CacheStore.readThroughWithWriteBackPolicy(userID);
}
return null;
}
public static void save(UserAccount userAccount) {
if (cachingPolicy == CachingPolicy.THROUGH) {
CacheStore.writeThrough(userAccount);
} else if (cachingPolicy == CachingPolicy.AROUND) {
CacheStore.writeAround(userAccount);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
CacheStore.writeBehind(userAccount);
}
}
public static String printCacheContent() {
return CacheStore.print();
}
}

View File

@ -1,104 +0,0 @@
package com.iluwatar.caching;
import java.util.ArrayList;
/**
*
* The caching strategies are implemented in this class.
*
*/
public class CacheStore {
static LRUCache cache = null;
public static void initCapacity(int capacity) {
if (null == cache)
cache = new LRUCache(capacity);
else
cache.setCapacity(capacity);
}
public static UserAccount readThrough(String userID) {
if (cache.contains(userID)) {
System.out.println("# Cache Hit!");
return cache.get(userID);
}
System.out.println("# Cache Miss!");
UserAccount userAccount = DBManager.readFromDB(userID);
cache.set(userID, userAccount);
return userAccount;
}
public static void writeThrough(UserAccount userAccount) {
if (cache.contains(userAccount.getUserID())) {
DBManager.updateDB(userAccount);
} else {
DBManager.writeToDB(userAccount);
}
cache.set(userAccount.getUserID(), userAccount);
}
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
// version from cache.
} else {
DBManager.writeToDB(userAccount);
}
}
public static UserAccount readThroughWithWriteBackPolicy(String userID) {
if (cache.contains(userID)) {
System.out.println("# Cache Hit!");
return cache.get(userID);
}
System.out.println("# Cache Miss!");
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);
}
cache.set(userID, userAccount);
return userAccount;
}
public static void writeBehind(UserAccount userAccount) {
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);
}
cache.set(userAccount.getUserID(), userAccount);
}
public static void clearCache() {
if (null != cache)
cache.clear();
}
/**
* Writes remaining content in the cache into the DB.
*/
public static void flushCache() {
System.out.println("# flushCache...");
if (null == cache)
return;
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
for (UserAccount userAccount : listOfUserAccounts) {
DBManager.upsertDB(userAccount);
}
}
public static String print() {
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
StringBuilder sb = new StringBuilder();
sb.append("\n--CACHE CONTENT--\n");
for (UserAccount userAccount : listOfUserAccounts) {
sb.append(userAccount.toString() + "\n");
}
sb.append("----\n");
return sb.toString();
}
}

View File

@ -1,20 +0,0 @@
package com.iluwatar.caching;
/**
*
* Enum class containing the three caching strategies implemented in the pattern.
*
*/
public enum CachingPolicy {
THROUGH("through"), AROUND("around"), BEHIND("behind");
private String policy;
private CachingPolicy(String policy) {
this.policy = policy;
}
public String getPolicy() {
return policy;
}
}

View File

@ -1,123 +0,0 @@
package com.iluwatar.caching;
import java.text.ParseException;
import java.util.HashMap;
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.UpdateOptions;
/**
*
* <p>DBManager handles the communication with the underlying data store i.e. Database. It contains the
* implemented methods for querying, inserting, and updating data. MongoDB was used as the database
* for the application.</p>
*
* <p>Developer/Tester is able to choose whether the application should use MongoDB as its underlying
* data storage (connect()) or a simple Java data structure to (temporarily) store the data/objects
* during runtime (createVirtualDB()).</p>
*
*/
public class DBManager {
private static MongoClient mongoClient;
private static MongoDatabase db;
private static boolean useMongoDB;
private static HashMap<String, UserAccount> virtualDB;
public static void createVirtualDB() {
useMongoDB = false;
virtualDB = new HashMap<String, UserAccount>();
}
public static void connect() throws ParseException {
useMongoDB = true;
mongoClient = new MongoClient();
db = mongoClient.getDatabase("test");
}
public static UserAccount readFromDB(String userID) {
if (!useMongoDB) {
if (virtualDB.containsKey(userID))
return virtualDB.get(userID);
return null;
}
if (null == db) {
try {
connect();
} catch (ParseException e) {
e.printStackTrace();
}
}
FindIterable<Document> iterable =
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"));
return userAccount;
}
public static void writeToDB(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
return;
}
if (null == db) {
try {
connect();
} catch (ParseException e) {
e.printStackTrace();
}
}
db.getCollection("user_accounts").insertOne(
new Document("userID", userAccount.getUserID()).append("userName",
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo()));
}
public static void updateDB(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
return;
}
if (null == db) {
try {
connect();
} catch (ParseException e) {
e.printStackTrace();
}
}
db.getCollection("user_accounts").updateOne(
new Document("userID", userAccount.getUserID()),
new Document("$set", new Document("userName", userAccount.getUserName()).append(
"additionalInfo", userAccount.getAdditionalInfo())));
}
/**
*
* Insert data into DB if it does not exist. Else, update it.
*/
public static void upsertDB(UserAccount userAccount) {
if (!useMongoDB) {
virtualDB.put(userAccount.getUserID(), userAccount);
return;
}
if (null == db) {
try {
connect();
} catch (ParseException e) {
e.printStackTrace();
}
}
db.getCollection("user_accounts").updateOne(
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

@ -1,146 +0,0 @@
package com.iluwatar.caching;
import java.util.ArrayList;
import java.util.HashMap;
/**
*
* Data structure/implementation of the application's cache. The data structure consists of a hash
* table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the
* LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated,
* the data is moved to the front of the list to depict itself as the most-recently-used data. The
* LRU data is always at the end of the list.
*
*/
public class LRUCache {
class Node {
String userID;
UserAccount userAccount;
Node previous;
Node next;
public Node(String userID, UserAccount userAccount) {
this.userID = userID;
this.userAccount = userAccount;
}
}
int capacity;
HashMap<String, Node> cache = new HashMap<String, Node>();
Node head = null;
Node end = null;
public LRUCache(int capacity) {
this.capacity = capacity;
}
public UserAccount get(String userID) {
if (cache.containsKey(userID)) {
Node node = cache.get(userID);
remove(node);
setHead(node);
return node.userAccount;
}
return null;
}
/**
*
* Remove node from linked list.
*/
public void remove(Node node) {
if (node.previous != null) {
node.previous.next = node.next;
} else {
head = node.next;
}
if (node.next != null) {
node.next.previous = node.previous;
} else {
end = node.previous;
}
}
/**
*
* Move node to the front of the list.
*/
public void setHead(Node node) {
node.next = head;
node.previous = null;
if (head != null)
head.previous = node;
head = node;
if (end == null)
end = head;
}
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);
if (cache.size() >= capacity) {
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);
}
}
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);
remove(toBeRemoved);
cache.remove(userID);
}
public boolean isFull() {
return cache.size() >= capacity;
}
public UserAccount getLRUData() {
return end.userAccount;
}
public void clear() {
head = null;
end = null;
cache.clear();
}
/**
*
* Returns cache data in list form.
*/
public ArrayList<UserAccount> getCacheDataInListForm() {
ArrayList<UserAccount> listOfCacheData = new ArrayList<UserAccount>();
Node temp = head;
while (temp != null) {
listOfCacheData.add(temp.userAccount);
temp = temp.next;
}
return listOfCacheData;
}
public void setCapacity(int newCapacity) {
if (capacity > newCapacity) {
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll
// just clear the cache.
} else {
this.capacity = newCapacity;
}
}
}

View File

@ -1,47 +0,0 @@
package com.iluwatar.caching;
/**
*
* Entity class (stored in cache and DB) used in the application.
*
*/
public class UserAccount {
private String userID;
private String userName;
private String additionalInfo;
public UserAccount(String userID, String userName, String additionalInfo) {
this.userID = userID;
this.userName = userName;
this.additionalInfo = additionalInfo;
}
public String getUserID() {
return userID;
}
public void setUserID(String userID) {
this.userID = userID;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getAdditionalInfo() {
return additionalInfo;
}
public void setAdditionalInfo(String additionalInfo) {
this.additionalInfo = additionalInfo;
}
@Override
public String toString() {
return userID + ", " + userName + ", " + additionalInfo;
}
}

View File

@ -1,41 +0,0 @@
package com.iluwatar.caching;
import org.junit.Before;
import org.junit.Test;
/**
*
* Application test
*
*/
public class AppTest {
App app;
/**
* Setup of application test includes: initializing DB connection and cache size/capacity.
*/
@Before
public void setUp() {
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).
AppManager.initCacheCapacity(3);
app = new App();
}
@Test
public void testReadAndWriteThroughStrategy() {
app.useReadAndWriteThroughStrategy();
}
@Test
public void testReadThroughAndWriteAroundStrategy() {
app.useReadThroughAndWriteAroundStrategy();
}
@Test
public void testReadThroughAndWriteBehindStrategy() {
app.useReadThroughAndWriteBehindStrategy();
}
}

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>callback</artifactId> <artifactId>callback</artifactId>
<dependencies> <dependencies>

View File

@ -2,9 +2,8 @@ package com.iluwatar.callback;
/** /**
* *
* Callback pattern is more native for functional languages where functions are treated as * Callback pattern is more native for functional languages where functions are treated as first-class citizens.
* first-class citizens. Prior to Java 8 callbacks can be simulated using simple (alike command) * Prior to Java 8 callbacks can be simulated using simple (alike command) interfaces.
* interfaces.
* *
*/ */
public class App { public class App {

View File

@ -9,6 +9,7 @@ public class SimpleTask extends Task {
@Override @Override
public void execute() { public void execute() {
System.out.println("Perform some important activity and after call the callback method."); System.out.println("Perform some important activity.");
} }
} }

View File

@ -2,38 +2,18 @@ package com.iluwatar.callback;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals; import com.iluwatar.callback.App;
/** /**
* Add a field as a counter. Every time the callback method is called increment this field. Unit
* test checks that the field is being incremented.
* *
* Could be done with mock objects as well where the call method call is verified. * Application test
*
*/ */
public class AppTest { public class AppTest {
private Integer callingCount = 0;
@Test @Test
public void test() { public void test() {
Callback callback = new Callback() { String[] args = {};
@Override App.main(args);
public void call() {
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

@ -4,9 +4,7 @@ title: Chain of responsibility
folder: chain folder: chain
permalink: /patterns/chain/ permalink: /patterns/chain/
categories: Behavioral categories: Behavioral
tags: tags: Java
- Java
- Gang Of Four
--- ---
**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
@ -25,7 +23,3 @@ objects and pass the request along the chain until an object handles it.
* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) * [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29)
* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) * [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>chain</artifactId> <artifactId>chain</artifactId>
<dependencies> <dependencies>

View File

@ -2,29 +2,24 @@ package com.iluwatar.chain;
/** /**
* *
* The Chain of Responsibility pattern is a design pattern consisting of command objects and a * Chain of Responsibility organizes request handlers ({@link RequestHandler}) into a
* series of processing objects. Each processing object contains logic that defines the types of * chain where each handler has a chance to act on the request on its turn. In
* command objects that it can handle; the rest are passed to the next processing object in the * this example the king ({@link OrcKing}) makes requests and the military orcs
* chain. A mechanism also exists for adding new processing objects to the end of this chain. * ({@link OrcCommander}, {@link OrcOfficer}, {@link OrcSoldier}) form the handler chain.
* <p>
* In this example we organize the request handlers ({@link RequestHandler}) into a chain where each
* handler has a chance to act on the request on its turn. Here the king ({@link OrcKing}) makes
* requests and the military orcs ({@link OrcCommander}, {@link OrcOfficer}, {@link OrcSoldier})
* form the handler chain.
* *
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point
*
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {
OrcKing king = new OrcKing(); OrcKing king = new OrcKing();
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle"));
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); king.makeRequest(new Request(RequestType.TORTURE_PRISONER,
"torture prisoner"));
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax"));
} }

View File

@ -4,13 +4,9 @@ title: Command
folder: command folder: command
permalink: /patterns/command/ permalink: /patterns/command/
categories: Behavioral categories: Behavioral
tags: tags: Java
- Java
- Gang Of Four
--- ---
**Also known as:** Action, Transaction
**Intent:** Encapsulate a request as an object, thereby letting you **Intent:** Encapsulate a request as an object, thereby letting you
parameterize clients with different requests, queue or log requests, and parameterize clients with different requests, queue or log requests, and
support undoable operations. support undoable operations.
@ -34,7 +30,3 @@ support undoable operations.
**Real world examples:** **Real world examples:**
* [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) * [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>command</artifactId> <artifactId>command</artifactId>
<dependencies> <dependencies>
@ -14,10 +15,5 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -2,22 +2,19 @@ package com.iluwatar.command;
/** /**
* *
* The Command pattern is a behavioral design pattern in which an object is used to encapsulate all * In Command pattern actions are objects that can be executed and undone.
* information needed to perform an action or trigger an event at a later time. This information
* includes the method name, the object that owns the method and values for the method parameters.
* <p> * <p>
* Four terms always associated with the command pattern are command, receiver, invoker and client. * Four terms always associated with the command pattern are command, receiver, invoker and client. A command
* A command object (spell) knows about the receiver (target) and invokes a method of the receiver. * object (spell) knows about the receiver (target) and invokes a method of the receiver. Values for parameters of
* Values for parameters of the receiver method are stored in the command. The receiver then does * the receiver method are stored in the command. The receiver then does the work. An invoker object (wizard)
* the work. An invoker object (wizard) knows how to execute a command, and optionally does * knows how to execute a command, and optionally does bookkeeping about the command execution. The invoker
* bookkeeping about the command execution. The invoker does not know anything about a concrete * does not know anything about a concrete command, it knows only about command interface. Both an invoker object
* command, it knows only about command interface. Both an invoker object and several command * and several command objects are held by a client object (app). The client decides which commands to execute at
* objects are held by a client object (app). The client decides which commands to execute at which * which points. To execute a command, it passes the command object to the invoker object.
* points. To execute a command, it passes the command object to the invoker object.
* <p> * <p>
* In other words, in this example the wizard casts spells on the goblin. The wizard keeps track of * In other words, in this example the wizard casts spells on the goblin. The wizard keeps track of the previous
* the previous spells cast, so it is easy to undo them. In addition, the wizard keeps track of the * spells cast, so it is easy to undo them. In addition, the wizard keeps track of the spells undone, so they
* spells undone, so they can be redone. * can be redone.
* *
* *
*/ */
@ -25,7 +22,6 @@ public class App {
/** /**
* Program entry point * Program entry point
*
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -31,8 +31,8 @@ public abstract class Target {
public abstract String toString(); public abstract String toString();
public void printStatus() { public void printStatus() {
System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, getSize(), System.out.println(String.format("%s, [size=%s] [visibility=%s]", this,
getVisibility())); getSize(), getVisibility()));
System.out.println(); System.out.println();
} }
} }

View File

@ -13,7 +13,8 @@ public class Wizard {
private Deque<Command> undoStack = new LinkedList<>(); private Deque<Command> undoStack = new LinkedList<>();
private Deque<Command> redoStack = new LinkedList<>(); private Deque<Command> redoStack = new LinkedList<>();
public Wizard() {} public Wizard() {
}
public void castSpell(Command command, Target target) { public void castSpell(Command command, Target target) {
System.out.println(this + " casts " + command + " at " + target); System.out.println(this + " casts " + command + " at " + target);

View File

@ -0,0 +1,19 @@
package com.iluwatar.command;
import org.junit.Test;
import com.iluwatar.command.App;
/**
*
* Application test
*
*/
public class AppTest {
@Test
public void test() {
String[] args = {};
App.main(args);
}
}

View File

@ -1,71 +0,0 @@
package com.iluwatar.command;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* The Command pattern is a behavioral design pattern in which an object is used to encapsulate all
* information needed to perform an action or trigger an event at a later time. This information
* includes the method name, the object that owns the method and values for the method parameters.
*
* <p>Four terms always associated with the command pattern are command, receiver, invoker and
* client. A command object (spell) knows about the receiver (target) and invokes a method of
* the receiver.Values for parameters of the receiver method are stored in the command. The receiver
* then does the work. An invoker object (wizard) knows how to execute a command, and optionally
* does bookkeeping about the command execution. The invoker does not know anything about a
* concrete command, it knows only about command interface. Both an invoker object and several
* command objects are held by a client object (app). The client decides which commands to execute
* at which points. To execute a command, it passes the command object to the invoker object.
*/
public class CommandTest {
private static final String GOBLIN = "Goblin";
/**
* This test verifies that when the wizard casts spells on the goblin. The wizard keeps track of
* the previous spells cast, so it is easy to undo them. In addition, it also verifies that the
* wizard keeps track of the spells undone, so they can be redone.
*/
@Test
public void testCommand() {
Wizard wizard = new Wizard();
Goblin goblin = new Goblin();
wizard.castSpell(new ShrinkSpell(), goblin);
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE);
wizard.castSpell(new InvisibilitySpell(), goblin);
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE);
wizard.undoLastSpell();
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE);
wizard.undoLastSpell();
verifyGoblin(goblin, GOBLIN, Size.NORMAL, Visibility.VISIBLE);
wizard.redoLastSpell();
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE);
wizard.redoLastSpell();
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE);
}
/**
* This method asserts that the passed goblin object has the name as expectedName, size as
* expectedSize and visibility as expectedVisibility.
*
* @param goblin a goblin object whose state is to be verified against other parameters
* @param expectedName expectedName of the goblin
* @param expectedSize expected size of the goblin
* @param expectedVisibilty exepcted visibility of the goblin
*/
private void verifyGoblin(Goblin goblin, String expectedName, Size expectedSize,
Visibility expectedVisibilty) {
assertEquals("Goblin's name must be same as expectedName", expectedName, goblin.toString());
assertEquals("Goblin's size must be same as expectedSize", expectedSize, goblin.getSize());
assertEquals("Goblin's visibility must be same as expectedVisibility", expectedVisibilty,
goblin.getVisibility());
}
}

View File

@ -4,9 +4,7 @@ title: Composite
folder: composite folder: composite
permalink: /patterns/composite/ permalink: /patterns/composite/
categories: Structural categories: Structural
tags: tags: Java
- Java
- Gang Of Four
--- ---
**Intent:** Compose objects into tree structures to represent part-whole **Intent:** Compose objects into tree structures to represent part-whole
@ -24,7 +22,3 @@ of objects uniformly.
* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html) * [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html)
* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) * [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java)
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>composite</artifactId> <artifactId>composite</artifactId>
<dependencies> <dependencies>

View File

@ -1,21 +1,16 @@
package com.iluwatar.composite; package com.iluwatar.composite;
/** /**
* The Composite pattern is a partitioning design pattern. The Composite pattern describes that a *
* group of objects is to be treated in the same way as a single instance of an object. The intent * With Composite we can treat tree hierarchies of objects with uniform
* of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. * interface ({@link LetterComposite}). In this example we have sentences composed of
* Implementing the Composite pattern lets clients treat individual objects and compositions * words composed of letters.
* uniformly.
* <p>
* In this example we have sentences composed of words composed of letters. All of the objects can
* be treated through the same interface ({@link LetterComposite}).
* *
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point
*
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -22,4 +22,5 @@ public class Letter extends LetterComposite {
protected void printThisAfter() { protected void printThisAfter() {
// nop // nop
} }
} }

View File

@ -15,19 +15,20 @@ public class Messenger {
List<Word> words = new ArrayList<Word>(); List<Word> words = new ArrayList<Word>();
words.add(new Word(Arrays.asList(new Letter('W'), new Letter('h'), new Letter('e'), new Letter( words.add(new Word(Arrays.asList(new Letter('W'), new Letter('h'),
'r'), new Letter('e')))); new Letter('e'), new Letter('r'), new Letter('e'))));
words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter( words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'),
'r'), new Letter('e')))); new Letter('e'), new Letter('r'), new Letter('e'))));
words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s')))); words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s'))));
words.add(new Word(Arrays.asList(new Letter('a')))); words.add(new Word(Arrays.asList(new Letter('a'))));
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('h'), new Letter('i'), new Letter( words.add(new Word(Arrays.asList(new Letter('w'), new Letter('h'),
'p')))); new Letter('i'), new Letter('p'))));
words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter( words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'),
'r'), new Letter('e')))); new Letter('e'), new Letter('r'), new Letter('e'))));
words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s')))); words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s'))));
words.add(new Word(Arrays.asList(new Letter('a')))); words.add(new Word(Arrays.asList(new Letter('a'))));
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('a'), new Letter('y')))); words.add(new Word(Arrays.asList(new Letter('w'), new Letter('a'),
new Letter('y'))));
return new Sentence(words); return new Sentence(words);
@ -37,18 +38,18 @@ public class Messenger {
List<Word> words = new ArrayList<Word>(); List<Word> words = new ArrayList<Word>();
words.add(new Word(Arrays.asList(new Letter('M'), new Letter('u'), new Letter('c'), new Letter( words.add(new Word(Arrays.asList(new Letter('M'), new Letter('u'),
'h')))); new Letter('c'), new Letter('h'))));
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('i'), new Letter('n'), new Letter( words.add(new Word(Arrays.asList(new Letter('w'), new Letter('i'),
'd')))); new Letter('n'), new Letter('d'))));
words.add(new Word(Arrays.asList(new Letter('p'), new Letter('o'), new Letter('u'), new Letter( words.add(new Word(Arrays.asList(new Letter('p'), new Letter('o'),
'r'), new Letter('s')))); new Letter('u'), new Letter('r'), new Letter('s'))));
words.add(new Word(Arrays.asList(new Letter('f'), new Letter('r'), new Letter('o'), new Letter( words.add(new Word(Arrays.asList(new Letter('f'), new Letter('r'),
'm')))); new Letter('o'), new Letter('m'))));
words.add(new Word(Arrays.asList(new Letter('y'), new Letter('o'), new Letter('u'), new Letter( words.add(new Word(Arrays.asList(new Letter('y'), new Letter('o'),
'r')))); new Letter('u'), new Letter('r'))));
words.add(new Word(Arrays.asList(new Letter('m'), new Letter('o'), new Letter('u'), new Letter( words.add(new Word(Arrays.asList(new Letter('m'), new Letter('o'),
't'), new Letter('h')))); new Letter('u'), new Letter('t'), new Letter('h'))));
return new Sentence(words); return new Sentence(words);

View File

@ -24,4 +24,5 @@ public class Sentence extends LetterComposite {
protected void printThisAfter() { protected void printThisAfter() {
System.out.print("."); System.out.print(".");
} }
} }

View File

@ -24,4 +24,5 @@ public class Word extends LetterComposite {
protected void printThisAfter() { protected void printThisAfter() {
// nop // nop
} }
} }

View File

@ -4,9 +4,7 @@ title: Data Access Object
folder: dao folder: dao
permalink: /patterns/dao/ permalink: /patterns/dao/
categories: Architectural categories: Architectural
tags: tags: Java
- 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
@ -18,7 +16,3 @@ other persistence mechanism.
* when you want to consolidate how the data layer is accessed * when you want to consolidate how the data layer is accessed
* when you want to avoid writing multiple data retrieval/persistence layers * when you want to avoid writing multiple data retrieval/persistence layers
**Credits:**
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)

View File

@ -6,64 +6,14 @@
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>dao</artifactId> <artifactId>dao</artifactId>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies> </dependencies>
<build>
<!--
log4j.xml file will be copied both in ${project.build.outputDirectory}
and ${project.build.directory}. Thanks to Sean Patrick Floyd
(http://stackoverflow.com/questions/5637532/maven-how-to-place-resource-file-together-with-jar)
-->
<resources>
<resource> <!-- regular processing for every resource file -->
<directory>src/main/resources</directory>
</resource>
<resource> <!-- processing with a different output directory for log4j.xml -->
<directory>src/main/resources</directory>
<includes>
<include>log4j.xml</include>
</includes>
<targetPath>..</targetPath> <!-- relative to target/classes i.e. ${project.build.directory} -->
</resource>
</resources>
<plugins>
<!--
This will exclude log4j.xml file from generated JAR
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<excludes>
<exclude>log4j.xml</exclude>
</excludes>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View File

@ -3,56 +3,51 @@ package com.iluwatar.dao;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.log4j.Logger;
/** /**
* *
* Data Access Object (DAO) is an object that provides an abstract interface to some type of * With the DAO pattern, we can use various method calls to retrieve/add/delete/update data without directly
* database or other persistence mechanism. By mapping application calls to the persistence layer, * interacting with the data. The below example demonstrates basic operations(CRUD): select, add, update, and delete.
* DAO provide some specific data operations without exposing details of the database. This
* isolation supports the Single responsibility principle. It separates what data accesses the
* application needs, in terms of domain-specific objects and data types (the public interface of
* the DAO), from how these needs can be satisfied with a specific DBMS.
* <p>
* With the DAO pattern, we can use various method calls to retrieve/add/delete/update data without
* directly interacting with the data. The below example demonstrates basic CRUD operations: select,
* add, update, and delete.
* *
*/ */
public class App { public class App {
private static Logger LOGGER = Logger.getLogger(App.class);
/** /**
* Program entry point. * Program entry point
* * @param args command line args
* @param args command line args.
*/ */
public static void main(final String[] args) { public static void main(String[] args) {
final CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers());
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers());
LOGGER.info("customerDao.getCusterById(2): " + customerDao.getCustomerById(2));
final Customer customer = new Customer(4, "Dan", "Danson"); System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
System.out.println("customerDao.getCusterById(2): " + customerDao.getCusterById(2));
Customer customer = new Customer(4, "Dan", "Danson");
customerDao.addCustomer(customer); customerDao.addCustomer(customer);
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
customer.setFirstName("Daniel"); customer.setFirstName("Daniel");
customer.setLastName("Danielson"); customer.setLastName("Danielson");
customerDao.updateCustomer(customer); customerDao.updateCustomer(customer);
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
customerDao.deleteCustomer(customer); customerDao.deleteCustomer(customer);
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
} }
/** /**
* Generate customers. * Generate customers
* * @return list of customers
* @return list of customers.
*/ */
public static List<Customer> generateSampleCustomers() { public static List<Customer> generateSampleCustomers() {
final Customer customer1 = new Customer(1, "Adam", "Adamson"); Customer customer1 = new Customer(1, "Adam", "Adamson");
final Customer customer2 = new Customer(2, "Bob", "Bobson"); Customer customer2 = new Customer(2, "Bob", "Bobson");
final Customer customer3 = new Customer(3, "Carl", "Carlson"); Customer customer3 = new Customer(3, "Carl", "Carlson");
final List<Customer> customers = new ArrayList<Customer>();
List<Customer> customers = new ArrayList<Customer>();
customers.add(customer1); customers.add(customer1);
customers.add(customer2); customers.add(customer2);
customers.add(customer3); customers.add(customer3);

View File

@ -11,7 +11,7 @@ public class Customer {
private String firstName; private String firstName;
private String lastName; private String lastName;
public Customer(final int id, final String firstName, final String lastName) { public Customer(int id, String firstName, String lastName) {
this.id = id; this.id = id;
this.firstName = firstName; this.firstName = firstName;
this.lastName = lastName; this.lastName = lastName;
@ -21,7 +21,7 @@ public class Customer {
return id; return id;
} }
public void setId(final int id) { public void setId(int id) {
this.id = id; this.id = id;
} }
@ -29,7 +29,7 @@ public class Customer {
return firstName; return firstName;
} }
public void setFirstName(final String firstName) { public void setFirstName(String firstName) {
this.firstName = firstName; this.firstName = firstName;
} }
@ -37,32 +37,34 @@ public class Customer {
return lastName; return lastName;
} }
public void setLastName(final String lastName) { public void setLastName(String lastName) {
this.lastName = lastName; this.lastName = lastName;
} }
@Override @Override
public String toString() { public String toString() {
return "Customer{" + "id=" + getId() + ", firstName='" + getFirstName() + '\'' + ", lastName='" return "Customer{" +
+ getLastName() + '\'' + '}'; "id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
} }
@Override @Override
public boolean equals(final Object o) { public boolean equals(Object o) {
boolean isEqual = false; if (this == o) return true;
if (this == o) { if (o == null || getClass() != o.getClass()) return false;
isEqual = true;
} else if (o != null && (getClass() == o.getClass())) { Customer customer = (Customer) o;
final Customer customer = (Customer) o;
if (getId() == customer.getId()) if (id != customer.id) return false;
isEqual = true;
} return true;
return isEqual;
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = getId(); int result = id;
return result; return result;
} }
} }

View File

@ -9,13 +9,9 @@ import java.util.List;
*/ */
public interface CustomerDao { public interface CustomerDao {
List<Customer> getAllCustomers(); public List<Customer> getAllCustomers();
public Customer getCusterById(int id);
Customer getCustomerById(int id); public void addCustomer(Customer customer);
public void updateCustomer(Customer customer);
void addCustomer(Customer customer); public void deleteCustomer(Customer customer);
void updateCustomer(Customer customer);
void deleteCustomer(Customer customer);
} }

View File

@ -4,12 +4,11 @@ import java.util.List;
/** /**
* *
* The data access object (DAO) is an object that provides an abstract interface to some type of * The data access object (DAO) is an object that provides an abstract interface to some type of database or other persistence mechanism.
* database or other persistence mechanism. By mapping application calls to the persistence layer, * By mapping application calls to the persistence layer, DAO provide some specific data operations without exposing details of the database.
* DAO provide some specific data operations without exposing details of the database. This * This isolation supports the Single responsibility principle. It separates what data accesses the application needs, in terms of
* isolation supports the Single responsibility principle. It separates what data accesses the * domain-specific objects and data types (the public interface of the DAO), from how these needs can be satisfied with a specific DBMS,
* application needs, in terms of domain-specific objects and data types (the public interface of * database schema, etc.
* the DAO), from how these needs can be satisfied with a specific DBMS, database schema, etc.
* *
*/ */
public class CustomerDaoImpl implements CustomerDao { public class CustomerDaoImpl implements CustomerDao {
@ -18,7 +17,7 @@ public class CustomerDaoImpl implements CustomerDao {
// Note: Normally this would be in the form of an actual database and not part of the Dao Impl. // Note: Normally this would be in the form of an actual database and not part of the Dao Impl.
private List<Customer> customers; private List<Customer> customers;
public CustomerDaoImpl(final List<Customer> customers) { public CustomerDaoImpl(List<Customer> customers) {
this.customers = customers; this.customers = customers;
} }
@ -28,35 +27,31 @@ public class CustomerDaoImpl implements CustomerDao {
} }
@Override @Override
public Customer getCustomerById(final int id) { public Customer getCusterById(int id) {
Customer customer = null; for (int i = 0; i < customers.size(); i++) {
for (final Customer cus : getAllCustomers()) { if (customers.get(i).getId() == id) {
if (cus.getId() == id) { return customers.get(i);
customer = cus;
break;
} }
} }
return customer; // No customer found
return null;
} }
@Override @Override
public void addCustomer(final Customer customer) { public void addCustomer(Customer customer) {
if (getCustomerById(customer.getId()) == null) {
customers.add(customer); customers.add(customer);
} }
}
@Override @Override
public void updateCustomer(final Customer customer) { public void updateCustomer(Customer customer) {
if (getAllCustomers().contains(customer)) { if (customers.contains(customer)) {
final int index = getAllCustomers().indexOf(customer); customers.set(customers.indexOf(customer), customer);
getAllCustomers().set(index, customer);
} }
} }
@Override @Override
public void deleteCustomer(final Customer customer) { public void deleteCustomer(Customer customer) {
getAllCustomers().remove(customer); customers.remove(customer);
} }
} }

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true"
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>
<root>
<level value="INFO" />
<appender-ref ref="console" />
</root>
</log4j:configuration>

View File

@ -0,0 +1,19 @@
package com.iluwatar.dao;
import org.junit.Test;
import com.iluwatar.dao.App;
/**
*
* Application test
*
*/
public class AppTest {
@Test
public void test() {
String[] args = {};
App.main(args);
}
}

View File

@ -1,99 +0,0 @@
package com.iluwatar.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
public class CustomerDaoImplTest {
private CustomerDaoImpl impl;
private List<Customer> customers;
private static final Customer CUSTOMER = new Customer(1, "Freddy", "Krueger");
@Before
public void setUp() {
customers = new ArrayList<Customer>();
customers.add(CUSTOMER);
impl = new CustomerDaoImpl(customers);
}
@Test
public void deleteExistingCustomer() {
assertEquals(1, impl.getAllCustomers().size());
impl.deleteCustomer(CUSTOMER);
assertTrue(impl.getAllCustomers().isEmpty());
}
@Test
public void deleteNonExistingCustomer() {
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
impl.deleteCustomer(nonExistingCustomer);
assertEquals(1, impl.getAllCustomers().size());
}
@Test
public void updateExistingCustomer() {
final String newFirstname = "Bernard";
final String newLastname = "Montgomery";
final Customer customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname);
impl.updateCustomer(customer);
final Customer cust = impl.getCustomerById(CUSTOMER.getId());
assertEquals(newFirstname, cust.getFirstName());
assertEquals(newLastname, cust.getLastName());
}
@Test
public void updateNonExistingCustomer() {
final int nonExistingId = getNonExistingCustomerId();
final String newFirstname = "Douglas";
final String newLastname = "MacArthur";
final Customer customer = new Customer(nonExistingId, newFirstname, newLastname);
impl.updateCustomer(customer);
assertNull(impl.getCustomerById(nonExistingId));
final Customer existingCustomer = impl.getCustomerById(CUSTOMER.getId());
assertEquals(CUSTOMER.getFirstName(), existingCustomer.getFirstName());
assertEquals(CUSTOMER.getLastName(), existingCustomer.getLastName());
}
@Test
public void addCustomer() {
final Customer newCustomer = new Customer(3, "George", "Patton");
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
}
@Test
public void addAlreadyAddedCustomer() {
final Customer newCustomer = new Customer(3, "George", "Patton");
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
}
@Test
public void getExistinCustomerById() {
assertEquals(CUSTOMER, impl.getCustomerById(CUSTOMER.getId()));
}
@Test
public void getNonExistinCustomerById() {
final int nonExistingId = getNonExistingCustomerId();
assertNull(impl.getCustomerById(nonExistingId));
}
/**
* An arbitrary number which does not correspond to an active Customer id.
*
* @return an int of a customer id which doesn't exist
*/
private int getNonExistingCustomerId() {
return 999;
}
}

View File

@ -1,74 +0,0 @@
package com.iluwatar.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import org.junit.Before;
import org.junit.Test;
public class CustomerTest {
private Customer customer;
private static final int ID = 1;
private static final String FIRSTNAME = "Winston";
private static final String LASTNAME = "Churchill";
@Before
public void setUp() {
customer = new Customer(ID, FIRSTNAME, LASTNAME);
}
@Test
public void getAndSetId() {
final int newId = 2;
customer.setId(newId);
assertEquals(newId, customer.getId());
}
@Test
public void getAndSetFirstname() {
final String newFirstname = "Bill";
customer.setFirstName(newFirstname);
assertEquals(newFirstname, customer.getFirstName());
}
@Test
public void getAndSetLastname() {
final String newLastname = "Clinton";
customer.setLastName(newLastname);
assertEquals(newLastname, customer.getLastName());
}
@Test
public void notEqualWithDifferentId() {
final int newId = 2;
final Customer otherCustomer = new Customer(newId, FIRSTNAME, LASTNAME);
assertNotEquals(customer, otherCustomer);
assertNotEquals(customer.hashCode(), otherCustomer.hashCode());
}
@Test
public void equalsWithSameObjectValues() {
final Customer otherCustomer = new Customer(ID, FIRSTNAME, LASTNAME);
assertEquals(customer, otherCustomer);
assertEquals(customer.hashCode(), otherCustomer.hashCode());
}
@Test
public void equalsWithSameObjects() {
assertEquals(customer, customer);
assertEquals(customer.hashCode(), customer.hashCode());
}
@Test
public void testToString() {
final StringBuffer buffer = new StringBuffer();
buffer.append("Customer{id=");
buffer.append("" + customer.getId());
buffer.append(", firstName='");
buffer.append(customer.getFirstName());
buffer.append("\', lastName='");
buffer.append(customer.getLastName() + "\'}");
assertEquals(buffer.toString(), customer.toString());
}
}

View File

@ -4,13 +4,9 @@ title: Decorator
folder: decorator folder: decorator
permalink: /patterns/decorator/ permalink: /patterns/decorator/
categories: Structural categories: Structural
tags: tags: Java
- Java
- Gang Of Four
--- ---
**Also known as:** Wrapper
**Intent:** Attach additional responsibilities to an object dynamically. **Intent:** Attach additional responsibilities to an object dynamically.
Decorators provide a flexible alternative to subclassing for extending Decorators provide a flexible alternative to subclassing for extending
functionality. functionality.
@ -22,7 +18,3 @@ functionality.
* to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects * to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects
* for responsibilities that can be withdrawn * for responsibilities that can be withdrawn
* when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing * when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing
**Credits**
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>decorator</artifactId> <artifactId>decorator</artifactId>
<dependencies> <dependencies>

View File

@ -2,21 +2,18 @@ package com.iluwatar.decorator;
/** /**
* *
* The Decorator pattern is a more flexible alternative to subclassing. The Decorator class * Decorator pattern is a more flexible alternative to subclassing. The decorator
* implements the same interface as the target and uses composition to "decorate" calls to the * class implements the same interface as the target and uses composition to
* target. Using the Decorator pattern it is possible to change the behavior of the class during * "decorate" calls to the target.
* runtime.
* <p> * <p>
* In this example we show how the simple {@link Troll} first attacks and then flees the battle. * Using decorator pattern it is possible to change class behavior during
* Then we decorate the {@link Troll} with a {@link SmartTroll} and perform the attack again. You * runtime, as the example shows.
* can see how the behavior changes after the decoration.
* *
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point
*
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {
@ -26,13 +23,11 @@ public class App {
Hostile troll = new Troll(); Hostile troll = new Troll();
troll.attack(); troll.attack();
troll.fleeBattle(); troll.fleeBattle();
System.out.printf("Simple troll power %d.\n", troll.getAttackPower());
// change the behavior of the simple troll by adding a decorator // change the behavior of the simple troll by adding a decorator
System.out.println("\nA smart looking troll surprises you."); System.out.println("\nA smart looking troll surprises you.");
Hostile smart = new SmartTroll(troll); Hostile smart = new SmartTroll(troll);
smart.attack(); smart.attack();
smart.fleeBattle(); smart.fleeBattle();
System.out.printf("Smart troll power %d.\n", smart.getAttackPower());
} }
} }

View File

@ -9,8 +9,6 @@ public interface Hostile {
void attack(); void attack();
int getAttackPower();
void fleeBattle(); void fleeBattle();
} }

View File

@ -1,9 +1,10 @@
package com.iluwatar.decorator; package com.iluwatar.decorator;
/** /**
* SmartTroll is a decorator for {@link Hostile} objects. The calls to the {@link Hostile} interface * SmartTroll is a decorator for {@link Hostile} objects.
* are intercepted and decorated. Finally the calls are delegated to the decorated {@link Hostile} * The calls to the {@link Hostile} interface are intercepted
* object. * and decorated. Finally the calls are delegated
* to the decorated {@link Hostile} object.
* *
*/ */
public class SmartTroll implements Hostile { public class SmartTroll implements Hostile {
@ -20,15 +21,10 @@ public class SmartTroll implements Hostile {
decorated.attack(); decorated.attack();
} }
@Override
public int getAttackPower() {
// decorated troll power + 20 because it is smart
return decorated.getAttackPower() + 20;
}
@Override @Override
public void fleeBattle() { public void fleeBattle() {
System.out.println("The troll calls for help!"); System.out.println("The troll calls for help!");
decorated.fleeBattle(); decorated.fleeBattle();
} }
} }

View File

@ -11,12 +11,8 @@ public class Troll implements Hostile {
System.out.println("The troll swings at you with a club!"); System.out.println("The troll swings at you with a club!");
} }
@Override
public int getAttackPower() {
return 10;
}
public void fleeBattle() { public void fleeBattle() {
System.out.println("The troll shrieks in horror and runs away!"); System.out.println("The troll shrieks in horror and runs away!");
} }
} }

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project
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"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>java-design-patterns</artifactId>
<version>1.8.0</version> <version>1.6.0</version>
</parent> </parent>
<artifactId>dependency-injection</artifactId> <artifactId>dependency-injection</artifactId>
<dependencies> <dependencies>

View File

@ -2,8 +2,9 @@ package com.iluwatar.dependency.injection;
/** /**
* *
* AdvancedWizard implements inversion of control. It depends on abstraction that can be injected * AdvancedWizard implements inversion of control.
* through its constructor. * It depends on abstraction that can be injected through
* its constructor.
* *
*/ */
public class AdvancedWizard implements Wizard { public class AdvancedWizard implements Wizard {

View File

@ -10,28 +10,27 @@ import com.google.inject.Injector;
* - High-level modules should not depend on low-level modules. Both should depend on abstractions. * - High-level modules should not depend on low-level modules. Both should depend on abstractions.
* - Abstractions should not depend on details. Details should depend on abstractions. * - Abstractions should not depend on details. Details should depend on abstractions.
* <p> * <p>
* In this example we show you three different wizards. The first one ({@link SimpleWizard}) is a * In this example we show you three different wizards. The first one ({@link SimpleWizard}) is a naive
* naive implementation violating the inversion of control principle. It depends directly on a * implementation violating the inversion of control principle. It depends directly on a concrete
* concrete implementation which cannot be changed. * implementation which cannot be changed.
* <p> * <p>
* The second wizard ({@link AdvancedWizard}) is more flexible. It does not depend on any concrete * The second wizard ({@link AdvancedWizard}) is more flexible. It does not depend on any concrete implementation
* implementation but abstraction. It utilizes Dependency Injection pattern allowing its * but abstraction. It utilizes Dependency Injection pattern allowing its {@link Tobacco} dependency to be
* {@link Tobacco} dependency to be injected through its constructor. This way, handling the * injected through its constructor. This way, handling the dependency is no longer the wizard's
* dependency is no longer the wizard's responsibility. It is resolved outside the wizard class. * responsibility. It is resolved outside the wizard class.
* <p> * <p>
* The third example takes the pattern a step further. It uses Guice framework for Dependency * The third example takes the pattern a step further. It uses Guice framework for Dependency Injection.
* Injection. {@link TobaccoModule} binds a concrete implementation to abstraction. Injector is then * {@link TobaccoModule} binds a concrete implementation to abstraction. Injector is then used to create
* used to create {@link GuiceWizard} object with correct dependencies. * {@link GuiceWizard} object with correct dependencies.
* *
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point
*
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main( String[] args ) {
SimpleWizard simpleWizard = new SimpleWizard(); SimpleWizard simpleWizard = new SimpleWizard();
simpleWizard.smoke(); simpleWizard.smoke();

View File

@ -4,8 +4,9 @@ import javax.inject.Inject;
/** /**
* *
* GuiceWizard implements inversion of control. Its dependencies are injected through its * GuiceWizard implements inversion of control.
* constructor by Guice framework. * Its dependencies are injected through its constructor
* by Guice framework.
* *
*/ */
public class GuiceWizard implements Wizard { public class GuiceWizard implements Wizard {

View File

@ -2,8 +2,8 @@ package com.iluwatar.dependency.injection;
/** /**
* *
* Naive Wizard implementation violating the inversion of control principle. It should depend on * Naive Wizard implementation violating the inversion of control principle.
* abstraction instead. * It should depend on abstraction instead.
* *
*/ */
public class SimpleWizard implements Wizard { public class SimpleWizard implements Wizard {

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