Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
d149c34307 | |||
ae212dec7e | |||
d9e55c7c81 | |||
c234ee7207 | |||
c6aae59407 | |||
e3f8ef00ae | |||
74481a0267 | |||
325b68908e | |||
694a1eeafa | |||
1496a468c5 | |||
af77911a84 |
14
.travis.yml
14
.travis.yml
@ -1,18 +1,20 @@
|
|||||||
language: java
|
language: java
|
||||||
|
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk8
|
- oraclejdk8
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- GH_REF: github.com/iluwatar/java-design-patterns.git
|
- GH_REF: github.com/iluwatar/java-design-patterns.git
|
||||||
- secure: LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg=
|
- secure: "LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg="
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- export DISPLAY=:99.0
|
- "export DISPLAY=:99.0"
|
||||||
- sh -e /etc/init.d/xvfb start
|
- "sh -e /etc/init.d/xvfb start"
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- mvn clean test jacoco:report coveralls:report
|
- mvn clean test jacoco:report coveralls:report
|
||||||
- bash update-ghpages.sh
|
- bash update-ghpages.sh
|
||||||
|
|
||||||
|
# Migration to container-based infrastructure
|
||||||
sudo: false
|
sudo: false
|
||||||
|
@ -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
|
|
||||||
|
|
||||||
|
|
10
README.md
10
README.md
@ -26,13 +26,7 @@ are familiar with the patterns.
|
|||||||
Before you dive into the material, you should be familiar with various
|
Before you dive into the material, you should be familiar with various
|
||||||
[Programming/Software Design Principles](http://webpro.github.io/programming-principles/).
|
[Programming/Software Design Principles](http://webpro.github.io/programming-principles/).
|
||||||
|
|
||||||
All designs should be as simple as possible. You should start with KISS, YAGNI,
|
Once you are familiar with these concepts you can start drilling down into patterns by any of the following approaches
|
||||||
and Do The Simplest Thing That Could Possibly Work principles. Complexity and
|
|
||||||
patterns should only be introduced when they are needed for practical
|
|
||||||
extensibility.
|
|
||||||
|
|
||||||
Once you are familiar with these concepts you can start drilling down into
|
|
||||||
patterns by any of the following approaches
|
|
||||||
|
|
||||||
- Using difficulty tags, `Difficulty-Beginner`, `Difficulty-Intermediate` & `Difficulty-Expert`.
|
- Using difficulty tags, `Difficulty-Beginner`, `Difficulty-Intermediate` & `Difficulty-Expert`.
|
||||||
- Using pattern categories, `Creational`, `Behavioral` and others.
|
- Using pattern categories, `Creational`, `Behavioral` and others.
|
||||||
@ -44,6 +38,7 @@ If you are willing to contribute to the project you will find the relevant infor
|
|||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
|
|
||||||
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
||||||
* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683)
|
* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683)
|
||||||
* [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/)
|
* [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/)
|
||||||
* [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf)
|
* [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf)
|
||||||
@ -53,6 +48,7 @@ If you are willing to contribute to the project you will find the relevant infor
|
|||||||
* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)
|
* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)
|
||||||
* [Spring Data](http://www.amazon.com/Spring-Data-Mark-Pollack/dp/1449323952/ref=sr_1_1)
|
* [Spring Data](http://www.amazon.com/Spring-Data-Mark-Pollack/dp/1449323952/ref=sr_1_1)
|
||||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
||||||
|
* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697)
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
|
@ -4,13 +4,9 @@ title: Abstract Factory
|
|||||||
folder: abstract-factory
|
folder: abstract-factory
|
||||||
permalink: /patterns/abstract-factory/
|
permalink: /patterns/abstract-factory/
|
||||||
categories: Creational
|
categories: Creational
|
||||||
tags:
|
tags: Java
|
||||||
- Java
|
|
||||||
- Gang Of Four
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Also known as:** Kit
|
|
||||||
|
|
||||||
**Intent:** Provide an interface for creating families of related or dependent
|
**Intent:** Provide an interface for creating families of related or dependent
|
||||||
objects without specifying their concrete classes.
|
objects without specifying their concrete classes.
|
||||||
|
|
||||||
@ -26,7 +22,3 @@ objects without specifying their concrete classes.
|
|||||||
**Real world examples:**
|
**Real world examples:**
|
||||||
|
|
||||||
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
|
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
|
||||||
|
|
||||||
**Credits**
|
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>abstract-factory</artifactId>
|
<artifactId>abstract-factory</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -3,18 +3,12 @@ package com.iluwatar.abstractfactory;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* The Abstract Factory pattern provides a way to encapsulate a group of individual factories that
|
* The essence of the Abstract Factory pattern is a factory interface
|
||||||
* have a common theme without specifying their concrete classes. In normal usage, the client
|
* ({@link KingdomFactory}) and its implementations ({@link ElfKingdomFactory},
|
||||||
* software creates a concrete implementation of the abstract factory and then uses the generic
|
* {@link OrcKingdomFactory}).
|
||||||
* interface of the factory to create the concrete objects that are part of the theme. The client
|
|
||||||
* does not know (or care) which concrete objects it gets from each of these internal factories,
|
|
||||||
* since it uses only the generic interfaces of their products. This pattern separates the details
|
|
||||||
* of implementation of a set of objects from their general usage and relies on object composition,
|
|
||||||
* as object creation is implemented in methods exposed in the factory interface.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) and
|
* The example uses both concrete implementations to create a king, a castle and
|
||||||
* its implementations ({@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses both
|
* an army.
|
||||||
* concrete implementations to create a king, a castle and an army.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
@ -23,10 +17,8 @@ public class App {
|
|||||||
private Castle castle;
|
private Castle castle;
|
||||||
private Army army;
|
private Army army;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates kingdom
|
* Creates kingdom
|
||||||
*
|
|
||||||
* @param factory
|
* @param factory
|
||||||
*/
|
*/
|
||||||
public void createKingdom(final KingdomFactory factory) {
|
public void createKingdom(final KingdomFactory factory) {
|
||||||
@ -78,33 +70,4 @@ public class App {
|
|||||||
private void setArmy(final Army army) {
|
private void setArmy(final Army army) {
|
||||||
this.army = army;
|
this.army = army;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Program entry point
|
|
||||||
*
|
|
||||||
* @param args command line args
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
|
|
||||||
App app = new App();
|
|
||||||
|
|
||||||
System.out.println("Elf Kingdom");
|
|
||||||
KingdomFactory elfKingdomFactory;
|
|
||||||
elfKingdomFactory = app.getElfKingdomFactory();
|
|
||||||
app.createKingdom(elfKingdomFactory);
|
|
||||||
System.out.println(app.getArmy().getDescription());
|
|
||||||
System.out.println(app.getCastle().getDescription());
|
|
||||||
System.out.println(app.getKing().getDescription());
|
|
||||||
|
|
||||||
System.out.println("\nOrc Kingdom");
|
|
||||||
KingdomFactory orcKingdomFactory;
|
|
||||||
orcKingdomFactory = app.getOrcKingdomFactory();
|
|
||||||
app.createKingdom(orcKingdomFactory);
|
|
||||||
System.out.println(app.getArmy().getDescription());
|
|
||||||
System.out.println(app.getCastle().getDescription());
|
|
||||||
System.out.println(app.getKing().getDescription());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ package com.iluwatar.abstractfactory;
|
|||||||
*/
|
*/
|
||||||
public class OrcKing implements King {
|
public class OrcKing implements King {
|
||||||
|
|
||||||
static final String DESCRIPTION = "This is the Orc king!";
|
static final String DESCRIPTION = "This is the Orc king!";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
|
@ -18,4 +18,5 @@ public class OrcKingdomFactory implements KingdomFactory {
|
|||||||
public Army createArmy() {
|
public Army createArmy() {
|
||||||
return new OrcArmy();
|
return new OrcArmy();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@ -8,7 +7,7 @@ import org.junit.Test;
|
|||||||
|
|
||||||
public class AppTest {
|
public class AppTest {
|
||||||
|
|
||||||
private App app = new App();
|
private App app = new App();;
|
||||||
private KingdomFactory elfFactory;
|
private KingdomFactory elfFactory;
|
||||||
private KingdomFactory orcFactory;
|
private KingdomFactory orcFactory;
|
||||||
|
|
||||||
|
@ -4,13 +4,9 @@ title: Adapter
|
|||||||
folder: adapter
|
folder: adapter
|
||||||
permalink: /patterns/adapter/
|
permalink: /patterns/adapter/
|
||||||
categories: Structural
|
categories: Structural
|
||||||
tags:
|
tags: Java
|
||||||
- Java
|
|
||||||
- Gang Of Four
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Also known as:** Wrapper
|
|
||||||
|
|
||||||
**Intent:** Convert the interface of a class into another interface the clients
|
**Intent:** Convert the interface of a class into another interface the clients
|
||||||
expect. Adapter lets classes work together that couldn't otherwise because of
|
expect. Adapter lets classes work together that couldn't otherwise because of
|
||||||
incompatible interfaces.
|
incompatible interfaces.
|
||||||
@ -26,7 +22,3 @@ incompatible interfaces.
|
|||||||
**Real world examples:**
|
**Real world examples:**
|
||||||
|
|
||||||
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
|
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
|
||||||
|
|
||||||
**Credits**
|
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>adapter</artifactId>
|
<artifactId>adapter</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -14,10 +15,5 @@
|
|||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.mockito</groupId>
|
|
||||||
<artifactId>mockito-core</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,28 +1,25 @@
|
|||||||
package com.iluwatar.adapter;
|
package com.iluwatar.adapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An adapter helps two incompatible interfaces to work together. This is the real world definition
|
|
||||||
* for an adapter. Interfaces may be incompatible but the inner functionality should suit the need.
|
|
||||||
* The Adapter design pattern allows otherwise incompatible classes to work together by converting
|
|
||||||
* the interface of one class into an interface expected by the clients.
|
|
||||||
*
|
*
|
||||||
* <p>There are two variations of the Adapter pattern: The class adapter implements the adaptee's
|
* There are two variations of the Adapter pattern: The class adapter implements
|
||||||
* interface whereas the object adapter uses composition to contain the adaptee in the adapter
|
* the adaptee's interface whereas the object adapter uses composition to
|
||||||
* object. This example uses the object adapter approach.
|
* contain the adaptee in the adapter object. This example uses the object
|
||||||
|
* adapter approach.
|
||||||
|
* <p>
|
||||||
|
* The Adapter ({@link GnomeEngineer}) converts the interface of the target class
|
||||||
|
* ({@link GoblinGlider}) into a suitable one expected by the client
|
||||||
|
* ({@link GnomeEngineeringManager}).
|
||||||
*
|
*
|
||||||
* <p>The Adapter ({@link GnomeEngineer}) converts the interface of the target class (
|
|
||||||
* {@link GoblinGlider}) into a suitable one expected by the client ({@link GnomeEngineeringManager}
|
|
||||||
* ).
|
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point.
|
* Program entry point
|
||||||
*
|
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Engineer manager = new GnomeEngineeringManager(new GnomeEngineer());
|
Engineer manager = new GnomeEngineeringManager();
|
||||||
manager.operateDevice();
|
manager.operateDevice();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,4 +8,5 @@ package com.iluwatar.adapter;
|
|||||||
public interface Engineer {
|
public interface Engineer {
|
||||||
|
|
||||||
void operateDevice();
|
void operateDevice();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package com.iluwatar.adapter;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Adapter class. Adapts the interface of the device ({@link GoblinGlider}) into {@link Engineer}
|
* Adapter class. Adapts the interface of the device ({@link GoblinGlider}) into
|
||||||
* interface expected by the client ({@link GnomeEngineeringManager}).
|
* {@link Engineer} interface expected by the client ({@link GnomeEngineeringManager}).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class GnomeEngineer implements Engineer {
|
public class GnomeEngineer implements Engineer {
|
||||||
@ -20,4 +20,5 @@ public class GnomeEngineer implements Engineer {
|
|||||||
glider.gainSpeed();
|
glider.gainSpeed();
|
||||||
glider.takeOff();
|
glider.takeOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,20 @@
|
|||||||
package com.iluwatar.adapter;
|
package com.iluwatar.adapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* GnomeEngineering manager uses {@link Engineer} to operate devices.
|
* GnomeEngineering manager uses {@link Engineer} to operate devices.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public class GnomeEngineeringManager implements Engineer {
|
public class GnomeEngineeringManager implements Engineer {
|
||||||
|
|
||||||
private Engineer engineer;
|
private Engineer engineer;
|
||||||
|
|
||||||
public GnomeEngineeringManager() {
|
public GnomeEngineeringManager() {
|
||||||
|
engineer = new GnomeEngineer();
|
||||||
}
|
|
||||||
|
|
||||||
public GnomeEngineeringManager(Engineer engineer) {
|
|
||||||
this.engineer = engineer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void operateDevice() {
|
public void operateDevice() {
|
||||||
engineer.operateDevice();
|
engineer.operateDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEngineer(Engineer engineer) {
|
|
||||||
this.engineer = engineer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
package com.iluwatar.adapter;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An adapter helps two incompatible interfaces to work together. This is the real world definition
|
|
||||||
* for an adapter. Interfaces may be incompatible but the inner functionality should suit the need.
|
|
||||||
* The Adapter design pattern allows otherwise incompatible classes to work together by converting
|
|
||||||
* the interface of one class into an interface expected by the clients.
|
|
||||||
*
|
|
||||||
* <p>There are two variations of the Adapter pattern:
|
|
||||||
* The class adapter implements the adaptee's
|
|
||||||
* interface whereas the object adapter uses composition to contain the adaptee in the adapter
|
|
||||||
* object. This example uses the object adapter approach.
|
|
||||||
*
|
|
||||||
* <p>The Adapter ({@link GnomeEngineer}) converts the interface
|
|
||||||
* of the target class ({@link GoblinGlider}) into a suitable one expected by
|
|
||||||
* the client ({@link GnomeEngineeringManager}
|
|
||||||
* ).
|
|
||||||
*/
|
|
||||||
public class AdapterPatternTest {
|
|
||||||
|
|
||||||
private Map<String, Object> beans;
|
|
||||||
|
|
||||||
private static final String ENGINEER_BEAN = "engineer";
|
|
||||||
|
|
||||||
private static final String MANAGER_BEAN = "manager";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method runs before the test execution and sets the bean objects in the beans Map.
|
|
||||||
*/
|
|
||||||
@Before
|
|
||||||
public void setup() {
|
|
||||||
beans = new HashMap<>();
|
|
||||||
|
|
||||||
GnomeEngineer gnomeEngineer = spy(new GnomeEngineer());
|
|
||||||
beans.put(ENGINEER_BEAN, gnomeEngineer);
|
|
||||||
|
|
||||||
GnomeEngineeringManager manager = new GnomeEngineeringManager();
|
|
||||||
manager.setEngineer((GnomeEngineer) beans.get(ENGINEER_BEAN));
|
|
||||||
beans.put(MANAGER_BEAN, manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This test asserts that when we call operateDevice() method on a manager bean, it is internally
|
|
||||||
* calling operateDevice method on the engineer object. The Adapter ({@link GnomeEngineer})
|
|
||||||
* converts the interface of the target class ( {@link GoblinGlider}) into a suitable one expected
|
|
||||||
* by the client ({@link GnomeEngineeringManager} ).
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testAdapter() {
|
|
||||||
Engineer manager = (Engineer) beans.get(MANAGER_BEAN);
|
|
||||||
|
|
||||||
// when manager is asked to operate device
|
|
||||||
manager.operateDevice();
|
|
||||||
|
|
||||||
// Manager internally calls the engineer object to operateDevice
|
|
||||||
Engineer engineer = (Engineer) beans.get(ENGINEER_BEAN);
|
|
||||||
verify(engineer).operateDevice();
|
|
||||||
}
|
|
||||||
}
|
|
19
adapter/src/test/java/com/iluwatar/adapter/AppTest.java
Normal file
19
adapter/src/test/java/com/iluwatar/adapter/AppTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>async-method-invocation</artifactId>
|
<artifactId>async-method-invocation</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -4,24 +4,23 @@ import java.util.concurrent.Callable;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
|
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
|
||||||
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated
|
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated value,
|
||||||
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and
|
* <code>AsyncCallback</code> which can be provided to be executed on task completion and
|
||||||
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
|
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
|
||||||
* <p>
|
* <p>
|
||||||
* The main method shows example flow of async invocations. The main thread starts multiple tasks
|
* The main method shows example flow of async invocations. The main thread starts multiple tasks with
|
||||||
* with variable durations and then continues its own work. When the main thread has done it's job
|
* variable durations and then continues its own work. When the main thread has done it's job it collects
|
||||||
* it collects the results of the async tasks. Two of the tasks are handled with callbacks, meaning
|
* the results of the async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are
|
||||||
* the callbacks are executed immediately when the tasks complete.
|
* executed immediately when the tasks complete.
|
||||||
* <p>
|
* <p>
|
||||||
* Noteworthy difference of thread usage between the async results and callbacks is that the async
|
* Noteworthy difference of thread usage between the async results and callbacks is that the async results
|
||||||
* results are collected in the main thread but the callbacks are executed within the worker
|
* are collected in the main thread but the callbacks are executed within the worker threads. This should be
|
||||||
* threads. This should be noted when working with thread pools.
|
* noted when working with thread pools.
|
||||||
* <p>
|
* <p>
|
||||||
* Java provides its own implementations of async method invocation pattern. FutureTask,
|
* Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture
|
||||||
* CompletableFuture and ExecutorService are the real world implementations of this pattern. But due
|
* and ExecutorService are the real world implementations of this pattern. But due to the nature of parallel
|
||||||
* to the nature of parallel programming, the implementations are not trivial. This example does not
|
* programming, the implementations are not trivial. This example does not take all possible scenarios into
|
||||||
* take all possible scenarios into account but rather provides a simple version that helps to
|
* account but rather provides a simple version that helps to understand the pattern.
|
||||||
* understand the pattern.
|
|
||||||
*
|
*
|
||||||
* @see AsyncResult
|
* @see AsyncResult
|
||||||
* @see AsyncCallback
|
* @see AsyncCallback
|
||||||
@ -41,10 +40,8 @@ public class App {
|
|||||||
AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
|
AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
|
||||||
AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
|
AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
|
||||||
AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
||||||
AsyncResult<Integer> asyncResult4 =
|
AsyncResult<Integer> asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
|
||||||
executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
|
AsyncResult<String> asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
|
||||||
AsyncResult<String> asyncResult5 =
|
|
||||||
executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
|
|
||||||
|
|
||||||
// emulate processing in the current thread while async tasks are running in their own threads
|
// emulate processing in the current thread while async tasks are running in their own threads
|
||||||
Thread.sleep(350); // Oh boy I'm working hard here
|
Thread.sleep(350); // Oh boy I'm working hard here
|
||||||
|
@ -18,4 +18,5 @@ public interface AsyncCallback<T> {
|
|||||||
* @param ex empty value if execution succeeds, some exception if executions fails
|
* @param ex empty value if execution succeeds, some exception if executions fails
|
||||||
*/
|
*/
|
||||||
void onComplete(T value, Optional<Exception> ex);
|
void onComplete(T value, Optional<Exception> ex);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,4 +38,5 @@ public interface AsyncExecutor {
|
|||||||
* @throws InterruptedException if the execution is interrupted
|
* @throws InterruptedException if the execution is interrupted
|
||||||
*/
|
*/
|
||||||
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
|
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,7 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException,
|
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException {
|
||||||
InterruptedException {
|
|
||||||
if (asyncResult.isCompleted()) {
|
if (asyncResult.isCompleted()) {
|
||||||
return asyncResult.getValue();
|
return asyncResult.getValue();
|
||||||
} else {
|
} else {
|
||||||
@ -45,8 +44,8 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple implementation of async result that allows completing it successfully with a value or
|
* Simple implementation of async result that allows completing it successfully with a value
|
||||||
* exceptionally with an exception. A really simplified version from its real life cousins
|
* or exceptionally with an exception. A really simplified version from its real life cousins
|
||||||
* FutureTask and CompletableFuture.
|
* FutureTask and CompletableFuture.
|
||||||
*
|
*
|
||||||
* @see java.util.concurrent.FutureTask
|
* @see java.util.concurrent.FutureTask
|
||||||
@ -71,8 +70,8 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value from successful execution and executes callback if available. Notifies any
|
* Sets the value from successful execution and executes callback if available. Notifies
|
||||||
* thread waiting for completion.
|
* any thread waiting for completion.
|
||||||
*
|
*
|
||||||
* @param value value of the evaluated task
|
* @param value value of the evaluated task
|
||||||
*/
|
*/
|
||||||
@ -86,8 +85,8 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the exception from failed execution and executes callback if available. Notifies any
|
* Sets the exception from failed execution and executes callback if available. Notifies
|
||||||
* thread waiting for completion.
|
* any thread waiting for completion.
|
||||||
*
|
*
|
||||||
* @param exception exception of the failed task
|
* @param exception exception of the failed task
|
||||||
*/
|
*/
|
||||||
|
@ -14,4 +14,5 @@ public class AppTest {
|
|||||||
String[] args = {};
|
String[] args = {};
|
||||||
App.main(args);
|
App.main(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,9 @@ title: Bridge
|
|||||||
folder: bridge
|
folder: bridge
|
||||||
permalink: /patterns/bridge/
|
permalink: /patterns/bridge/
|
||||||
categories: Structural
|
categories: Structural
|
||||||
tags:
|
tags: Java
|
||||||
- Java
|
|
||||||
- Gang Of Four
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Also known as:** Handle/Body
|
|
||||||
|
|
||||||
**Intent:** Decouple an abstraction from its implementation so that the two can
|
**Intent:** Decouple an abstraction from its implementation so that the two can
|
||||||
vary independently.
|
vary independently.
|
||||||
|
|
||||||
@ -24,7 +20,3 @@ vary independently.
|
|||||||
* changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled.
|
* changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled.
|
||||||
* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies
|
* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies
|
||||||
* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
|
* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
|
||||||
|
|
||||||
**Credits**
|
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>bridge</artifactId>
|
<artifactId>bridge</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -2,38 +2,38 @@ package com.iluwatar.bridge;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* The Bridge pattern can also be thought of as two layers of abstraction. With Bridge, you can
|
* In Bridge pattern both abstraction ({@link MagicWeapon}) and implementation
|
||||||
* decouple an abstraction from its implementation so that the two can vary independently.
|
* ({@link MagicWeaponImpl}) have their own class hierarchies. The interface of the
|
||||||
* <p>
|
* implementations can be changed without affecting the clients.
|
||||||
* In Bridge pattern both abstraction ({@link MagicWeapon}) and implementation (
|
|
||||||
* {@link MagicWeaponImpl}) have their own class hierarchies. The interface of the implementations
|
|
||||||
* can be changed without affecting the clients.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point
|
* Program entry point
|
||||||
*
|
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(new Excalibur());
|
BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(
|
||||||
|
new Excalibur());
|
||||||
blindingMagicWeapon.wield();
|
blindingMagicWeapon.wield();
|
||||||
blindingMagicWeapon.blind();
|
blindingMagicWeapon.blind();
|
||||||
blindingMagicWeapon.swing();
|
blindingMagicWeapon.swing();
|
||||||
blindingMagicWeapon.unwield();
|
blindingMagicWeapon.unwield();
|
||||||
|
|
||||||
FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(new Mjollnir());
|
FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(
|
||||||
|
new Mjollnir());
|
||||||
flyingMagicWeapon.wield();
|
flyingMagicWeapon.wield();
|
||||||
flyingMagicWeapon.fly();
|
flyingMagicWeapon.fly();
|
||||||
flyingMagicWeapon.swing();
|
flyingMagicWeapon.swing();
|
||||||
flyingMagicWeapon.unwield();
|
flyingMagicWeapon.unwield();
|
||||||
|
|
||||||
SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(new Stormbringer());
|
SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(
|
||||||
|
new Stormbringer());
|
||||||
soulEatingMagicWeapon.wield();
|
soulEatingMagicWeapon.wield();
|
||||||
soulEatingMagicWeapon.swing();
|
soulEatingMagicWeapon.swing();
|
||||||
soulEatingMagicWeapon.eatSoul();
|
soulEatingMagicWeapon.eatSoul();
|
||||||
soulEatingMagicWeapon.unwield();
|
soulEatingMagicWeapon.unwield();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,4 +34,5 @@ public class BlindingMagicWeapon extends MagicWeapon {
|
|||||||
public void blind() {
|
public void blind() {
|
||||||
getImp().blindImp();
|
getImp().blindImp();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ public class Excalibur extends BlindingMagicWeaponImpl {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void blindImp() {
|
public void blindImp() {
|
||||||
System.out.println("bright light streams from Excalibur blinding the enemy");
|
System.out
|
||||||
|
.println("bright light streams from Excalibur blinding the enemy");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,4 +22,5 @@ public abstract class MagicWeapon {
|
|||||||
public MagicWeaponImpl getImp() {
|
public MagicWeaponImpl getImp() {
|
||||||
return imp;
|
return imp;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ public class Mjollnir extends FlyingMagicWeaponImpl {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flyImp() {
|
public void flyImp() {
|
||||||
System.out.println("Mjollnir hits the enemy in the air and returns back to the owner's hand");
|
System.out
|
||||||
|
.println("Mjollnir hits the enemy in the air and returns back to the owner's hand");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,4 +26,5 @@ public class Stormbringer extends SoulEatingMagicWeaponImpl {
|
|||||||
public void eatSoulImp() {
|
public void eatSoulImp() {
|
||||||
System.out.println("Stormbringer devours the enemy's soul");
|
System.out.println("Stormbringer devours the enemy's soul");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ title: Builder
|
|||||||
folder: builder
|
folder: builder
|
||||||
permalink: /patterns/builder/
|
permalink: /patterns/builder/
|
||||||
categories: Creational
|
categories: Creational
|
||||||
tags:
|
tags: Java
|
||||||
- Java
|
|
||||||
- Gang Of Four
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Separate the construction of a complex object from its
|
**Intent:** Separate the construction of a complex object from its
|
||||||
@ -24,7 +22,3 @@ representations.
|
|||||||
|
|
||||||
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
|
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
|
||||||
* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
|
* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
|
||||||
|
|
||||||
**Credits**
|
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>builder</artifactId>
|
<artifactId>builder</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -1,54 +1,41 @@
|
|||||||
package com.iluwatar.builder;
|
package com.iluwatar.builder;
|
||||||
|
|
||||||
import com.iluwatar.builder.Hero.HeroBuilder;
|
import com.iluwatar. builder.Hero.HeroBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* The intention of the Builder pattern is to find a solution to the telescoping constructor
|
* This is the Builder pattern variation as described by Joshua Bloch in
|
||||||
* anti-pattern. The telescoping constructor anti-pattern occurs when the increase of object
|
* Effective Java 2nd Edition.
|
||||||
* constructor parameter combination leads to an exponential list of constructors. Instead of using
|
|
||||||
* numerous constructors, the builder pattern uses another object, a builder, that receives each
|
|
||||||
* initialization parameter step by step and then returns the resulting constructed object at once.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The Builder pattern has another benefit. It can be used for objects that contain flat data (html
|
* We want to build {@link Hero} objects, but its construction is complex because of the
|
||||||
* code, SQL query, X.509 certificate...), that is to say, data that can't be easily edited. This
|
* many parameters needed. To aid the user we introduce {@link HeroBuilder} class.
|
||||||
* type of data cannot be edited step by step and must be edited at once. The best way to construct
|
* {@link HeroBuilder} takes the minimum parameters to build {@link Hero} object in its
|
||||||
* such an object is to use a builder class.
|
* constructor. After that additional configuration for the {@link Hero} object can be
|
||||||
* <p>
|
* done using the fluent {@link HeroBuilder} interface. When configuration is ready the
|
||||||
* In this example we have the Builder pattern variation as described by Joshua Bloch in Effective
|
* build method is called to receive the final {@link Hero} object.
|
||||||
* Java 2nd Edition.
|
|
||||||
* <p>
|
|
||||||
* We want to build {@link Hero} objects, but its construction is complex because of the many
|
|
||||||
* parameters needed. To aid the user we introduce {@link HeroBuilder} class. {@link HeroBuilder}
|
|
||||||
* takes the minimum parameters to build {@link Hero} object in its constructor. After that
|
|
||||||
* additional configuration for the {@link Hero} object can be done using the fluent
|
|
||||||
* {@link HeroBuilder} interface. When configuration is ready the build method is called to receive
|
|
||||||
* the final {@link Hero} object.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point
|
* Program entry point
|
||||||
*
|
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
Hero mage =
|
Hero mage = new HeroBuilder(Profession.MAGE, "Riobard")
|
||||||
new HeroBuilder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK)
|
.withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER)
|
||||||
.withWeapon(Weapon.DAGGER).build();
|
.build();
|
||||||
System.out.println(mage);
|
System.out.println(mage);
|
||||||
|
|
||||||
Hero warrior =
|
Hero warrior = new HeroBuilder(Profession.WARRIOR, "Amberjill")
|
||||||
new HeroBuilder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND)
|
.withHairColor(HairColor.BLOND)
|
||||||
.withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD)
|
.withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL)
|
||||||
.build();
|
.withWeapon(Weapon.SWORD).build();
|
||||||
System.out.println(warrior);
|
System.out.println(warrior);
|
||||||
|
|
||||||
Hero thief =
|
Hero thief = new HeroBuilder(Profession.THIEF, "Desmond")
|
||||||
new HeroBuilder(Profession.THIEF, "Desmond").withHairType(HairType.BALD)
|
.withHairType(HairType.BALD).withWeapon(Weapon.BOW).build();
|
||||||
.withWeapon(Weapon.BOW).build();
|
|
||||||
System.out.println(thief);
|
System.out.println(thief);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,7 @@ package com.iluwatar.builder;
|
|||||||
*/
|
*/
|
||||||
public enum HairType {
|
public enum HairType {
|
||||||
|
|
||||||
BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY(
|
BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY("long curly");
|
||||||
"long curly");
|
|
||||||
|
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
@ -95,7 +95,8 @@ public class Hero {
|
|||||||
|
|
||||||
public HeroBuilder(Profession profession, String name) {
|
public HeroBuilder(Profession profession, String name) {
|
||||||
if (profession == null || name == null) {
|
if (profession == null || name == null) {
|
||||||
throw new IllegalArgumentException("profession and name can not be null");
|
throw new IllegalArgumentException(
|
||||||
|
"profession and name can not be null");
|
||||||
}
|
}
|
||||||
this.profession = profession;
|
this.profession = profession;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -13,4 +13,5 @@ public enum Profession {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return name().toLowerCase();
|
return name().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,4 +13,5 @@ public enum Weapon {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return name().toLowerCase();
|
return name().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package com.iluwatar.builder;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.iluwatar.builder.App;
|
import com.iluwatar. builder.App;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>business-delegate</artifactId>
|
<artifactId>business-delegate</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -15,10 +15,5 @@
|
|||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.mockito</groupId>
|
|
||||||
<artifactId>mockito-core</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,34 +1,29 @@
|
|||||||
package com.iluwatar.business.delegate;
|
package com.iluwatar.business.delegate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Business Delegate pattern adds an abstraction layer between the presentation and business
|
|
||||||
* tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate
|
|
||||||
* encapsulates knowledge about how to locate, connect to, and interact with the business objects
|
|
||||||
* that make up the application.
|
|
||||||
*
|
*
|
||||||
* <p>Some of the services the Business Delegate uses are instantiated directly, and some can be
|
* The Business Delegate pattern adds an abstraction layer between the presentation and business tiers.
|
||||||
* retrieved through service lookups. The Business Delegate itself may contain business logic too
|
* By using the pattern we gain loose coupling between the tiers. The Business Delegate encapsulates
|
||||||
* potentially tying together multiple service calls, exception handling, retrying etc.
|
* knowledge about how to locate, connect to, and interact with the business objects that make up
|
||||||
|
* the application.
|
||||||
|
* <p>
|
||||||
|
* Some of the services the Business Delegate uses are instantiated directly, and some can be retrieved
|
||||||
|
* through service lookups. The Business Delegate itself may contain business logic too potentially tying
|
||||||
|
* together multiple service calls, exception handling, retrying etc.
|
||||||
|
* <p>
|
||||||
|
* In this example the client ({@link Client}) utilizes a business delegate ({@link BusinessDelegate}) to execute a task.
|
||||||
|
* The Business Delegate then selects the appropriate service and makes the service call.
|
||||||
*
|
*
|
||||||
* <p>In this example the client ({@link Client}) utilizes a business delegate (
|
|
||||||
* {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
|
|
||||||
* service and makes the service call.
|
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point.
|
* Program entry point
|
||||||
*
|
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
BusinessDelegate businessDelegate = new BusinessDelegate();
|
BusinessDelegate businessDelegate = new BusinessDelegate();
|
||||||
BusinessLookup businessLookup = new BusinessLookup();
|
|
||||||
businessLookup.setEjbService(new EjbService());
|
|
||||||
businessLookup.setJmsService(new JmsService());
|
|
||||||
|
|
||||||
businessDelegate.setLookupService(businessLookup);
|
|
||||||
businessDelegate.setServiceType(ServiceType.EJB);
|
businessDelegate.setServiceType(ServiceType.EJB);
|
||||||
|
|
||||||
Client client = new Client(businessDelegate);
|
Client client = new Client(businessDelegate);
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
package com.iluwatar.business.delegate;
|
package com.iluwatar.business.delegate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* BusinessDelegate separates the presentation and business tiers
|
* BusinessDelegate separates the presentation and business tiers
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public class BusinessDelegate {
|
public class BusinessDelegate {
|
||||||
|
|
||||||
private BusinessLookup lookupService;
|
private BusinessLookup lookupService = new BusinessLookup();
|
||||||
private BusinessService businessService;
|
private BusinessService businessService;
|
||||||
private ServiceType serviceType;
|
private ServiceType serviceType;
|
||||||
|
|
||||||
public void setLookupService(BusinessLookup businessLookup) {
|
|
||||||
this.lookupService = businessLookup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setServiceType(ServiceType serviceType) {
|
public void setServiceType(ServiceType serviceType) {
|
||||||
this.serviceType = serviceType;
|
this.serviceType = serviceType;
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,17 @@
|
|||||||
package com.iluwatar.business.delegate;
|
package com.iluwatar.business.delegate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for performing service lookups.
|
*
|
||||||
|
* Class for performing service lookups
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public class BusinessLookup {
|
public class BusinessLookup {
|
||||||
|
|
||||||
private EjbService ejbService;
|
|
||||||
|
|
||||||
private JmsService jmsService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param serviceType Type of service instance to be returned.
|
|
||||||
* @return Service instance.
|
|
||||||
*/
|
|
||||||
public BusinessService getBusinessService(ServiceType serviceType) {
|
public BusinessService getBusinessService(ServiceType serviceType) {
|
||||||
if (serviceType.equals(ServiceType.EJB)) {
|
if (serviceType.equals(ServiceType.EJB)) {
|
||||||
return ejbService;
|
return new EjbService();
|
||||||
} else {
|
} else {
|
||||||
return jmsService;
|
return new JmsService();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setJmsService(JmsService jmsService) {
|
|
||||||
this.jmsService = jmsService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEjbService(EjbService ejbService) {
|
|
||||||
this.ejbService = ejbService;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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
1
caching/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/target/
|
|
Binary file not shown.
Before Width: | Height: | Size: 55 KiB |
@ -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>
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
layout: pattern
|
|
||||||
title: Caching
|
|
||||||
folder: caching
|
|
||||||
permalink: /patterns/caching/
|
|
||||||
categories: Other
|
|
||||||
tags:
|
|
||||||
- Java
|
|
||||||
---
|
|
||||||
|
|
||||||
**Intent:** To avoid expensive re-acquisition of resources by not releasing
|
|
||||||
the resources immediately after their use. The resources retain their identity, are kept in some
|
|
||||||
fast-access storage, and are re-used to avoid having to acquire them again.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
**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)
|
|
@ -1,51 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>com.iluwatar</groupId>
|
|
||||||
<artifactId>java-design-patterns</artifactId>
|
|
||||||
<version>1.8.0</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>caching</artifactId>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mongodb</groupId>
|
|
||||||
<artifactId>mongodb-driver</artifactId>
|
|
||||||
<version>3.0.4</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mongodb</groupId>
|
|
||||||
<artifactId>mongodb-driver-core</artifactId>
|
|
||||||
<version>3.0.4</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mongodb</groupId>
|
|
||||||
<artifactId>bson</artifactId>
|
|
||||||
<version>3.0.4</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<!--
|
|
||||||
Due to the use of MongoDB in the test of this pattern, TRAVIS and/or MAVEN might fail if the DB connection is
|
|
||||||
not open for the JUnit test. To avoid disrupting the compilation process, the surefire plug-in was used
|
|
||||||
to SKIP the running of the JUnit tests for this pattern. To ACTIVATE the running of the tests, change the
|
|
||||||
skipTests (below) flag to 'false' and vice-versa.
|
|
||||||
-->
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<version>2.19</version>
|
|
||||||
<configuration>
|
|
||||||
<skipTests>false</skipTests>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
@ -1,117 +0,0 @@
|
|||||||
package com.iluwatar.caching;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing
|
|
||||||
* the resources immediately after their use. The resources retain their identity, are kept in some
|
|
||||||
* fast-access storage, and are re-used to avoid having to acquire them again. There are three main
|
|
||||||
* caching strategies/techniques in this pattern; each with their own pros and cons. They are:
|
|
||||||
* <code>write-through</code> which writes data to the cache and DB in a single transaction,
|
|
||||||
* <code>write-around</code> which writes data immediately into the DB instead of the cache, and
|
|
||||||
* <code>write-behind</code> which writes data into the cache initially whilst the data is only
|
|
||||||
* written into the DB when the cache is full. The <code>read-through</code> strategy is also
|
|
||||||
* included in the mentioned three strategies -- returns data from the cache to the caller <b>if</b>
|
|
||||||
* it exists <b>else</b> queries from DB and stores it into the cache for future use. These
|
|
||||||
* strategies determine when the data in the cache should be written back to the backing store (i.e.
|
|
||||||
* Database) and help keep both data sources synchronized/up-to-date. This pattern can improve
|
|
||||||
* performance and also helps to maintain consistency between data held in the cache and the data in
|
|
||||||
* the underlying data store.
|
|
||||||
* <p>
|
|
||||||
* In this example, the user account ({@link UserAccount}) entity is used as the underlying
|
|
||||||
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts
|
|
||||||
* a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The three
|
|
||||||
* strategies are individually tested. The testing of the cache is restricted towards saving and
|
|
||||||
* querying of user accounts from the underlying data store ( {@link DBManager}). The main class (
|
|
||||||
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
|
|
||||||
* whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
|
|
||||||
* ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
|
|
||||||
* (depending on the preferred caching policy/strategy).
|
|
||||||
*
|
|
||||||
* <i>App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager</i>
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @see CacheStore
|
|
||||||
* @See LRUCache
|
|
||||||
* @see CachingPolicy
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class App {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Program entry point
|
|
||||||
*
|
|
||||||
* @param args command line args
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
AppManager.initDB(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
|
|
||||||
// and the App class to avoid Maven compilation errors. Set flag to
|
|
||||||
// true to run the tests with MongoDB (provided that MongoDB is
|
|
||||||
// installed and socket connection is open).
|
|
||||||
AppManager.initCacheCapacity(3);
|
|
||||||
App app = new App();
|
|
||||||
app.useReadAndWriteThroughStrategy();
|
|
||||||
app.useReadThroughAndWriteAroundStrategy();
|
|
||||||
app.useReadThroughAndWriteBehindStrategy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read-through and write-through
|
|
||||||
*/
|
|
||||||
public void useReadAndWriteThroughStrategy() {
|
|
||||||
System.out.println("# CachingPolicy.THROUGH");
|
|
||||||
AppManager.initCachingPolicy(CachingPolicy.THROUGH);
|
|
||||||
|
|
||||||
UserAccount userAccount1 = new UserAccount("001", "John", "He is a boy.");
|
|
||||||
|
|
||||||
AppManager.save(userAccount1);
|
|
||||||
System.out.println(AppManager.printCacheContent());
|
|
||||||
userAccount1 = AppManager.find("001");
|
|
||||||
userAccount1 = AppManager.find("001");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read-through and write-around
|
|
||||||
*/
|
|
||||||
public void useReadThroughAndWriteAroundStrategy() {
|
|
||||||
System.out.println("# CachingPolicy.AROUND");
|
|
||||||
AppManager.initCachingPolicy(CachingPolicy.AROUND);
|
|
||||||
|
|
||||||
UserAccount userAccount2 = new UserAccount("002", "Jane", "She is a girl.");
|
|
||||||
|
|
||||||
AppManager.save(userAccount2);
|
|
||||||
System.out.println(AppManager.printCacheContent());
|
|
||||||
userAccount2 = AppManager.find("002");
|
|
||||||
System.out.println(AppManager.printCacheContent());
|
|
||||||
userAccount2 = AppManager.find("002");
|
|
||||||
userAccount2.setUserName("Jane G.");
|
|
||||||
AppManager.save(userAccount2);
|
|
||||||
System.out.println(AppManager.printCacheContent());
|
|
||||||
userAccount2 = AppManager.find("002");
|
|
||||||
System.out.println(AppManager.printCacheContent());
|
|
||||||
userAccount2 = AppManager.find("002");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read-through and write-behind
|
|
||||||
*/
|
|
||||||
public void useReadThroughAndWriteBehindStrategy() {
|
|
||||||
System.out.println("# CachingPolicy.BEHIND");
|
|
||||||
AppManager.initCachingPolicy(CachingPolicy.BEHIND);
|
|
||||||
|
|
||||||
UserAccount userAccount3 = new UserAccount("003", "Adam", "He likes food.");
|
|
||||||
UserAccount userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
|
|
||||||
UserAccount userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
|
|
||||||
|
|
||||||
AppManager.save(userAccount3);
|
|
||||||
AppManager.save(userAccount4);
|
|
||||||
AppManager.save(userAccount5);
|
|
||||||
System.out.println(AppManager.printCacheContent());
|
|
||||||
userAccount3 = AppManager.find("003");
|
|
||||||
System.out.println(AppManager.printCacheContent());
|
|
||||||
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
|
|
||||||
AppManager.save(userAccount6);
|
|
||||||
System.out.println(AppManager.printCacheContent());
|
|
||||||
userAccount4 = AppManager.find("004");
|
|
||||||
System.out.println(AppManager.printCacheContent());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
package com.iluwatar.caching;
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* AppManager helps to bridge the gap in communication between the main class and the application's
|
|
||||||
* back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
|
|
||||||
* also initialized here. Before the cache can be used, the size of the cache has to be set.
|
|
||||||
* Depending on the chosen caching policy, AppManager will call the appropriate function in the
|
|
||||||
* CacheStore class.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class AppManager {
|
|
||||||
|
|
||||||
private static CachingPolicy cachingPolicy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Developer/Tester is able to choose whether the application should use MongoDB as its underlying
|
|
||||||
* data storage or a simple Java data structure to (temporarily) store the data/objects during
|
|
||||||
* runtime.
|
|
||||||
*/
|
|
||||||
public static void initDB(boolean useMongoDB) {
|
|
||||||
if (useMongoDB) {
|
|
||||||
try {
|
|
||||||
DBManager.connect();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DBManager.createVirtualDB();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void initCachingPolicy(CachingPolicy policy) {
|
|
||||||
cachingPolicy = policy;
|
|
||||||
if (cachingPolicy == CachingPolicy.BEHIND) {
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
CacheStore.flushCache();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
CacheStore.clearCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void initCacheCapacity(int capacity) {
|
|
||||||
CacheStore.initCapacity(capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UserAccount find(String userID) {
|
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
|
|
||||||
return CacheStore.readThrough(userID);
|
|
||||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
|
||||||
return CacheStore.readThroughWithWriteBackPolicy(userID);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void save(UserAccount userAccount) {
|
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH) {
|
|
||||||
CacheStore.writeThrough(userAccount);
|
|
||||||
} else if (cachingPolicy == CachingPolicy.AROUND) {
|
|
||||||
CacheStore.writeAround(userAccount);
|
|
||||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
|
||||||
CacheStore.writeBehind(userAccount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String printCacheContent() {
|
|
||||||
return CacheStore.print();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
package com.iluwatar.caching;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* The caching strategies are implemented in this class.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class CacheStore {
|
|
||||||
|
|
||||||
static LRUCache cache = null;
|
|
||||||
|
|
||||||
public static void initCapacity(int capacity) {
|
|
||||||
if (null == cache)
|
|
||||||
cache = new LRUCache(capacity);
|
|
||||||
else
|
|
||||||
cache.setCapacity(capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UserAccount readThrough(String userID) {
|
|
||||||
if (cache.contains(userID)) {
|
|
||||||
System.out.println("# Cache Hit!");
|
|
||||||
return cache.get(userID);
|
|
||||||
}
|
|
||||||
System.out.println("# Cache Miss!");
|
|
||||||
UserAccount userAccount = DBManager.readFromDB(userID);
|
|
||||||
cache.set(userID, userAccount);
|
|
||||||
return userAccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeThrough(UserAccount userAccount) {
|
|
||||||
if (cache.contains(userAccount.getUserID())) {
|
|
||||||
DBManager.updateDB(userAccount);
|
|
||||||
} else {
|
|
||||||
DBManager.writeToDB(userAccount);
|
|
||||||
}
|
|
||||||
cache.set(userAccount.getUserID(), userAccount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeAround(UserAccount userAccount) {
|
|
||||||
if (cache.contains(userAccount.getUserID())) {
|
|
||||||
DBManager.updateDB(userAccount);
|
|
||||||
cache.invalidate(userAccount.getUserID()); // Cache data has been updated -- remove older
|
|
||||||
// version from cache.
|
|
||||||
} else {
|
|
||||||
DBManager.writeToDB(userAccount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UserAccount readThroughWithWriteBackPolicy(String userID) {
|
|
||||||
if (cache.contains(userID)) {
|
|
||||||
System.out.println("# Cache Hit!");
|
|
||||||
return cache.get(userID);
|
|
||||||
}
|
|
||||||
System.out.println("# Cache Miss!");
|
|
||||||
UserAccount userAccount = DBManager.readFromDB(userID);
|
|
||||||
if (cache.isFull()) {
|
|
||||||
System.out.println("# Cache is FULL! Writing LRU data to DB...");
|
|
||||||
UserAccount toBeWrittenToDB = cache.getLRUData();
|
|
||||||
DBManager.upsertDB(toBeWrittenToDB);
|
|
||||||
}
|
|
||||||
cache.set(userID, userAccount);
|
|
||||||
return userAccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeBehind(UserAccount userAccount) {
|
|
||||||
if (cache.isFull() && !cache.contains(userAccount.getUserID())) {
|
|
||||||
System.out.println("# Cache is FULL! Writing LRU data to DB...");
|
|
||||||
UserAccount toBeWrittenToDB = cache.getLRUData();
|
|
||||||
DBManager.upsertDB(toBeWrittenToDB);
|
|
||||||
}
|
|
||||||
cache.set(userAccount.getUserID(), userAccount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void clearCache() {
|
|
||||||
if (null != cache)
|
|
||||||
cache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes remaining content in the cache into the DB.
|
|
||||||
*/
|
|
||||||
public static void flushCache() {
|
|
||||||
System.out.println("# flushCache...");
|
|
||||||
if (null == cache)
|
|
||||||
return;
|
|
||||||
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
|
|
||||||
for (UserAccount userAccount : listOfUserAccounts) {
|
|
||||||
DBManager.upsertDB(userAccount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String print() {
|
|
||||||
ArrayList<UserAccount> listOfUserAccounts = cache.getCacheDataInListForm();
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("\n--CACHE CONTENT--\n");
|
|
||||||
for (UserAccount userAccount : listOfUserAccounts) {
|
|
||||||
sb.append(userAccount.toString() + "\n");
|
|
||||||
}
|
|
||||||
sb.append("----\n");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,123 +0,0 @@
|
|||||||
package com.iluwatar.caching;
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import org.bson.Document;
|
|
||||||
|
|
||||||
import com.mongodb.MongoClient;
|
|
||||||
import com.mongodb.client.FindIterable;
|
|
||||||
import com.mongodb.client.MongoDatabase;
|
|
||||||
import com.mongodb.client.model.UpdateOptions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* <p>DBManager handles the communication with the underlying data store i.e. Database. It contains the
|
|
||||||
* implemented methods for querying, inserting, and updating data. MongoDB was used as the database
|
|
||||||
* for the application.</p>
|
|
||||||
*
|
|
||||||
* <p>Developer/Tester is able to choose whether the application should use MongoDB as its underlying
|
|
||||||
* data storage (connect()) or a simple Java data structure to (temporarily) store the data/objects
|
|
||||||
* during runtime (createVirtualDB()).</p>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class DBManager {
|
|
||||||
|
|
||||||
private static MongoClient mongoClient;
|
|
||||||
private static MongoDatabase db;
|
|
||||||
private static boolean useMongoDB;
|
|
||||||
|
|
||||||
private static HashMap<String, UserAccount> virtualDB;
|
|
||||||
|
|
||||||
public static void createVirtualDB() {
|
|
||||||
useMongoDB = false;
|
|
||||||
virtualDB = new HashMap<String, UserAccount>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void connect() throws ParseException {
|
|
||||||
useMongoDB = true;
|
|
||||||
mongoClient = new MongoClient();
|
|
||||||
db = mongoClient.getDatabase("test");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UserAccount readFromDB(String userID) {
|
|
||||||
if (!useMongoDB) {
|
|
||||||
if (virtualDB.containsKey(userID))
|
|
||||||
return virtualDB.get(userID);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (null == db) {
|
|
||||||
try {
|
|
||||||
connect();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FindIterable<Document> iterable =
|
|
||||||
db.getCollection("user_accounts").find(new Document("userID", userID));
|
|
||||||
if (iterable == null)
|
|
||||||
return null;
|
|
||||||
Document doc = iterable.first();
|
|
||||||
UserAccount userAccount =
|
|
||||||
new UserAccount(userID, doc.getString("userName"), doc.getString("additionalInfo"));
|
|
||||||
return userAccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeToDB(UserAccount userAccount) {
|
|
||||||
if (!useMongoDB) {
|
|
||||||
virtualDB.put(userAccount.getUserID(), userAccount);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (null == db) {
|
|
||||||
try {
|
|
||||||
connect();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.getCollection("user_accounts").insertOne(
|
|
||||||
new Document("userID", userAccount.getUserID()).append("userName",
|
|
||||||
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void updateDB(UserAccount userAccount) {
|
|
||||||
if (!useMongoDB) {
|
|
||||||
virtualDB.put(userAccount.getUserID(), userAccount);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (null == db) {
|
|
||||||
try {
|
|
||||||
connect();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.getCollection("user_accounts").updateOne(
|
|
||||||
new Document("userID", userAccount.getUserID()),
|
|
||||||
new Document("$set", new Document("userName", userAccount.getUserName()).append(
|
|
||||||
"additionalInfo", userAccount.getAdditionalInfo())));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Insert data into DB if it does not exist. Else, update it.
|
|
||||||
*/
|
|
||||||
public static void upsertDB(UserAccount userAccount) {
|
|
||||||
if (!useMongoDB) {
|
|
||||||
virtualDB.put(userAccount.getUserID(), userAccount);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (null == db) {
|
|
||||||
try {
|
|
||||||
connect();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.getCollection("user_accounts").updateOne(
|
|
||||||
new Document("userID", userAccount.getUserID()),
|
|
||||||
new Document("$set", new Document("userID", userAccount.getUserID()).append("userName",
|
|
||||||
userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo())),
|
|
||||||
new UpdateOptions().upsert(true));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,146 +0,0 @@
|
|||||||
package com.iluwatar.caching;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Data structure/implementation of the application's cache. The data structure consists of a hash
|
|
||||||
* table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the
|
|
||||||
* LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated,
|
|
||||||
* the data is moved to the front of the list to depict itself as the most-recently-used data. The
|
|
||||||
* LRU data is always at the end of the list.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class LRUCache {
|
|
||||||
|
|
||||||
class Node {
|
|
||||||
String userID;
|
|
||||||
UserAccount userAccount;
|
|
||||||
Node previous;
|
|
||||||
Node next;
|
|
||||||
|
|
||||||
public Node(String userID, UserAccount userAccount) {
|
|
||||||
this.userID = userID;
|
|
||||||
this.userAccount = userAccount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int capacity;
|
|
||||||
HashMap<String, Node> cache = new HashMap<String, Node>();
|
|
||||||
Node head = null;
|
|
||||||
Node end = null;
|
|
||||||
|
|
||||||
public LRUCache(int capacity) {
|
|
||||||
this.capacity = capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserAccount get(String userID) {
|
|
||||||
if (cache.containsKey(userID)) {
|
|
||||||
Node node = cache.get(userID);
|
|
||||||
remove(node);
|
|
||||||
setHead(node);
|
|
||||||
return node.userAccount;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Remove node from linked list.
|
|
||||||
*/
|
|
||||||
public void remove(Node node) {
|
|
||||||
if (node.previous != null) {
|
|
||||||
node.previous.next = node.next;
|
|
||||||
} else {
|
|
||||||
head = node.next;
|
|
||||||
}
|
|
||||||
if (node.next != null) {
|
|
||||||
node.next.previous = node.previous;
|
|
||||||
} else {
|
|
||||||
end = node.previous;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Move node to the front of the list.
|
|
||||||
*/
|
|
||||||
public void setHead(Node node) {
|
|
||||||
node.next = head;
|
|
||||||
node.previous = null;
|
|
||||||
if (head != null)
|
|
||||||
head.previous = node;
|
|
||||||
head = node;
|
|
||||||
if (end == null)
|
|
||||||
end = head;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(String userID, UserAccount userAccount) {
|
|
||||||
if (cache.containsKey(userID)) {
|
|
||||||
Node old = cache.get(userID);
|
|
||||||
old.userAccount = userAccount;
|
|
||||||
remove(old);
|
|
||||||
setHead(old);
|
|
||||||
} else {
|
|
||||||
Node newNode = new Node(userID, userAccount);
|
|
||||||
if (cache.size() >= capacity) {
|
|
||||||
System.out.println("# Cache is FULL! Removing " + end.userID + " from cache...");
|
|
||||||
cache.remove(end.userID); // remove LRU data from cache.
|
|
||||||
remove(end);
|
|
||||||
setHead(newNode);
|
|
||||||
} else {
|
|
||||||
setHead(newNode);
|
|
||||||
}
|
|
||||||
cache.put(userID, newNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean contains(String userID) {
|
|
||||||
return cache.containsKey(userID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidate(String userID) {
|
|
||||||
System.out.println("# " + userID + " has been updated! Removing older version from cache...");
|
|
||||||
Node toBeRemoved = cache.get(userID);
|
|
||||||
remove(toBeRemoved);
|
|
||||||
cache.remove(userID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFull() {
|
|
||||||
return cache.size() >= capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserAccount getLRUData() {
|
|
||||||
return end.userAccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
head = null;
|
|
||||||
end = null;
|
|
||||||
cache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Returns cache data in list form.
|
|
||||||
*/
|
|
||||||
public ArrayList<UserAccount> getCacheDataInListForm() {
|
|
||||||
ArrayList<UserAccount> listOfCacheData = new ArrayList<UserAccount>();
|
|
||||||
Node temp = head;
|
|
||||||
while (temp != null) {
|
|
||||||
listOfCacheData.add(temp.userAccount);
|
|
||||||
temp = temp.next;
|
|
||||||
}
|
|
||||||
return listOfCacheData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCapacity(int newCapacity) {
|
|
||||||
if (capacity > newCapacity) {
|
|
||||||
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll
|
|
||||||
// just clear the cache.
|
|
||||||
} else {
|
|
||||||
this.capacity = newCapacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package com.iluwatar.caching;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Entity class (stored in cache and DB) used in the application.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class UserAccount {
|
|
||||||
private String userID;
|
|
||||||
private String userName;
|
|
||||||
private String additionalInfo;
|
|
||||||
|
|
||||||
public UserAccount(String userID, String userName, String additionalInfo) {
|
|
||||||
this.userID = userID;
|
|
||||||
this.userName = userName;
|
|
||||||
this.additionalInfo = additionalInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserID() {
|
|
||||||
return userID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserID(String userID) {
|
|
||||||
this.userID = userID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserName() {
|
|
||||||
return userName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserName(String userName) {
|
|
||||||
this.userName = userName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAdditionalInfo() {
|
|
||||||
return additionalInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAdditionalInfo(String additionalInfo) {
|
|
||||||
this.additionalInfo = additionalInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return userID + ", " + userName + ", " + additionalInfo;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>callback</artifactId>
|
<artifactId>callback</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -2,9 +2,8 @@ package com.iluwatar.callback;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Callback pattern is more native for functional languages where functions are treated as
|
* Callback pattern is more native for functional languages where functions are treated as first-class citizens.
|
||||||
* first-class citizens. Prior to Java 8 callbacks can be simulated using simple (alike command)
|
* Prior to Java 8 callbacks can be simulated using simple (alike command) interfaces.
|
||||||
* interfaces.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
@ -9,6 +9,7 @@ public class SimpleTask extends Task {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
System.out.println("Perform some important activity and after call the callback method.");
|
System.out.println("Perform some important activity.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,38 +2,18 @@ package com.iluwatar.callback;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import com.iluwatar.callback.App;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a field as a counter. Every time the callback method is called increment this field. Unit
|
|
||||||
* test checks that the field is being incremented.
|
|
||||||
*
|
*
|
||||||
* Could be done with mock objects as well where the call method call is verified.
|
* Application test
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public class AppTest {
|
public class AppTest {
|
||||||
|
|
||||||
private Integer callingCount = 0;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() {
|
public void test() {
|
||||||
Callback callback = new Callback() {
|
String[] args = {};
|
||||||
@Override
|
App.main(args);
|
||||||
public void call() {
|
|
||||||
callingCount++;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Task task = new SimpleTask();
|
|
||||||
|
|
||||||
assertEquals("Initial calling count of 0", new Integer(0), callingCount);
|
|
||||||
|
|
||||||
task.executeWith(callback);
|
|
||||||
|
|
||||||
assertEquals("Callback called once", new Integer(1), callingCount);
|
|
||||||
|
|
||||||
task.executeWith(callback);
|
|
||||||
|
|
||||||
assertEquals("Callback called twice", new Integer(2), callingCount);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ title: Chain of responsibility
|
|||||||
folder: chain
|
folder: chain
|
||||||
permalink: /patterns/chain/
|
permalink: /patterns/chain/
|
||||||
categories: Behavioral
|
categories: Behavioral
|
||||||
tags:
|
tags: Java
|
||||||
- Java
|
|
||||||
- Gang Of Four
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Avoid coupling the sender of a request to its receiver by giving
|
**Intent:** Avoid coupling the sender of a request to its receiver by giving
|
||||||
@ -25,7 +23,3 @@ objects and pass the request along the chain until an object handles it.
|
|||||||
|
|
||||||
* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29)
|
* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29)
|
||||||
* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html)
|
* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html)
|
||||||
|
|
||||||
**Credits**
|
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>chain</artifactId>
|
<artifactId>chain</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -2,29 +2,24 @@ package com.iluwatar.chain;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* The Chain of Responsibility pattern is a design pattern consisting of command objects and a
|
* Chain of Responsibility organizes request handlers ({@link RequestHandler}) into a
|
||||||
* series of processing objects. Each processing object contains logic that defines the types of
|
* chain where each handler has a chance to act on the request on its turn. In
|
||||||
* command objects that it can handle; the rest are passed to the next processing object in the
|
* this example the king ({@link OrcKing}) makes requests and the military orcs
|
||||||
* chain. A mechanism also exists for adding new processing objects to the end of this chain.
|
* ({@link OrcCommander}, {@link OrcOfficer}, {@link OrcSoldier}) form the handler chain.
|
||||||
* <p>
|
|
||||||
* In this example we organize the request handlers ({@link RequestHandler}) into a chain where each
|
|
||||||
* handler has a chance to act on the request on its turn. Here the king ({@link OrcKing}) makes
|
|
||||||
* requests and the military orcs ({@link OrcCommander}, {@link OrcOfficer}, {@link OrcSoldier})
|
|
||||||
* form the handler chain.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point
|
* Program entry point
|
||||||
*
|
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
OrcKing king = new OrcKing();
|
OrcKing king = new OrcKing();
|
||||||
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle"));
|
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle"));
|
||||||
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner"));
|
king.makeRequest(new Request(RequestType.TORTURE_PRISONER,
|
||||||
|
"torture prisoner"));
|
||||||
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax"));
|
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,9 @@ title: Command
|
|||||||
folder: command
|
folder: command
|
||||||
permalink: /patterns/command/
|
permalink: /patterns/command/
|
||||||
categories: Behavioral
|
categories: Behavioral
|
||||||
tags:
|
tags: Java
|
||||||
- Java
|
|
||||||
- Gang Of Four
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Also known as:** Action, Transaction
|
|
||||||
|
|
||||||
**Intent:** Encapsulate a request as an object, thereby letting you
|
**Intent:** Encapsulate a request as an object, thereby letting you
|
||||||
parameterize clients with different requests, queue or log requests, and
|
parameterize clients with different requests, queue or log requests, and
|
||||||
support undoable operations.
|
support undoable operations.
|
||||||
@ -34,7 +30,3 @@ support undoable operations.
|
|||||||
**Real world examples:**
|
**Real world examples:**
|
||||||
|
|
||||||
* [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)
|
* [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)
|
||||||
|
|
||||||
**Credits**
|
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>command</artifactId>
|
<artifactId>command</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -14,10 +15,5 @@
|
|||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.mockito</groupId>
|
|
||||||
<artifactId>mockito-core</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -2,22 +2,19 @@ package com.iluwatar.command;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* The Command pattern is a behavioral design pattern in which an object is used to encapsulate all
|
* In Command pattern actions are objects that can be executed and undone.
|
||||||
* information needed to perform an action or trigger an event at a later time. This information
|
|
||||||
* includes the method name, the object that owns the method and values for the method parameters.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Four terms always associated with the command pattern are command, receiver, invoker and client.
|
* Four terms always associated with the command pattern are command, receiver, invoker and client. A command
|
||||||
* A command object (spell) knows about the receiver (target) and invokes a method of the receiver.
|
* object (spell) knows about the receiver (target) and invokes a method of the receiver. Values for parameters of
|
||||||
* Values for parameters of the receiver method are stored in the command. The receiver then does
|
* the receiver method are stored in the command. The receiver then does the work. An invoker object (wizard)
|
||||||
* the work. An invoker object (wizard) knows how to execute a command, and optionally does
|
* knows how to execute a command, and optionally does bookkeeping about the command execution. The invoker
|
||||||
* bookkeeping about the command execution. The invoker does not know anything about a concrete
|
* does not know anything about a concrete command, it knows only about command interface. Both an invoker object
|
||||||
* command, it knows only about command interface. Both an invoker object and several command
|
* and several command objects are held by a client object (app). The client decides which commands to execute at
|
||||||
* objects are held by a client object (app). The client decides which commands to execute at which
|
* which points. To execute a command, it passes the command object to the invoker object.
|
||||||
* points. To execute a command, it passes the command object to the invoker object.
|
|
||||||
* <p>
|
* <p>
|
||||||
* In other words, in this example the wizard casts spells on the goblin. The wizard keeps track of
|
* In other words, in this example the wizard casts spells on the goblin. The wizard keeps track of the previous
|
||||||
* the previous spells cast, so it is easy to undo them. In addition, the wizard keeps track of the
|
* spells cast, so it is easy to undo them. In addition, the wizard keeps track of the spells undone, so they
|
||||||
* spells undone, so they can be redone.
|
* can be redone.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -25,7 +22,6 @@ public class App {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point
|
* Program entry point
|
||||||
*
|
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -31,8 +31,8 @@ public abstract class Target {
|
|||||||
public abstract String toString();
|
public abstract String toString();
|
||||||
|
|
||||||
public void printStatus() {
|
public void printStatus() {
|
||||||
System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, getSize(),
|
System.out.println(String.format("%s, [size=%s] [visibility=%s]", this,
|
||||||
getVisibility()));
|
getSize(), getVisibility()));
|
||||||
System.out.println();
|
System.out.println();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@ public class Wizard {
|
|||||||
private Deque<Command> undoStack = new LinkedList<>();
|
private Deque<Command> undoStack = new LinkedList<>();
|
||||||
private Deque<Command> redoStack = new LinkedList<>();
|
private Deque<Command> redoStack = new LinkedList<>();
|
||||||
|
|
||||||
public Wizard() {}
|
public Wizard() {
|
||||||
|
}
|
||||||
|
|
||||||
public void castSpell(Command command, Target target) {
|
public void castSpell(Command command, Target target) {
|
||||||
System.out.println(this + " casts " + command + " at " + target);
|
System.out.println(this + " casts " + command + " at " + target);
|
||||||
|
19
command/src/test/java/com/iluwatar/command/AppTest.java
Normal file
19
command/src/test/java/com/iluwatar/command/AppTest.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.iluwatar.command;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.iluwatar.command.App;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Application test
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AppTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
String[] args = {};
|
||||||
|
App.main(args);
|
||||||
|
}
|
||||||
|
}
|
@ -1,71 +0,0 @@
|
|||||||
package com.iluwatar.command;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Command pattern is a behavioral design pattern in which an object is used to encapsulate all
|
|
||||||
* information needed to perform an action or trigger an event at a later time. This information
|
|
||||||
* includes the method name, the object that owns the method and values for the method parameters.
|
|
||||||
*
|
|
||||||
* <p>Four terms always associated with the command pattern are command, receiver, invoker and
|
|
||||||
* client. A command object (spell) knows about the receiver (target) and invokes a method of
|
|
||||||
* the receiver.Values for parameters of the receiver method are stored in the command. The receiver
|
|
||||||
* then does the work. An invoker object (wizard) knows how to execute a command, and optionally
|
|
||||||
* does bookkeeping about the command execution. The invoker does not know anything about a
|
|
||||||
* concrete command, it knows only about command interface. Both an invoker object and several
|
|
||||||
* command objects are held by a client object (app). The client decides which commands to execute
|
|
||||||
* at which points. To execute a command, it passes the command object to the invoker object.
|
|
||||||
*/
|
|
||||||
public class CommandTest {
|
|
||||||
|
|
||||||
private static final String GOBLIN = "Goblin";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This test verifies that when the wizard casts spells on the goblin. The wizard keeps track of
|
|
||||||
* the previous spells cast, so it is easy to undo them. In addition, it also verifies that the
|
|
||||||
* wizard keeps track of the spells undone, so they can be redone.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCommand() {
|
|
||||||
|
|
||||||
Wizard wizard = new Wizard();
|
|
||||||
Goblin goblin = new Goblin();
|
|
||||||
|
|
||||||
wizard.castSpell(new ShrinkSpell(), goblin);
|
|
||||||
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE);
|
|
||||||
|
|
||||||
wizard.castSpell(new InvisibilitySpell(), goblin);
|
|
||||||
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE);
|
|
||||||
|
|
||||||
wizard.undoLastSpell();
|
|
||||||
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE);
|
|
||||||
|
|
||||||
wizard.undoLastSpell();
|
|
||||||
verifyGoblin(goblin, GOBLIN, Size.NORMAL, Visibility.VISIBLE);
|
|
||||||
|
|
||||||
wizard.redoLastSpell();
|
|
||||||
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE);
|
|
||||||
|
|
||||||
wizard.redoLastSpell();
|
|
||||||
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method asserts that the passed goblin object has the name as expectedName, size as
|
|
||||||
* expectedSize and visibility as expectedVisibility.
|
|
||||||
*
|
|
||||||
* @param goblin a goblin object whose state is to be verified against other parameters
|
|
||||||
* @param expectedName expectedName of the goblin
|
|
||||||
* @param expectedSize expected size of the goblin
|
|
||||||
* @param expectedVisibilty exepcted visibility of the goblin
|
|
||||||
*/
|
|
||||||
private void verifyGoblin(Goblin goblin, String expectedName, Size expectedSize,
|
|
||||||
Visibility expectedVisibilty) {
|
|
||||||
assertEquals("Goblin's name must be same as expectedName", expectedName, goblin.toString());
|
|
||||||
assertEquals("Goblin's size must be same as expectedSize", expectedSize, goblin.getSize());
|
|
||||||
assertEquals("Goblin's visibility must be same as expectedVisibility", expectedVisibilty,
|
|
||||||
goblin.getVisibility());
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,9 +4,7 @@ title: Composite
|
|||||||
folder: composite
|
folder: composite
|
||||||
permalink: /patterns/composite/
|
permalink: /patterns/composite/
|
||||||
categories: Structural
|
categories: Structural
|
||||||
tags:
|
tags: Java
|
||||||
- Java
|
|
||||||
- Gang Of Four
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Compose objects into tree structures to represent part-whole
|
**Intent:** Compose objects into tree structures to represent part-whole
|
||||||
@ -24,7 +22,3 @@ of objects uniformly.
|
|||||||
|
|
||||||
* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html)
|
* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html)
|
||||||
* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java)
|
* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java)
|
||||||
|
|
||||||
**Credits**
|
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>composite</artifactId>
|
<artifactId>composite</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -1,21 +1,16 @@
|
|||||||
package com.iluwatar.composite;
|
package com.iluwatar.composite;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Composite pattern is a partitioning design pattern. The Composite pattern describes that a
|
*
|
||||||
* group of objects is to be treated in the same way as a single instance of an object. The intent
|
* With Composite we can treat tree hierarchies of objects with uniform
|
||||||
* of a composite is to "compose" objects into tree structures to represent part-whole hierarchies.
|
* interface ({@link LetterComposite}). In this example we have sentences composed of
|
||||||
* Implementing the Composite pattern lets clients treat individual objects and compositions
|
* words composed of letters.
|
||||||
* uniformly.
|
|
||||||
* <p>
|
|
||||||
* In this example we have sentences composed of words composed of letters. All of the objects can
|
|
||||||
* be treated through the same interface ({@link LetterComposite}).
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point
|
* Program entry point
|
||||||
*
|
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -22,4 +22,5 @@ public class Letter extends LetterComposite {
|
|||||||
protected void printThisAfter() {
|
protected void printThisAfter() {
|
||||||
// nop
|
// nop
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,19 +15,20 @@ public class Messenger {
|
|||||||
|
|
||||||
List<Word> words = new ArrayList<Word>();
|
List<Word> words = new ArrayList<Word>();
|
||||||
|
|
||||||
words.add(new Word(Arrays.asList(new Letter('W'), new Letter('h'), new Letter('e'), new Letter(
|
words.add(new Word(Arrays.asList(new Letter('W'), new Letter('h'),
|
||||||
'r'), new Letter('e'))));
|
new Letter('e'), new Letter('r'), new Letter('e'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter(
|
words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'),
|
||||||
'r'), new Letter('e'))));
|
new Letter('e'), new Letter('r'), new Letter('e'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s'))));
|
words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('a'))));
|
words.add(new Word(Arrays.asList(new Letter('a'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('h'), new Letter('i'), new Letter(
|
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('h'),
|
||||||
'p'))));
|
new Letter('i'), new Letter('p'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter(
|
words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'),
|
||||||
'r'), new Letter('e'))));
|
new Letter('e'), new Letter('r'), new Letter('e'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s'))));
|
words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('a'))));
|
words.add(new Word(Arrays.asList(new Letter('a'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('a'), new Letter('y'))));
|
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('a'),
|
||||||
|
new Letter('y'))));
|
||||||
|
|
||||||
return new Sentence(words);
|
return new Sentence(words);
|
||||||
|
|
||||||
@ -37,18 +38,18 @@ public class Messenger {
|
|||||||
|
|
||||||
List<Word> words = new ArrayList<Word>();
|
List<Word> words = new ArrayList<Word>();
|
||||||
|
|
||||||
words.add(new Word(Arrays.asList(new Letter('M'), new Letter('u'), new Letter('c'), new Letter(
|
words.add(new Word(Arrays.asList(new Letter('M'), new Letter('u'),
|
||||||
'h'))));
|
new Letter('c'), new Letter('h'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('i'), new Letter('n'), new Letter(
|
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('i'),
|
||||||
'd'))));
|
new Letter('n'), new Letter('d'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('p'), new Letter('o'), new Letter('u'), new Letter(
|
words.add(new Word(Arrays.asList(new Letter('p'), new Letter('o'),
|
||||||
'r'), new Letter('s'))));
|
new Letter('u'), new Letter('r'), new Letter('s'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('f'), new Letter('r'), new Letter('o'), new Letter(
|
words.add(new Word(Arrays.asList(new Letter('f'), new Letter('r'),
|
||||||
'm'))));
|
new Letter('o'), new Letter('m'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('y'), new Letter('o'), new Letter('u'), new Letter(
|
words.add(new Word(Arrays.asList(new Letter('y'), new Letter('o'),
|
||||||
'r'))));
|
new Letter('u'), new Letter('r'))));
|
||||||
words.add(new Word(Arrays.asList(new Letter('m'), new Letter('o'), new Letter('u'), new Letter(
|
words.add(new Word(Arrays.asList(new Letter('m'), new Letter('o'),
|
||||||
't'), new Letter('h'))));
|
new Letter('u'), new Letter('t'), new Letter('h'))));
|
||||||
|
|
||||||
return new Sentence(words);
|
return new Sentence(words);
|
||||||
|
|
||||||
|
@ -24,4 +24,5 @@ public class Sentence extends LetterComposite {
|
|||||||
protected void printThisAfter() {
|
protected void printThisAfter() {
|
||||||
System.out.print(".");
|
System.out.print(".");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,4 +24,5 @@ public class Word extends LetterComposite {
|
|||||||
protected void printThisAfter() {
|
protected void printThisAfter() {
|
||||||
// nop
|
// nop
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ title: Data Access Object
|
|||||||
folder: dao
|
folder: dao
|
||||||
permalink: /patterns/dao/
|
permalink: /patterns/dao/
|
||||||
categories: Architectural
|
categories: Architectural
|
||||||
tags:
|
tags: Java
|
||||||
- Java
|
|
||||||
- Difficulty-Beginner
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Intent:** Object provides an abstract interface to some type of database or
|
**Intent:** Object provides an abstract interface to some type of database or
|
||||||
@ -18,7 +16,3 @@ other persistence mechanism.
|
|||||||
|
|
||||||
* when you want to consolidate how the data layer is accessed
|
* when you want to consolidate how the data layer is accessed
|
||||||
* when you want to avoid writing multiple data retrieval/persistence layers
|
* when you want to avoid writing multiple data retrieval/persistence layers
|
||||||
|
|
||||||
**Credits:**
|
|
||||||
|
|
||||||
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
|
|
||||||
|
52
dao/pom.xml
52
dao/pom.xml
@ -6,64 +6,14 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>dao</artifactId>
|
<artifactId>dao</artifactId>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>log4j</groupId>
|
|
||||||
<artifactId>log4j</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
log4j.xml file will be copied both in ${project.build.outputDirectory}
|
|
||||||
and ${project.build.directory}. Thanks to Sean Patrick Floyd
|
|
||||||
(http://stackoverflow.com/questions/5637532/maven-how-to-place-resource-file-together-with-jar)
|
|
||||||
-->
|
|
||||||
|
|
||||||
<resources>
|
|
||||||
<resource> <!-- regular processing for every resource file -->
|
|
||||||
<directory>src/main/resources</directory>
|
|
||||||
</resource>
|
|
||||||
<resource> <!-- processing with a different output directory for log4j.xml -->
|
|
||||||
<directory>src/main/resources</directory>
|
|
||||||
<includes>
|
|
||||||
<include>log4j.xml</include>
|
|
||||||
</includes>
|
|
||||||
<targetPath>..</targetPath> <!-- relative to target/classes i.e. ${project.build.directory} -->
|
|
||||||
</resource>
|
|
||||||
</resources>
|
|
||||||
|
|
||||||
<plugins>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
This will exclude log4j.xml file from generated JAR
|
|
||||||
-->
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
|
||||||
<version>2.6</version>
|
|
||||||
<configuration>
|
|
||||||
<excludes>
|
|
||||||
<exclude>log4j.xml</exclude>
|
|
||||||
</excludes>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -3,56 +3,51 @@ package com.iluwatar.dao;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Data Access Object (DAO) is an object that provides an abstract interface to some type of
|
* With the DAO pattern, we can use various method calls to retrieve/add/delete/update data without directly
|
||||||
* database or other persistence mechanism. By mapping application calls to the persistence layer,
|
* interacting with the data. The below example demonstrates basic operations(CRUD): select, add, update, and delete.
|
||||||
* DAO provide some specific data operations without exposing details of the database. This
|
|
||||||
* isolation supports the Single responsibility principle. It separates what data accesses the
|
|
||||||
* application needs, in terms of domain-specific objects and data types (the public interface of
|
|
||||||
* the DAO), from how these needs can be satisfied with a specific DBMS.
|
|
||||||
* <p>
|
|
||||||
* With the DAO pattern, we can use various method calls to retrieve/add/delete/update data without
|
|
||||||
* directly interacting with the data. The below example demonstrates basic CRUD operations: select,
|
|
||||||
* add, update, and delete.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
private static Logger LOGGER = Logger.getLogger(App.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point.
|
* Program entry point
|
||||||
*
|
* @param args command line args
|
||||||
* @param args command line args.
|
|
||||||
*/
|
*/
|
||||||
public static void main(final String[] args) {
|
public static void main(String[] args) {
|
||||||
final CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers());
|
|
||||||
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers());
|
||||||
LOGGER.info("customerDao.getCusterById(2): " + customerDao.getCustomerById(2));
|
|
||||||
final Customer customer = new Customer(4, "Dan", "Danson");
|
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
||||||
|
System.out.println("customerDao.getCusterById(2): " + customerDao.getCusterById(2));
|
||||||
|
|
||||||
|
Customer customer = new Customer(4, "Dan", "Danson");
|
||||||
customerDao.addCustomer(customer);
|
customerDao.addCustomer(customer);
|
||||||
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
|
||||||
|
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
||||||
|
|
||||||
customer.setFirstName("Daniel");
|
customer.setFirstName("Daniel");
|
||||||
customer.setLastName("Danielson");
|
customer.setLastName("Danielson");
|
||||||
customerDao.updateCustomer(customer);
|
customerDao.updateCustomer(customer);
|
||||||
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
|
||||||
|
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
||||||
|
|
||||||
customerDao.deleteCustomer(customer);
|
customerDao.deleteCustomer(customer);
|
||||||
LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
|
||||||
|
System.out.println("customerDao.getAllCustomers(): " + customerDao.getAllCustomers());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate customers.
|
* Generate customers
|
||||||
*
|
* @return list of customers
|
||||||
* @return list of customers.
|
|
||||||
*/
|
*/
|
||||||
public static List<Customer> generateSampleCustomers() {
|
public static List<Customer> generateSampleCustomers() {
|
||||||
final Customer customer1 = new Customer(1, "Adam", "Adamson");
|
Customer customer1 = new Customer(1, "Adam", "Adamson");
|
||||||
final Customer customer2 = new Customer(2, "Bob", "Bobson");
|
Customer customer2 = new Customer(2, "Bob", "Bobson");
|
||||||
final Customer customer3 = new Customer(3, "Carl", "Carlson");
|
Customer customer3 = new Customer(3, "Carl", "Carlson");
|
||||||
final List<Customer> customers = new ArrayList<Customer>();
|
|
||||||
|
List<Customer> customers = new ArrayList<Customer>();
|
||||||
customers.add(customer1);
|
customers.add(customer1);
|
||||||
customers.add(customer2);
|
customers.add(customer2);
|
||||||
customers.add(customer3);
|
customers.add(customer3);
|
||||||
|
@ -11,7 +11,7 @@ public class Customer {
|
|||||||
private String firstName;
|
private String firstName;
|
||||||
private String lastName;
|
private String lastName;
|
||||||
|
|
||||||
public Customer(final int id, final String firstName, final String lastName) {
|
public Customer(int id, String firstName, String lastName) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.firstName = firstName;
|
this.firstName = firstName;
|
||||||
this.lastName = lastName;
|
this.lastName = lastName;
|
||||||
@ -21,7 +21,7 @@ public class Customer {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(final int id) {
|
public void setId(int id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ public class Customer {
|
|||||||
return firstName;
|
return firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFirstName(final String firstName) {
|
public void setFirstName(String firstName) {
|
||||||
this.firstName = firstName;
|
this.firstName = firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,32 +37,34 @@ public class Customer {
|
|||||||
return lastName;
|
return lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLastName(final String lastName) {
|
public void setLastName(String lastName) {
|
||||||
this.lastName = lastName;
|
this.lastName = lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Customer{" + "id=" + getId() + ", firstName='" + getFirstName() + '\'' + ", lastName='"
|
return "Customer{" +
|
||||||
+ getLastName() + '\'' + '}';
|
"id=" + id +
|
||||||
|
", firstName='" + firstName + '\'' +
|
||||||
|
", lastName='" + lastName + '\'' +
|
||||||
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object o) {
|
public boolean equals(Object o) {
|
||||||
boolean isEqual = false;
|
if (this == o) return true;
|
||||||
if (this == o) {
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
isEqual = true;
|
|
||||||
} else if (o != null && (getClass() == o.getClass())) {
|
Customer customer = (Customer) o;
|
||||||
final Customer customer = (Customer) o;
|
|
||||||
if (getId() == customer.getId())
|
if (id != customer.id) return false;
|
||||||
isEqual = true;
|
|
||||||
}
|
return true;
|
||||||
return isEqual;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = getId();
|
int result = id;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,13 +9,9 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface CustomerDao {
|
public interface CustomerDao {
|
||||||
|
|
||||||
List<Customer> getAllCustomers();
|
public List<Customer> getAllCustomers();
|
||||||
|
public Customer getCusterById(int id);
|
||||||
Customer getCustomerById(int id);
|
public void addCustomer(Customer customer);
|
||||||
|
public void updateCustomer(Customer customer);
|
||||||
void addCustomer(Customer customer);
|
public void deleteCustomer(Customer customer);
|
||||||
|
|
||||||
void updateCustomer(Customer customer);
|
|
||||||
|
|
||||||
void deleteCustomer(Customer customer);
|
|
||||||
}
|
}
|
@ -4,12 +4,11 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* The data access object (DAO) is an object that provides an abstract interface to some type of
|
* The data access object (DAO) is an object that provides an abstract interface to some type of database or other persistence mechanism.
|
||||||
* database or other persistence mechanism. By mapping application calls to the persistence layer,
|
* By mapping application calls to the persistence layer, DAO provide some specific data operations without exposing details of the database.
|
||||||
* DAO provide some specific data operations without exposing details of the database. This
|
* This isolation supports the Single responsibility principle. It separates what data accesses the application needs, in terms of
|
||||||
* isolation supports the Single responsibility principle. It separates what data accesses the
|
* domain-specific objects and data types (the public interface of the DAO), from how these needs can be satisfied with a specific DBMS,
|
||||||
* application needs, in terms of domain-specific objects and data types (the public interface of
|
* database schema, etc.
|
||||||
* the DAO), from how these needs can be satisfied with a specific DBMS, database schema, etc.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class CustomerDaoImpl implements CustomerDao {
|
public class CustomerDaoImpl implements CustomerDao {
|
||||||
@ -18,7 +17,7 @@ public class CustomerDaoImpl implements CustomerDao {
|
|||||||
// Note: Normally this would be in the form of an actual database and not part of the Dao Impl.
|
// Note: Normally this would be in the form of an actual database and not part of the Dao Impl.
|
||||||
private List<Customer> customers;
|
private List<Customer> customers;
|
||||||
|
|
||||||
public CustomerDaoImpl(final List<Customer> customers) {
|
public CustomerDaoImpl(List<Customer> customers) {
|
||||||
this.customers = customers;
|
this.customers = customers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,35 +27,31 @@ public class CustomerDaoImpl implements CustomerDao {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Customer getCustomerById(final int id) {
|
public Customer getCusterById(int id) {
|
||||||
Customer customer = null;
|
for (int i = 0; i < customers.size(); i++) {
|
||||||
for (final Customer cus : getAllCustomers()) {
|
if (customers.get(i).getId() == id) {
|
||||||
if (cus.getId() == id) {
|
return customers.get(i);
|
||||||
customer = cus;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return customer;
|
// No customer found
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCustomer(final Customer customer) {
|
public void addCustomer(Customer customer) {
|
||||||
if (getCustomerById(customer.getId()) == null) {
|
|
||||||
customers.add(customer);
|
customers.add(customer);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateCustomer(final Customer customer) {
|
public void updateCustomer(Customer customer) {
|
||||||
if (getAllCustomers().contains(customer)) {
|
if (customers.contains(customer)) {
|
||||||
final int index = getAllCustomers().indexOf(customer);
|
customers.set(customers.indexOf(customer), customer);
|
||||||
getAllCustomers().set(index, customer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteCustomer(final Customer customer) {
|
public void deleteCustomer(Customer customer) {
|
||||||
getAllCustomers().remove(customer);
|
customers.remove(customer);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
|
||||||
<log4j:configuration debug="true"
|
|
||||||
xmlns:log4j='http://jakarta.apache.org/log4j/'>
|
|
||||||
|
|
||||||
<appender name="console" class="org.apache.log4j.ConsoleAppender">
|
|
||||||
<layout class="org.apache.log4j.PatternLayout">
|
|
||||||
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
|
|
||||||
</layout>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<root>
|
|
||||||
<level value="INFO" />
|
|
||||||
<appender-ref ref="console" />
|
|
||||||
</root>
|
|
||||||
|
|
||||||
</log4j:configuration>
|
|
19
dao/src/test/java/com/iluwatar/dao/AppTest.java
Normal file
19
dao/src/test/java/com/iluwatar/dao/AppTest.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.iluwatar.dao;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.iluwatar.dao.App;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Application test
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AppTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
String[] args = {};
|
||||||
|
App.main(args);
|
||||||
|
}
|
||||||
|
}
|
@ -1,99 +0,0 @@
|
|||||||
package com.iluwatar.dao;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class CustomerDaoImplTest {
|
|
||||||
|
|
||||||
private CustomerDaoImpl impl;
|
|
||||||
private List<Customer> customers;
|
|
||||||
private static final Customer CUSTOMER = new Customer(1, "Freddy", "Krueger");
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
customers = new ArrayList<Customer>();
|
|
||||||
customers.add(CUSTOMER);
|
|
||||||
impl = new CustomerDaoImpl(customers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deleteExistingCustomer() {
|
|
||||||
assertEquals(1, impl.getAllCustomers().size());
|
|
||||||
impl.deleteCustomer(CUSTOMER);
|
|
||||||
assertTrue(impl.getAllCustomers().isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deleteNonExistingCustomer() {
|
|
||||||
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
|
|
||||||
impl.deleteCustomer(nonExistingCustomer);
|
|
||||||
assertEquals(1, impl.getAllCustomers().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void updateExistingCustomer() {
|
|
||||||
final String newFirstname = "Bernard";
|
|
||||||
final String newLastname = "Montgomery";
|
|
||||||
final Customer customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname);
|
|
||||||
impl.updateCustomer(customer);
|
|
||||||
final Customer cust = impl.getCustomerById(CUSTOMER.getId());
|
|
||||||
assertEquals(newFirstname, cust.getFirstName());
|
|
||||||
assertEquals(newLastname, cust.getLastName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void updateNonExistingCustomer() {
|
|
||||||
final int nonExistingId = getNonExistingCustomerId();
|
|
||||||
final String newFirstname = "Douglas";
|
|
||||||
final String newLastname = "MacArthur";
|
|
||||||
final Customer customer = new Customer(nonExistingId, newFirstname, newLastname);
|
|
||||||
impl.updateCustomer(customer);
|
|
||||||
assertNull(impl.getCustomerById(nonExistingId));
|
|
||||||
final Customer existingCustomer = impl.getCustomerById(CUSTOMER.getId());
|
|
||||||
assertEquals(CUSTOMER.getFirstName(), existingCustomer.getFirstName());
|
|
||||||
assertEquals(CUSTOMER.getLastName(), existingCustomer.getLastName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addCustomer() {
|
|
||||||
final Customer newCustomer = new Customer(3, "George", "Patton");
|
|
||||||
impl.addCustomer(newCustomer);
|
|
||||||
assertEquals(2, impl.getAllCustomers().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addAlreadyAddedCustomer() {
|
|
||||||
final Customer newCustomer = new Customer(3, "George", "Patton");
|
|
||||||
impl.addCustomer(newCustomer);
|
|
||||||
assertEquals(2, impl.getAllCustomers().size());
|
|
||||||
impl.addCustomer(newCustomer);
|
|
||||||
assertEquals(2, impl.getAllCustomers().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getExistinCustomerById() {
|
|
||||||
assertEquals(CUSTOMER, impl.getCustomerById(CUSTOMER.getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getNonExistinCustomerById() {
|
|
||||||
final int nonExistingId = getNonExistingCustomerId();
|
|
||||||
assertNull(impl.getCustomerById(nonExistingId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An arbitrary number which does not correspond to an active Customer id.
|
|
||||||
*
|
|
||||||
* @return an int of a customer id which doesn't exist
|
|
||||||
*/
|
|
||||||
private int getNonExistingCustomerId() {
|
|
||||||
return 999;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
package com.iluwatar.dao;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class CustomerTest {
|
|
||||||
|
|
||||||
private Customer customer;
|
|
||||||
private static final int ID = 1;
|
|
||||||
private static final String FIRSTNAME = "Winston";
|
|
||||||
private static final String LASTNAME = "Churchill";
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
customer = new Customer(ID, FIRSTNAME, LASTNAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getAndSetId() {
|
|
||||||
final int newId = 2;
|
|
||||||
customer.setId(newId);
|
|
||||||
assertEquals(newId, customer.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getAndSetFirstname() {
|
|
||||||
final String newFirstname = "Bill";
|
|
||||||
customer.setFirstName(newFirstname);
|
|
||||||
assertEquals(newFirstname, customer.getFirstName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getAndSetLastname() {
|
|
||||||
final String newLastname = "Clinton";
|
|
||||||
customer.setLastName(newLastname);
|
|
||||||
assertEquals(newLastname, customer.getLastName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void notEqualWithDifferentId() {
|
|
||||||
final int newId = 2;
|
|
||||||
final Customer otherCustomer = new Customer(newId, FIRSTNAME, LASTNAME);
|
|
||||||
assertNotEquals(customer, otherCustomer);
|
|
||||||
assertNotEquals(customer.hashCode(), otherCustomer.hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsWithSameObjectValues() {
|
|
||||||
final Customer otherCustomer = new Customer(ID, FIRSTNAME, LASTNAME);
|
|
||||||
assertEquals(customer, otherCustomer);
|
|
||||||
assertEquals(customer.hashCode(), otherCustomer.hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsWithSameObjects() {
|
|
||||||
assertEquals(customer, customer);
|
|
||||||
assertEquals(customer.hashCode(), customer.hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testToString() {
|
|
||||||
final StringBuffer buffer = new StringBuffer();
|
|
||||||
buffer.append("Customer{id=");
|
|
||||||
buffer.append("" + customer.getId());
|
|
||||||
buffer.append(", firstName='");
|
|
||||||
buffer.append(customer.getFirstName());
|
|
||||||
buffer.append("\', lastName='");
|
|
||||||
buffer.append(customer.getLastName() + "\'}");
|
|
||||||
assertEquals(buffer.toString(), customer.toString());
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,13 +4,9 @@ title: Decorator
|
|||||||
folder: decorator
|
folder: decorator
|
||||||
permalink: /patterns/decorator/
|
permalink: /patterns/decorator/
|
||||||
categories: Structural
|
categories: Structural
|
||||||
tags:
|
tags: Java
|
||||||
- Java
|
|
||||||
- Gang Of Four
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Also known as:** Wrapper
|
|
||||||
|
|
||||||
**Intent:** Attach additional responsibilities to an object dynamically.
|
**Intent:** Attach additional responsibilities to an object dynamically.
|
||||||
Decorators provide a flexible alternative to subclassing for extending
|
Decorators provide a flexible alternative to subclassing for extending
|
||||||
functionality.
|
functionality.
|
||||||
@ -22,7 +18,3 @@ functionality.
|
|||||||
* to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects
|
* to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects
|
||||||
* for responsibilities that can be withdrawn
|
* for responsibilities that can be withdrawn
|
||||||
* when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing
|
* when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing
|
||||||
|
|
||||||
**Credits**
|
|
||||||
|
|
||||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>decorator</artifactId>
|
<artifactId>decorator</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -2,21 +2,18 @@ package com.iluwatar.decorator;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* The Decorator pattern is a more flexible alternative to subclassing. The Decorator class
|
* Decorator pattern is a more flexible alternative to subclassing. The decorator
|
||||||
* implements the same interface as the target and uses composition to "decorate" calls to the
|
* class implements the same interface as the target and uses composition to
|
||||||
* target. Using the Decorator pattern it is possible to change the behavior of the class during
|
* "decorate" calls to the target.
|
||||||
* runtime.
|
|
||||||
* <p>
|
* <p>
|
||||||
* In this example we show how the simple {@link Troll} first attacks and then flees the battle.
|
* Using decorator pattern it is possible to change class behavior during
|
||||||
* Then we decorate the {@link Troll} with a {@link SmartTroll} and perform the attack again. You
|
* runtime, as the example shows.
|
||||||
* can see how the behavior changes after the decoration.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point
|
* Program entry point
|
||||||
*
|
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@ -26,13 +23,11 @@ public class App {
|
|||||||
Hostile troll = new Troll();
|
Hostile troll = new Troll();
|
||||||
troll.attack();
|
troll.attack();
|
||||||
troll.fleeBattle();
|
troll.fleeBattle();
|
||||||
System.out.printf("Simple troll power %d.\n", troll.getAttackPower());
|
|
||||||
|
|
||||||
// change the behavior of the simple troll by adding a decorator
|
// change the behavior of the simple troll by adding a decorator
|
||||||
System.out.println("\nA smart looking troll surprises you.");
|
System.out.println("\nA smart looking troll surprises you.");
|
||||||
Hostile smart = new SmartTroll(troll);
|
Hostile smart = new SmartTroll(troll);
|
||||||
smart.attack();
|
smart.attack();
|
||||||
smart.fleeBattle();
|
smart.fleeBattle();
|
||||||
System.out.printf("Smart troll power %d.\n", smart.getAttackPower());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,6 @@ public interface Hostile {
|
|||||||
|
|
||||||
void attack();
|
void attack();
|
||||||
|
|
||||||
int getAttackPower();
|
|
||||||
|
|
||||||
void fleeBattle();
|
void fleeBattle();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package com.iluwatar.decorator;
|
package com.iluwatar.decorator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SmartTroll is a decorator for {@link Hostile} objects. The calls to the {@link Hostile} interface
|
* SmartTroll is a decorator for {@link Hostile} objects.
|
||||||
* are intercepted and decorated. Finally the calls are delegated to the decorated {@link Hostile}
|
* The calls to the {@link Hostile} interface are intercepted
|
||||||
* object.
|
* and decorated. Finally the calls are delegated
|
||||||
|
* to the decorated {@link Hostile} object.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SmartTroll implements Hostile {
|
public class SmartTroll implements Hostile {
|
||||||
@ -20,15 +21,10 @@ public class SmartTroll implements Hostile {
|
|||||||
decorated.attack();
|
decorated.attack();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAttackPower() {
|
|
||||||
// decorated troll power + 20 because it is smart
|
|
||||||
return decorated.getAttackPower() + 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fleeBattle() {
|
public void fleeBattle() {
|
||||||
System.out.println("The troll calls for help!");
|
System.out.println("The troll calls for help!");
|
||||||
decorated.fleeBattle();
|
decorated.fleeBattle();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,8 @@ public class Troll implements Hostile {
|
|||||||
System.out.println("The troll swings at you with a club!");
|
System.out.println("The troll swings at you with a club!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAttackPower() {
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void fleeBattle() {
|
public void fleeBattle() {
|
||||||
System.out.println("The troll shrieks in horror and runs away!");
|
System.out.println("The troll shrieks in horror and runs away!");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
<project
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.6.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>dependency-injection</artifactId>
|
<artifactId>dependency-injection</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -2,8 +2,9 @@ package com.iluwatar.dependency.injection;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* AdvancedWizard implements inversion of control. It depends on abstraction that can be injected
|
* AdvancedWizard implements inversion of control.
|
||||||
* through its constructor.
|
* It depends on abstraction that can be injected through
|
||||||
|
* its constructor.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class AdvancedWizard implements Wizard {
|
public class AdvancedWizard implements Wizard {
|
||||||
|
@ -10,28 +10,27 @@ import com.google.inject.Injector;
|
|||||||
* - High-level modules should not depend on low-level modules. Both should depend on abstractions.
|
* - High-level modules should not depend on low-level modules. Both should depend on abstractions.
|
||||||
* - Abstractions should not depend on details. Details should depend on abstractions.
|
* - Abstractions should not depend on details. Details should depend on abstractions.
|
||||||
* <p>
|
* <p>
|
||||||
* In this example we show you three different wizards. The first one ({@link SimpleWizard}) is a
|
* In this example we show you three different wizards. The first one ({@link SimpleWizard}) is a naive
|
||||||
* naive implementation violating the inversion of control principle. It depends directly on a
|
* implementation violating the inversion of control principle. It depends directly on a concrete
|
||||||
* concrete implementation which cannot be changed.
|
* implementation which cannot be changed.
|
||||||
* <p>
|
* <p>
|
||||||
* The second wizard ({@link AdvancedWizard}) is more flexible. It does not depend on any concrete
|
* The second wizard ({@link AdvancedWizard}) is more flexible. It does not depend on any concrete implementation
|
||||||
* implementation but abstraction. It utilizes Dependency Injection pattern allowing its
|
* but abstraction. It utilizes Dependency Injection pattern allowing its {@link Tobacco} dependency to be
|
||||||
* {@link Tobacco} dependency to be injected through its constructor. This way, handling the
|
* injected through its constructor. This way, handling the dependency is no longer the wizard's
|
||||||
* dependency is no longer the wizard's responsibility. It is resolved outside the wizard class.
|
* responsibility. It is resolved outside the wizard class.
|
||||||
* <p>
|
* <p>
|
||||||
* The third example takes the pattern a step further. It uses Guice framework for Dependency
|
* The third example takes the pattern a step further. It uses Guice framework for Dependency Injection.
|
||||||
* Injection. {@link TobaccoModule} binds a concrete implementation to abstraction. Injector is then
|
* {@link TobaccoModule} binds a concrete implementation to abstraction. Injector is then used to create
|
||||||
* used to create {@link GuiceWizard} object with correct dependencies.
|
* {@link GuiceWizard} object with correct dependencies.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point
|
* Program entry point
|
||||||
*
|
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main( String[] args ) {
|
||||||
SimpleWizard simpleWizard = new SimpleWizard();
|
SimpleWizard simpleWizard = new SimpleWizard();
|
||||||
simpleWizard.smoke();
|
simpleWizard.smoke();
|
||||||
|
|
||||||
|
@ -4,8 +4,9 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* GuiceWizard implements inversion of control. Its dependencies are injected through its
|
* GuiceWizard implements inversion of control.
|
||||||
* constructor by Guice framework.
|
* Its dependencies are injected through its constructor
|
||||||
|
* by Guice framework.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class GuiceWizard implements Wizard {
|
public class GuiceWizard implements Wizard {
|
||||||
|
@ -2,8 +2,8 @@ package com.iluwatar.dependency.injection;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Naive Wizard implementation violating the inversion of control principle. It should depend on
|
* Naive Wizard implementation violating the inversion of control principle.
|
||||||
* abstraction instead.
|
* It should depend on abstraction instead.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SimpleWizard implements Wizard {
|
public class SimpleWizard implements Wizard {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user