Compare commits

..

11 Commits
1.9.0 ... urm

845 changed files with 10463 additions and 23664 deletions

View File

@ -1,18 +1,20 @@
language: java
jdk:
- oraclejdk8
env:
global:
- 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:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
after_success:
- mvn clean test jacoco:report coveralls:report
- bash update-ghpages.sh
# Migration to container-based infrastructure
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

@ -1,4 +0,0 @@
This is great you have something to contribute!
Before going any further please read the [wiki](https://github.com/iluwatar/java-design-patterns/wiki)
with conventions and rules we used for this project.

View File

@ -2,10 +2,11 @@
that smart and dearly wants an empty line before a heading to be able to
display it as such, e.g. website) -->
# Design patterns implemented in Java
# Design pattern samples in Java
[![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master)](https://travis-ci.org/iluwatar/java-design-patterns)
[![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5634/badge.svg)](https://scan.coverity.com/projects/5634)
[![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
# Introduction
@ -25,13 +26,7 @@ are familiar with the patterns.
Before you dive into the material, you should be familiar with various
[Programming/Software Design Principles](http://webpro.github.io/programming-principles/).
All designs should be as simple as possible. You should start with KISS, YAGNI,
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
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 pattern categories, `Creational`, `Behavioral` and others.
@ -39,10 +34,11 @@ patterns by any of the following approaches
# How to contribute
If you are willing to contribute to the project you will find the relevant information in our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki). We will help you and answer your questions in the [Gitter chatroom](https://gitter.im/iluwatar/java-design-patterns).
If you are willing to contribute to the project you will find the relevant information in our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki).
# 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)
* [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)
@ -52,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)
* [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)
* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697)
# License

View File

@ -4,14 +4,9 @@ title: Abstract Factory
folder: abstract-factory
permalink: /patterns/abstract-factory/
categories: Creational
tags:
- Java
- Gang Of Four
- Difficulty-Intermediate
tags: Java
---
**Also known as:** Kit
**Intent:** Provide an interface for creating families of related or dependent
objects without specifying their concrete classes.
@ -27,7 +22,3 @@ objects without specifying their concrete classes.
**Real world examples:**
* [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"?>
<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">
<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.9.0</version>
<version>1.6.0</version>
</parent>
<artifactId>abstract-factory</artifactId>
<dependencies>

View File

@ -1,18 +1,14 @@
package com.iluwatar.abstractfactory;
/**
*
* The Abstract Factory pattern provides a way to encapsulate a group of individual factories that have a common theme
* without specifying their concrete classes. In normal usage, the client software creates a concrete implementation of
* the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part
* of the theme. The client does not know (or care) which concrete objects it gets from each of these internal
* factories, since it uses only the generic interfaces of their products. This pattern separates the details of
* implementation of a set of objects from their general usage and relies on object composition, as object creation is
* implemented in methods exposed in the factory interface.
* The essence of the Abstract Factory pattern is a factory interface
* ({@link KingdomFactory}) and its implementations ({@link ElfKingdomFactory},
* {@link OrcKingdomFactory}).
* <p>
* The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) and its implementations (
* {@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses both concrete implementations to create a
* king, a castle and an army.
* The example uses both concrete implementations to create a king, a castle and
* an army.
*
*/
public class App {
@ -23,6 +19,7 @@ public class App {
/**
* Creates kingdom
* @param factory
*/
public void createKingdom(final KingdomFactory factory) {
setKing(factory.createKing());
@ -42,6 +39,14 @@ public class App {
return factory.createKing();
}
Castle getCastle(final KingdomFactory factory) {
return factory.createCastle();
}
Army getArmy(final KingdomFactory factory) {
return factory.createArmy();
}
public King getKing() {
return king;
}
@ -50,10 +55,6 @@ public class App {
this.king = king;
}
Castle getCastle(final KingdomFactory factory) {
return factory.createCastle();
}
public Castle getCastle() {
return castle;
}
@ -62,10 +63,6 @@ public class App {
this.castle = castle;
}
Army getArmy(final KingdomFactory factory) {
return factory.createArmy();
}
public Army getArmy() {
return army;
}
@ -73,33 +70,4 @@ public class App {
private void setArmy(final 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

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,61 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.9" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
associations="true" dependencies="false" nesting-relationships="true" router="FAN">
<class id="1" language="java" name="com.iluwatar.adapter.FishingBoat" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="656" y="355"/>
<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="com.iluwatar.adapter.GnomeEngineeringManager" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java" binary="false" corner="BOTTOM_RIGHT">
<position height="106" width="224" x="110" y="210"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.adapter.Captain" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/Captain.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="228" y="185"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.adapter.BattleFishingBoat" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="463" y="357"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="4" language="java" name="com.iluwatar.adapter.BattleShip" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/BattleShip.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="466" y="170"/>
<interface id="2" language="java" name="com.iluwatar.adapter.Engineer" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/Engineer.java" binary="false" corner="BOTTOM_RIGHT">
<position height="88" width="141" x="110" y="356"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="3" language="java" name="com.iluwatar.adapter.GnomeEngineer" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java" binary="false" corner="BOTTOM_RIGHT">
<position height="106" width="141" x="374" y="210"/>
<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="4" language="java" name="com.iluwatar.adapter.GoblinGlider" project="adapter"
file="/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java" binary="false" corner="BOTTOM_RIGHT">
<position height="142" width="130" x="374" y="356"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<realization id="5">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="4"/>
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="2"/>
</realization>
<association id="6">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="7" name="boat"/>
<multiplicity id="8" minimum="0" maximum="1"/>
<realization id="6">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="2"/>
</realization>
<association id="7">
<end type="SOURCE" refId="1" navigable="false">
<attribute id="8" name="engineer"/>
<multiplicity id="9" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<end type="TARGET" refId="2" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="9">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="4"/>
</realization>
<association id="10">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="11" name="battleship"/>
<end type="SOURCE" refId="3" navigable="false">
<attribute id="11" name="glider"/>
<multiplicity id="12" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="4" navigable="true"/>

BIN
adapter/etc/adapter_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -4,19 +4,14 @@ title: Adapter
folder: adapter
permalink: /patterns/adapter/
categories: Structural
tags:
- Java
- Gang Of Four
- Difficulty-Beginner
tags: Java
---
**Also known as:** Wrapper
**Intent:** Convert the interface of a class into another interface the clients
expect. Adapter lets classes work together that couldn't otherwise because of
incompatible interfaces.
![alt text](./etc/adapter.png "Adapter")
![alt text](./etc/adapter_1.png "Adapter")
**Applicability:** Use the Adapter pattern when
@ -27,7 +22,3 @@ incompatible interfaces.
**Real world examples:**
* [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"?>
<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">
<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.9.0</version>
<version>1.6.0</version>
</parent>
<artifactId>adapter</artifactId>
<dependencies>
@ -14,10 +15,5 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,38 +1,25 @@
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.
*
* 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>
* 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 BattleFishingBoat}) converts the interface of the adaptee class (
* {@link FishingBoat}) into a suitable one expected by the client ( {@link BattleShip} ).
*
* <p>
* The story of this implementation is this. <br>
* Pirates are coming! we need a {@link BattleShip} to fight! We have a {@link FishingBoat} and our
* captain. We have no time to make up a new ship! we need to reuse this {@link FishingBoat}. The
* captain needs a battleship which can fire and move. The spec is in {@link BattleShip}. We will
* use the Adapter pattern to reuse {@link FishingBoat}.
* 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 {
/**
* Program entry point.
*
* Program entry point
* @param args command line args
*/
public static void main(String[] args) {
Captain captain = new Captain(new BattleFishingBoat());
captain.move();
captain.fire();
Engineer manager = new GnomeEngineeringManager();
manager.operateDevice();
}
}

View File

@ -1,29 +0,0 @@
package com.iluwatar.adapter;
/**
*
* Adapter class. Adapts the interface of the device ({@link FishingBoat}) into {@link BattleShip}
* interface expected by the client ({@link Captain}). <br>
* In this case we added a new function fire to suit the interface. We are reusing the
* {@link FishingBoat} without changing itself. The Adapter class can just map the functions of the
* Adaptee or add, delete features of the Adaptee.
*
*/
public class BattleFishingBoat implements BattleShip {
private FishingBoat boat;
public BattleFishingBoat() {
boat = new FishingBoat();
}
@Override
public void fire() {
System.out.println("fire!");
}
@Override
public void move() {
boat.sail();
}
}

View File

@ -1,14 +0,0 @@
package com.iluwatar.adapter;
/**
* The interface expected by the client.<br>
* A Battleship can fire and move.
*
*/
public interface BattleShip {
void fire();
void move();
}

View File

@ -1,33 +0,0 @@
package com.iluwatar.adapter;
/**
* The Captain uses {@link BattleShip} to fight. <br>
* This is the client in the pattern.
*/
public class Captain implements BattleShip {
private BattleShip battleship;
public Captain() {
}
public Captain(BattleShip battleship) {
this.battleship = battleship;
}
public void setBattleship(BattleShip battleship) {
this.battleship = battleship;
}
@Override
public void fire() {
battleship.fire();
}
@Override
public void move() {
battleship.move();
}
}

View File

@ -0,0 +1,12 @@
package com.iluwatar.adapter;
/**
*
* Engineers can operate devices.
*
*/
public interface Engineer {
void operateDevice();
}

View File

@ -1,18 +0,0 @@
package com.iluwatar.adapter;
/**
*
* Device class (adaptee in the pattern). We want to reuse this class
*
*/
public class FishingBoat {
public void sail() {
System.out.println("The Boat is moving to that place");
}
public void fish() {
System.out.println("fishing ...");
}
}

View File

@ -0,0 +1,24 @@
package com.iluwatar.adapter;
/**
*
* Adapter class. Adapts the interface of the device ({@link GoblinGlider}) into
* {@link Engineer} interface expected by the client ({@link GnomeEngineeringManager}).
*
*/
public class GnomeEngineer implements Engineer {
private GoblinGlider glider;
public GnomeEngineer() {
glider = new GoblinGlider();
}
@Override
public void operateDevice() {
glider.attachGlider();
glider.gainSpeed();
glider.takeOff();
}
}

View File

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

View File

@ -0,0 +1,21 @@
package com.iluwatar.adapter;
/**
*
* Device class (adaptee in the pattern).
*
*/
public class GoblinGlider {
public void attachGlider() {
System.out.println("Glider attached.");
}
public void gainSpeed() {
System.out.println("Gaining speed.");
}
public void takeOff() {
System.out.println("Lift-off!");
}
}

View File

@ -1,61 +0,0 @@
package com.iluwatar.adapter;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
* Test class
*
*/
public class AdapterPatternTest {
private Map<String, Object> beans;
private static final String BATTLESHIP_BEAN = "engineer";
private static final String CAPTAIN_BEAN = "captain";
/**
* This method runs before the test execution and sets the bean objects in the beans Map.
*/
@Before
public void setup() {
beans = new HashMap<>();
BattleFishingBoat battleFishingBoat = spy(new BattleFishingBoat());
beans.put(BATTLESHIP_BEAN, battleFishingBoat);
Captain captain = new Captain();
captain.setBattleship((BattleFishingBoat) beans.get(BATTLESHIP_BEAN));
beans.put(CAPTAIN_BEAN, captain);
}
/**
* This test asserts that when we use the move() method on a captain bean(client), it is
* internally calling move method on the battleship object. The Adapter ({@link BattleFishingBoat}
* ) converts the interface of the target class ( {@link FishingBoat}) into a suitable one
* expected by the client ({@link Captain} ).
*/
@Test
public void testAdapter() {
BattleShip captain = (BattleShip) beans.get(CAPTAIN_BEAN);
// when captain moves
captain.move();
// the captain internally calls the battleship object to move
BattleShip battleship = (BattleShip) beans.get(BATTLESHIP_BEAN);
verify(battleship).move();
// same with above with firing
captain.fire();
verify(battleship).fire();
}
}

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

@ -4,10 +4,7 @@ title: Async Method Invocation
folder: async-method-invocation
permalink: /patterns/async-method-invocation/
categories: Concurrency
tags:
- Java
- Difficulty-Intermediate
- Functional
tags: Java
---
**Intent:** Asynchronous method invocation is pattern where the calling thread

View File

@ -1,11 +1,12 @@
<?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">
<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.9.0</version>
<version>1.6.0</version>
</parent>
<artifactId>async-method-invocation</artifactId>
<dependencies>
@ -14,10 +15,5 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -5,22 +5,22 @@ import java.util.concurrent.Callable;
/**
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated value,
* <code>AsyncCallback</code> which can be provided to be executed on task completion and <code>AsyncExecutor</code>
* that manages the execution of the async tasks.
* <code>AsyncCallback</code> which can be provided to be executed on task completion and
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
* <p>
* The main method shows example flow of async invocations. The main thread starts multiple tasks with variable
* durations and then continues its own work. When the main thread has done it's job it collects the results of the
* async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are executed immediately when the
* tasks complete.
* The main method shows example flow of async invocations. The main thread starts multiple tasks with
* variable durations and then continues its own work. When the main thread has done it's job it collects
* the results of the async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are
* executed immediately when the tasks complete.
* <p>
* Noteworthy difference of thread usage between the async results and callbacks is that the async results are collected
* in the main thread but the callbacks are executed within the worker threads. This should be noted when working with
* thread pools.
* Noteworthy difference of thread usage between the async results and callbacks is that the async results
* are collected in the main thread but the callbacks are executed within the worker threads. This should be
* noted when working with thread pools.
* <p>
* Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture and
* ExecutorService are the real world implementations of this pattern. But due to the nature of parallel programming,
* the implementations are not trivial. This example does not take all possible scenarios into account but rather
* provides a simple version that helps to understand the pattern.
* Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture
* and ExecutorService are the real world implementations of this pattern. But due to the nature of parallel
* programming, the implementations are not trivial. This example does not take all possible scenarios into
* account but rather provides a simple version that helps to understand the pattern.
*
* @see AsyncResult
* @see AsyncCallback
@ -32,9 +32,6 @@ import java.util.concurrent.Callable;
*/
public class App {
/**
* Program entry point
*/
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
AsyncExecutor executor = new ThreadAsyncExecutor();
@ -66,10 +63,8 @@ public class App {
/**
* Creates a callable that lazily evaluates to given value with artificial delay.
*
* @param value
* value to evaluate
* @param delayMillis
* artificial delay in milliseconds
* @param value value to evaluate
* @param delayMillis artificial delay in milliseconds
* @return new callable for lazy evaluation
*/
private static <T> Callable<T> lazyval(T value, long delayMillis) {
@ -83,8 +78,7 @@ public class App {
/**
* Creates a simple callback that logs the complete status of the async result.
*
* @param name
* callback name
* @param name callback name
* @return new async callback
*/
private static <T> AsyncCallback<T> callback(String name) {

View File

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

View File

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

View File

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

View File

@ -44,8 +44,9 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
}
/**
* Simple implementation of async result that allows completing it successfully with a value or exceptionally with an
* exception. A really simplified version from its real life cousins FutureTask and CompletableFuture.
* Simple implementation of async result that allows completing it successfully with a value
* or exceptionally with an exception. A really simplified version from its real life cousins
* FutureTask and CompletableFuture.
*
* @see java.util.concurrent.FutureTask
* @see java.util.concurrent.CompletableFuture
@ -69,11 +70,10 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
}
/**
* Sets the value from successful execution and executes callback if available. Notifies any thread waiting for
* completion.
* Sets the value from successful execution and executes callback if available. Notifies
* any thread waiting for completion.
*
* @param value
* value of the evaluated task
* @param value value of the evaluated task
*/
void setValue(T value) {
this.value = value;
@ -85,11 +85,10 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
}
/**
* Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for
* completion.
* Sets the exception from failed execution and executes callback if available. Notifies
* any thread waiting for completion.
*
* @param exception
* exception of the failed task
* @param exception exception of the failed task
*/
void setException(Exception exception) {
this.exception = exception;
@ -102,7 +101,7 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
@Override
public boolean isCompleted() {
return state > RUNNING;
return (state > RUNNING);
}
@Override

View File

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

View File

@ -1,290 +0,0 @@
package com.iluwatar.async.method.invocation;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Matchers;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
* Date: 12/6/15 - 10:49 AM
*
* @author Jeroen Meulemeester
*/
public class ThreadAsyncExecutorTest {
/**
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)}
*/
@Test(timeout = 3000)
public void testSuccessfulTaskWithoutCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final Object result = new Object();
final Callable<Object> task = mock(Callable.class);
when(task.call()).thenReturn(result);
final AsyncResult<Object> asyncResult = executor.startProcess(task);
assertNotNull(asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
// Our task should only execute once ...
verify(task, times(1)).call();
// ... and the result should be exactly the same object
assertSame(result, asyncResult.getValue());
}
/**
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)}
*/
@Test(timeout = 3000)
public void testSuccessfulTaskWithCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final Object result = new Object();
final Callable<Object> task = mock(Callable.class);
when(task.call()).thenReturn(result);
final AsyncCallback callback = mock(AsyncCallback.class);
final AsyncResult<Object> asyncResult = executor.startProcess(task, callback);
assertNotNull(asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
// Our task should only execute once ...
verify(task, times(1)).call();
// ... same for the callback, we expect our object
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
verify(callback, times(1)).onComplete(eq(result), optionalCaptor.capture());
final Optional<Exception> optionalException = optionalCaptor.getValue();
assertNotNull(optionalException);
assertFalse(optionalException.isPresent());
// ... and the result should be exactly the same object
assertSame(result, asyncResult.getValue());
}
/**
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while
* to execute
*/
@Test(timeout = 5000)
public void testLongRunningTaskWithoutCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final Object result = new Object();
final Callable<Object> task = mock(Callable.class);
when(task.call()).thenAnswer(i -> {
Thread.sleep(1500);
return result;
});
final AsyncResult<Object> asyncResult = executor.startProcess(task);
assertNotNull(asyncResult);
assertFalse(asyncResult.isCompleted());
try {
asyncResult.getValue();
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
} catch (IllegalStateException e) {
assertNotNull(e.getMessage());
}
// Our task should only execute once, but it can take a while ...
verify(task, timeout(3000).times(1)).call();
// Prevent timing issues, and wait until the result is available
asyncResult.await();
assertTrue(asyncResult.isCompleted());
verifyNoMoreInteractions(task);
// ... and the result should be exactly the same object
assertSame(result, asyncResult.getValue());
}
/**
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when a task
* takes a while to execute
*/
@Test(timeout = 5000)
public void testLongRunningTaskWithCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final Object result = new Object();
final Callable<Object> task = mock(Callable.class);
when(task.call()).thenAnswer(i -> {
Thread.sleep(1500);
return result;
});
final AsyncCallback<Object> callback = mock(AsyncCallback.class);
final AsyncResult<Object> asyncResult = executor.startProcess(task, callback);
assertNotNull(asyncResult);
assertFalse(asyncResult.isCompleted());
verifyZeroInteractions(callback);
try {
asyncResult.getValue();
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
} catch (IllegalStateException e) {
assertNotNull(e.getMessage());
}
// Our task should only execute once, but it can take a while ...
verify(task, timeout(3000).times(1)).call();
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
verify(callback, timeout(3000).times(1)).onComplete(eq(result), optionalCaptor.capture());
final Optional<Exception> optionalException = optionalCaptor.getValue();
assertNotNull(optionalException);
assertFalse(optionalException.isPresent());
// Prevent timing issues, and wait until the result is available
asyncResult.await();
assertTrue(asyncResult.isCompleted());
verifyNoMoreInteractions(task, callback);
// ... and the result should be exactly the same object
assertSame(result, asyncResult.getValue());
}
/**
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while
* to execute, while waiting on the result using {@link ThreadAsyncExecutor#endProcess(AsyncResult)}
*/
@Test(timeout = 5000)
public void testEndProcess() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final Object result = new Object();
final Callable<Object> task = mock(Callable.class);
when(task.call()).thenAnswer(i -> {
Thread.sleep(1500);
return result;
});
final AsyncResult<Object> asyncResult = executor.startProcess(task);
assertNotNull(asyncResult);
assertFalse(asyncResult.isCompleted());
try {
asyncResult.getValue();
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
} catch (IllegalStateException e) {
assertNotNull(e.getMessage());
}
assertSame(result, executor.endProcess(asyncResult));
verify(task, times(1)).call();
assertTrue(asyncResult.isCompleted());
// Calling end process a second time while already finished should give the same result
assertSame(result, executor.endProcess(asyncResult));
verifyNoMoreInteractions(task);
}
/**
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when the callable is 'null'
*/
@Test(timeout = 3000)
public void testNullTask() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final AsyncResult<Object> asyncResult = executor.startProcess(null);
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
try {
asyncResult.getValue();
fail("Expected ExecutionException with NPE as cause");
} catch (final ExecutionException e) {
assertNotNull(e.getMessage());
assertNotNull(e.getCause());
assertEquals(NullPointerException.class, e.getCause().getClass());
}
}
/**
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when the
* callable is 'null', but the asynchronous callback is provided
*/
@Test(timeout = 3000)
public void testNullTaskWithCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final AsyncCallback<Object> callback = mock(AsyncCallback.class);
final AsyncResult<Object> asyncResult = executor.startProcess(null, callback);
assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
final ArgumentCaptor<Optional<Exception>> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class);
verify(callback, times(1)).onComplete(Matchers.isNull(), optionalCaptor.capture());
final Optional<Exception> optionalException = optionalCaptor.getValue();
assertNotNull(optionalException);
assertTrue(optionalException.isPresent());
final Exception exception = optionalException.get();
assertNotNull(exception);
assertEquals(NullPointerException.class, exception.getClass());
try {
asyncResult.getValue();
fail("Expected ExecutionException with NPE as cause");
} catch (final ExecutionException e) {
assertNotNull(e.getMessage());
assertNotNull(e.getCause());
assertEquals(NullPointerException.class, e.getCause().getClass());
}
}
/**
* Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when both
* the callable and the asynchronous callback are 'null'
*/
@Test(timeout = 3000)
public void testNullTaskWithNullCallback() throws Exception {
// Instantiate a new executor and start a new 'null' task ...
final ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
final AsyncResult<Object> asyncResult = executor.startProcess(null, null);
assertNotNull("The AsyncResult should not be 'null', even though the task and callback were 'null'.", asyncResult);
asyncResult.await(); // Prevent timing issues, and wait until the result is available
assertTrue(asyncResult.isCompleted());
try {
asyncResult.getValue();
fail("Expected ExecutionException with NPE as cause");
} catch (final ExecutionException e) {
assertNotNull(e.getMessage());
assertNotNull(e.getCause());
assertEquals(NullPointerException.class, e.getCause().getClass());
}
}
}

View File

@ -4,14 +4,9 @@ title: Bridge
folder: bridge
permalink: /patterns/bridge/
categories: Structural
tags:
- Java
- Gang Of Four
- Difficulty-Intermediate
tags: Java
---
**Also known as:** Handle/Body
**Intent:** Decouple an abstraction from its implementation so that the two can
vary independently.
@ -25,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.
* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies
* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
**Credits**
* [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"?>
<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">
<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.9.0</version>
<version>1.6.0</version>
</parent>
<artifactId>bridge</artifactId>
<dependencies>
@ -14,10 +15,5 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

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
* decouple an abstraction from its implementation so that the two can vary independently.
* <p>
* 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.
* 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 {
/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {
BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(new Excalibur());
BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(
new Excalibur());
blindingMagicWeapon.wield();
blindingMagicWeapon.blind();
blindingMagicWeapon.swing();
blindingMagicWeapon.unwield();
FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(new Mjollnir());
FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(
new Mjollnir());
flyingMagicWeapon.wield();
flyingMagicWeapon.fly();
flyingMagicWeapon.swing();
flyingMagicWeapon.unwield();
SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(new Stormbringer());
SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(
new Stormbringer());
soulEatingMagicWeapon.wield();
soulEatingMagicWeapon.swing();
soulEatingMagicWeapon.eatSoul();
soulEatingMagicWeapon.unwield();
}
}

View File

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

View File

@ -24,6 +24,8 @@ public class Excalibur extends BlindingMagicWeaponImpl {
@Override
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() {
return imp;
}
}

View File

@ -24,6 +24,8 @@ public class Mjollnir extends FlyingMagicWeaponImpl {
@Override
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() {
System.out.println("Stormbringer devours the enemy's soul");
}
}

View File

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

View File

@ -1,33 +0,0 @@
package com.iluwatar.bridge;
import org.junit.Test;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
* Date: 12/6/15 - 11:15 PM
*
* @author Jeroen Meulemeester
*/
public class BlindingMagicWeaponTest extends MagicWeaponTest {
/**
* Invoke all possible actions on the weapon and check if the actions are executed on the actual
* underlying weapon implementation.
*/
@Test
public void testExcalibur() throws Exception {
final Excalibur excalibur = spy(new Excalibur());
final BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(excalibur);
testBasicWeaponActions(blindingMagicWeapon, excalibur);
blindingMagicWeapon.blind();
verify(excalibur, times(1)).blindImp();
verifyNoMoreInteractions(excalibur);
}
}

View File

@ -1,33 +0,0 @@
package com.iluwatar.bridge;
import org.junit.Test;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
* Date: 12/6/15 - 11:26 PM
*
* @author Jeroen Meulemeester
*/
public class FlyingMagicWeaponTest extends MagicWeaponTest {
/**
* Invoke all possible actions on the weapon and check if the actions are executed on the actual
* underlying weapon implementation.
*/
@Test
public void testMjollnir() throws Exception {
final Mjollnir mjollnir = spy(new Mjollnir());
final FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(mjollnir);
testBasicWeaponActions(flyingMagicWeapon, mjollnir);
flyingMagicWeapon.fly();
verify(mjollnir, times(1)).flyImp();
verifyNoMoreInteractions(mjollnir);
}
}

View File

@ -1,42 +0,0 @@
package com.iluwatar.bridge;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
* Date: 12/6/15 - 11:28 PM
*
* @author Jeroen Meulemeester
*/
public abstract class MagicWeaponTest {
/**
* Invoke the basic actions of the given weapon, and test if the underlying weapon implementation
* is invoked
*
* @param weaponImpl The spied weapon implementation where actions are bridged to
* @param weapon The weapon, handled by the app
*/
protected final void testBasicWeaponActions(final MagicWeapon weapon,
final MagicWeaponImpl weaponImpl) {
assertNotNull(weapon);
assertNotNull(weaponImpl);
assertNotNull(weapon.getImp());
weapon.swing();
verify(weaponImpl, times(1)).swingImp();
verifyNoMoreInteractions(weaponImpl);
weapon.wield();
verify(weaponImpl, times(1)).wieldImp();
verifyNoMoreInteractions(weaponImpl);
weapon.unwield();
verify(weaponImpl, times(1)).unwieldImp();
verifyNoMoreInteractions(weaponImpl);
}
}

View File

@ -1,33 +0,0 @@
package com.iluwatar.bridge;
import org.junit.Test;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
/**
* Date: 12/6/15 - 11:43 PM
*
* @author Jeroen Meulemeester
*/
public class SoulEatingMagicWeaponTest extends MagicWeaponTest {
/**
* Invoke all possible actions on the weapon and check if the actions are executed on the actual
* underlying weapon implementation.
*/
@Test
public void testStormBringer() throws Exception {
final Stormbringer stormbringer = spy(new Stormbringer());
final SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(stormbringer);
testBasicWeaponActions(soulEatingMagicWeapon, stormbringer);
soulEatingMagicWeapon.eatSoul();
verify(stormbringer, times(1)).eatSoulImp();
verifyNoMoreInteractions(stormbringer);
}
}

View File

@ -4,10 +4,7 @@ title: Builder
folder: builder
permalink: /patterns/builder/
categories: Creational
tags:
- Java
- Gang Of Four
- Difficulty-Intermediate
tags: Java
---
**Intent:** Separate the construction of a complex object from its
@ -25,7 +22,3 @@ representations.
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
**Credits**
* [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"?>
<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">
<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.9.0</version>
<version>1.6.0</version>
</parent>
<artifactId>builder</artifactId>
<dependencies>

View File

@ -4,51 +4,38 @@ import com.iluwatar.builder.Hero.HeroBuilder;
/**
*
* The intention of the Builder pattern is to find a solution to the telescoping constructor
* anti-pattern. The telescoping constructor anti-pattern occurs when the increase of object
* 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.
* This is the Builder pattern variation as described by Joshua Bloch in
* Effective Java 2nd Edition.
* <p>
* The Builder pattern has another benefit. It can be used for objects that contain flat data (html
* code, SQL query, X.509 certificate...), that is to say, data that can't be easily edited. This
* type of data cannot be edited step by step and must be edited at once. The best way to construct
* such an object is to use a builder class.
* <p>
* In this example we have the Builder pattern variation as described by Joshua Bloch in Effective
* 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.
* 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 {
/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {
Hero mage =
new HeroBuilder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK)
.withWeapon(Weapon.DAGGER).build();
Hero mage = new HeroBuilder(Profession.MAGE, "Riobard")
.withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER)
.build();
System.out.println(mage);
Hero warrior =
new HeroBuilder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND)
.withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD)
.build();
Hero warrior = new HeroBuilder(Profession.WARRIOR, "Amberjill")
.withHairColor(HairColor.BLOND)
.withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL)
.withWeapon(Weapon.SWORD).build();
System.out.println(warrior);
Hero thief =
new HeroBuilder(Profession.THIEF, "Desmond").withHairType(HairType.BALD)
.withWeapon(Weapon.BOW).build();
Hero thief = new HeroBuilder(Profession.THIEF, "Desmond")
.withHairType(HairType.BALD).withWeapon(Weapon.BOW).build();
System.out.println(thief);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,56 +0,0 @@
package com.iluwatar.builder;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Date: 12/6/15 - 11:01 PM
*
* @author Jeroen Meulemeester
*/
public class HeroTest {
/**
* Test if we get the expected exception when trying to create a hero without a profession
*/
@Test(expected = IllegalArgumentException.class)
public void testMissingProfession() throws Exception {
new Hero.HeroBuilder(null, "Sir without a job");
}
/**
* Test if we get the expected exception when trying to create a hero without a name
*/
@Test(expected = IllegalArgumentException.class)
public void testMissingName() throws Exception {
new Hero.HeroBuilder(Profession.THIEF, null);
}
/**
* Test if the hero build by the builder has the correct attributes, as requested
*/
@Test
public void testBuildHero() throws Exception {
final String heroName = "Sir Lancelot";
final Hero hero = new Hero.HeroBuilder(Profession.WARRIOR, heroName)
.withArmor(Armor.CHAIN_MAIL)
.withWeapon(Weapon.SWORD)
.withHairType(HairType.LONG_CURLY)
.withHairColor(HairColor.BLOND)
.build();
assertNotNull(hero);
assertNotNull(hero.toString());
assertEquals(Profession.WARRIOR, hero.getProfession());
assertEquals(heroName, hero.getName());
assertEquals(Armor.CHAIN_MAIL, hero.getArmor());
assertEquals(Weapon.SWORD, hero.getWeapon());
assertEquals(HairType.LONG_CURLY, hero.getHairType());
assertEquals(HairColor.BLOND, hero.getHairColor());
}
}

View File

@ -4,9 +4,7 @@ title: Business Delegate
folder: business-delegate
permalink: /patterns/business-delegate/
categories: Business Tier
tags:
- Java
- Difficulty-Intermediate
tags: Java
---
**Intent:** The Business Delegate pattern adds an abstraction layer between

View File

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

View File

@ -1,34 +1,29 @@
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
* retrieved through service lookups. The Business Delegate itself may contain business logic too
* potentially tying together multiple service calls, exception handling, retrying etc.
* 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.
* <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 {
/**
* Program entry point.
*
* Program entry point
* @param args command line args
*/
public static void main(String[] args) {
BusinessDelegate businessDelegate = new BusinessDelegate();
BusinessLookup businessLookup = new BusinessLookup();
businessLookup.setEjbService(new EjbService());
businessLookup.setJmsService(new JmsService());
businessDelegate.setLookupService(businessLookup);
businessDelegate.setServiceType(ServiceType.EJB);
Client client = new Client(businessDelegate);

View File

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

View File

@ -1,31 +1,17 @@
package com.iluwatar.business.delegate;
/**
* Class for performing service lookups.
*
* Class for performing service lookups
*
*/
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) {
if (serviceType.equals(ServiceType.EJB)) {
return ejbService;
return new EjbService();
} 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,26 +0,0 @@
---
layout: pattern
title: Caching
folder: caching
permalink: /patterns/caching/
categories: Other
tags:
- Java
- Difficulty-Intermediate
- Performance
---
**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.9.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());
AppManager.find("001");
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());
AppManager.find("002");
System.out.println(AppManager.printCacheContent());
userAccount2 = AppManager.find("002");
userAccount2.setUserName("Jane G.");
AppManager.save(userAccount2);
System.out.println(AppManager.printCacheContent());
AppManager.find("002");
System.out.println(AppManager.printCacheContent());
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());
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());
AppManager.find("004");
System.out.println(AppManager.printCacheContent());
}
}

View File

@ -1,87 +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;
private AppManager() {
}
/**
*
* Developer/Tester is able to choose whether the application should use MongoDB as its underlying
* data storage or a simple Java data structure to (temporarily) store the data/objects during
* runtime.
*/
public static void initDb(boolean useMongoDb) {
if (useMongoDb) {
try {
DbManager.connect();
} catch (ParseException e) {
e.printStackTrace();
}
} else {
DbManager.createVirtualDb();
}
}
/**
* Initialize caching policy
*/
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);
}
/**
* Find user account
*/
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;
}
/**
* Save user account
*/
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,134 +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;
private CacheStore() {
}
/**
* Init cache capacity
*/
public static void initCapacity(int capacity) {
if (null == cache) {
cache = new LruCache(capacity);
} else {
cache.setCapacity(capacity);
}
}
/**
* Get user account using read-through cache
*/
public static UserAccount readThrough(String userId) {
if (cache.contains(userId)) {
System.out.println("# Cache Hit!");
return cache.get(userId);
}
System.out.println("# Cache Miss!");
UserAccount userAccount = DbManager.readFromDb(userId);
cache.set(userId, userAccount);
return userAccount;
}
/**
* Get user account using write-through cache
*/
public static void writeThrough(UserAccount userAccount) {
if (cache.contains(userAccount.getUserId())) {
DbManager.updateDb(userAccount);
} else {
DbManager.writeToDb(userAccount);
}
cache.set(userAccount.getUserId(), userAccount);
}
/**
* Get user account using write-around cache
*/
public static void writeAround(UserAccount userAccount) {
if (cache.contains(userAccount.getUserId())) {
DbManager.updateDb(userAccount);
cache.invalidate(userAccount.getUserId()); // Cache data has been updated -- remove older
// version from cache.
} else {
DbManager.writeToDb(userAccount);
}
}
/**
* Get user account using read-through cache with write-back policy
*/
public static UserAccount readThroughWithWriteBackPolicy(String userId) {
if (cache.contains(userId)) {
System.out.println("# Cache Hit!");
return cache.get(userId);
}
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;
}
/**
* Set user account
*/
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);
}
/**
* Clears cache
*/
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);
}
}
/**
* Print user accounts
*/
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,143 +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;
private DbManager() {
}
/**
* Create DB
*/
public static void createVirtualDb() {
useMongoDB = false;
virtualDB = new HashMap<String, UserAccount>();
}
/**
* Connect to DB
*/
public static void connect() throws ParseException {
useMongoDB = true;
mongoClient = new MongoClient();
db = mongoClient.getDatabase("test");
}
/**
* Read user account from DB
*/
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;
}
/**
* Write user account to DB
*/
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()));
}
/**
* Update DB
*/
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,163 +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;
}
/**
* Get user account
*/
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;
}
}
/**
* Set user account
*/
public void set(String userId, UserAccount userAccount) {
if (cache.containsKey(userId)) {
Node old = cache.get(userId);
old.userAccount = userAccount;
remove(old);
setHead(old);
} else {
Node newNode = new Node(userId, userAccount);
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);
}
/**
* Invalidate cache for user
*/
public void invalidate(String userId) {
System.out.println("# " + userId + " has been updated! Removing older version from cache...");
Node toBeRemoved = cache.get(userId);
remove(toBeRemoved);
cache.remove(userId);
}
public boolean isFull() {
return cache.size() >= capacity;
}
public UserAccount getLruData() {
return end.userAccount;
}
/**
* Clear cache
*/
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;
}
/**
* Set cache capacity
*/
public void setCapacity(int newCapacity) {
if (capacity > newCapacity) {
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll
// just clear the cache.
} else {
this.capacity = newCapacity;
}
}
}

View File

@ -1,50 +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;
/**
* Constructor
*/
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

@ -4,11 +4,7 @@ title: Callback
folder: callback
permalink: /patterns/callback/
categories: Other
tags:
- Java
- Difficulty-Beginner
- Functional
- Idiom
tags: Java
---
**Intent:** Callback is a piece of executable code that is passed as an

View File

@ -1,11 +1,12 @@
<?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">
<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.9.0</version>
<version>1.6.0</version>
</parent>
<artifactId>callback</artifactId>
<dependencies>

View File

@ -2,16 +2,12 @@ package com.iluwatar.callback;
/**
*
* Callback pattern is more native for functional languages where functions are treated as
* first-class citizens. Prior to Java 8 callbacks can be simulated using simple (alike command)
* interfaces.
* Callback pattern is more native for functional languages where functions are treated as first-class citizens.
* Prior to Java 8 callbacks can be simulated using simple (alike command) interfaces.
*
*/
public class App {
/**
* Program entry point
*/
public static void main(String[] args) {
Task task = new SimpleTask();
Callback callback = new Callback() {

View File

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

View File

@ -9,6 +9,7 @@ public class SimpleTask extends Task {
@Override
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

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

View File

@ -2,38 +2,18 @@ package com.iluwatar.callback;
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 {
private Integer callingCount = 0;
@Test
public void test() {
Callback callback = new Callback() {
@Override
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);
String[] args = {};
App.main(args);
}
}

View File

@ -4,10 +4,7 @@ title: Chain of responsibility
folder: chain
permalink: /patterns/chain/
categories: Behavioral
tags:
- Java
- Gang Of Four
- Difficulty-Intermediate
tags: Java
---
**Intent:** Avoid coupling the sender of a request to its receiver by giving
@ -26,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)
* [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"?>
<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">
<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.9.0</version>
<version>1.6.0</version>
</parent>
<artifactId>chain</artifactId>
<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
* series of processing objects. Each processing object contains logic that defines the types of
* command objects that it can handle; the rest are passed to the next processing object in the
* chain. A mechanism also exists for adding new processing objects to the end of this 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.
* Chain of Responsibility organizes request handlers ({@link RequestHandler}) into a
* chain where each handler has a chance to act on the request on its turn. In
* this example the king ({@link OrcKing}) makes requests and the military orcs
* ({@link OrcCommander}, {@link OrcOfficer}, {@link OrcSoldier}) form the handler chain.
*
*/
public class App {
/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {
OrcKing king = new OrcKing();
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"));
}

View File

@ -15,7 +15,6 @@ public class OrcCommander extends RequestHandler {
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) {
printHandling(req);
req.markHandled();
} else {
super.handleRequest(req);
}

View File

@ -15,7 +15,6 @@ public class OrcOfficer extends RequestHandler {
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.TORTURE_PRISONER)) {
printHandling(req);
req.markHandled();
} else {
super.handleRequest(req);
}

View File

@ -15,7 +15,6 @@ public class OrcSoldier extends RequestHandler {
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.COLLECT_TAX)) {
printHandling(req);
req.markHandled();
} else {
super.handleRequest(req);
}

View File

@ -1,78 +1,38 @@
package com.iluwatar.chain;
import java.util.Objects;
/**
*
* Request
*
*/
public class Request {
/**
* The type of this request, used by each item in the chain to see if they should or can handle
* this particular request
*/
private final RequestType requestType;
private String requestDescription;
private RequestType requestType;
/**
* A description of the request
*/
private final String requestDescription;
/**
* Indicates if the request is handled or not. A request can only switch state from unhandled to
* handled, there's no way to 'unhandle' a request
*/
private boolean handled = false;
/**
* Create a new request of the given type and accompanied description.
*
* @param requestType The type of request
* @param requestDescription The description of the request
*/
public Request(final RequestType requestType, final String requestDescription) {
this.requestType = Objects.requireNonNull(requestType);
this.requestDescription = Objects.requireNonNull(requestDescription);
public Request(RequestType requestType, String requestDescription) {
this.setRequestType(requestType);
this.setRequestDescription(requestDescription);
}
/**
* Get a description of the request
*
* @return A human readable description of the request
*/
public String getRequestDescription() {
return requestDescription;
}
/**
* Get the type of this request, used by each person in the chain of command to see if they should
* or can handle this particular request
*
* @return The request type
*/
public void setRequestDescription(String requestDescription) {
this.requestDescription = requestDescription;
}
public RequestType getRequestType() {
return requestType;
}
/**
* Mark the request as handled
*/
public void markHandled() {
this.handled = true;
}
/**
* Indicates if this request is handled or not
*
* @return <tt>true</tt> when the request is handled, <tt>false</tt> if not
*/
public boolean isHandled() {
return this.handled;
public void setRequestType(RequestType requestType) {
this.requestType = requestType;
}
@Override
public String toString() {
return getRequestDescription();
}
}

View File

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

View File

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

View File

@ -1,37 +0,0 @@
package com.iluwatar.chain;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
* Date: 12/6/15 - 9:29 PM
*
* @author Jeroen Meulemeester
*/
public class OrcKingTest {
/**
* All possible requests
*/
private static final Request[] REQUESTS = new Request[]{
new Request(RequestType.DEFEND_CASTLE, "Don't let the barbarians enter my castle!!"),
new Request(RequestType.TORTURE_PRISONER, "Don't just stand there, tickle him!"),
new Request(RequestType.COLLECT_TAX, "Don't steal, the King hates competition ..."),
};
@Test
public void testMakeRequest() throws Exception {
final OrcKing king = new OrcKing();
for (final Request request : REQUESTS) {
king.makeRequest(request);
assertTrue(
"Expected all requests from King to be handled, but [" + request + "] was not!",
request.isHandled()
);
}
}
}

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress checks="AvoidStarImport" files="[\\/]src[\\/]test[\\/]java[\\/]"/>
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]test[\\/]resources[\\/]"/>
<suppress checks="[a-zA-Z0-9]*" files="[\\/]build[\\/]generated-sources[\\/]"/>
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]main[\\/]resources[\\/]"/>
</suppressions>

View File

@ -26,9 +26,7 @@
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="fileExtensions" value="java, xml, properties"/>
<property name="severity" value="error"/>
<property name="severity" value="warning"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
@ -50,7 +48,7 @@
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="LineLength">
<property name="max" value="120"/>
<property name="max" value="100"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="AvoidStarImport"/>
@ -63,7 +61,7 @@
</module>
<module name="NeedBraces"/>
<module name="LeftCurly">
<property name="maxLineLength" value="120"/>
<property name="maxLineLength" value="100"/>
</module>
<module name="RightCurly"/>
<module name="RightCurly">
@ -88,6 +86,9 @@
<module name="FallThrough"/>
<module name="UpperEll"/>
<module name="ModifierOrder"/>
<module name="EmptyLineSeparator">
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="SeparatorWrap">
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
@ -96,19 +97,42 @@
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName">
<message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern"
value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern"
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalVariableName">
<property name="tokens" value="VARIABLE_DEF"/>
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<property name="allowOneCharVarInForLoop" value="true"/>
<message key="name.invalidPattern"
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="NoFinalizer"/>
<module name="GenericWhitespace">
<message key="ws.followed"
@ -133,6 +157,14 @@
<property name="allowedAbbreviationLength" value="1"/>
</module>
<module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
<module name="CustomImportOrder">
<property name="thirdPartyPackageRegExp" value=".*"/>
<property name="specialImportsRegExp" value="com.google"/>
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="customImportOrderRules"
value="STATIC###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE"/>
</module>
<module name="MethodParamPad"/>
<module name="OperatorWrap">
<property name="option" value="NL"/>
@ -148,6 +180,11 @@
</module>
<module name="NonEmptyAtclauseDescription"/>
<module name="JavadocTagContinuationIndentation"/>
<module name="SummaryJavadocCheck">
<property name="forbiddenSummaryFragments"
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
<module name="JavadocParagraph"/>
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
@ -158,7 +195,7 @@
<property name="allowMissingThrowsTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="minLineCount" value="2"/>
<property name="allowedAnnotations" value="Override, Test, Before, After, Parameters, Given, When, BeforeClass, AfterClass, Parameterized"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="allowThrowsTagsForSubclasses" value="true"/>
</module>
<module name="MethodName">
@ -168,5 +205,4 @@
</module>
<module name="SingleLineJavadoc"/>
</module>
</module>

View File

@ -4,14 +4,9 @@ title: Command
folder: command
permalink: /patterns/command/
categories: Behavioral
tags:
- Java
- Gang Of Four
- Difficulty-Intermediate
tags: Java
---
**Also known as:** Action, Transaction
**Intent:** Encapsulate a request as an object, thereby letting you
parameterize clients with different requests, queue or log requests, and
support undoable operations.
@ -35,7 +30,3 @@ support undoable operations.
**Real world examples:**
* [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"?>
<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">
<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.9.0</version>
<version>1.6.0</version>
</parent>
<artifactId>command</artifactId>
<dependencies>
@ -14,10 +15,5 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</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
* 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.
* In Command pattern actions are objects that can be executed and undone.
* <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.
* 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.
* <p>
* In other words, in this example 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, the wizard keeps track of the
* spells undone, so they can be redone.
* In other words, in this example 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, the wizard keeps track of the spells undone, so they
* can be redone.
*
*
*/
@ -25,7 +22,6 @@ public class App {
/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {

View File

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

View File

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

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