diff --git a/README.md b/README.md index d5c2a2e82..2118fb5ef 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,14 @@  +## Introduction + +Design patterns are formalized best practices that the programmer can use to solve common problems when designing an application or system. + +Design patterns can speed up the development process by providing tested, proven development paradigms. + +Reusing design patterns helps to prevent subtle issues that can cause major problems, and it also improves code readability for coders and architects who are familiar with the patterns. + ## <a name="list-of-design-patterns">List of Design Patterns</a> ### Creational Patterns @@ -14,8 +22,9 @@ Creational design patterns abstract the instantiation process. They help make a * [Builder](#builder) * [Factory Method](#factory-method) * [Prototype](#prototype) +* [Property](#property) * [Singleton](#singleton) - + ### Structural Patterns Structural patterns are concerned with how classes and objects are composed to form larger structures. @@ -50,6 +59,7 @@ Behavioral patterns are concerned with algorithms and the assignment of responsi * [Double Checked Locking](#double-checked-locking) * [Null Object](#null-object) * [Callback](#callback) +* [Execute Around](#execute-around) ### Presentation Tier Patterns @@ -427,6 +437,7 @@ Presentation Tier patterns are the top-most level of the application, this is co **Applicability:** Use the Callback pattern when * When some arbitrary synchronous or asynchronous action must be performed after execution of some defined activity. +<<<<<<< HEAD ## <a name="intercepting-filter">Intercepting Filter</a> [↑](#list-of-design-patterns) **Intent:** Provide pluggable filters to conduct necessary pre-processing and post-processing to requests from a client to a target @@ -436,6 +447,29 @@ Presentation Tier patterns are the top-most level of the application, this is co * a system uses pre-processing or post-processing requests * a system should do the authentication/ authorization/ logging or tracking of request and then pass the requests to corresponding handlers * you want a modular approach to configuring pre-processing and post-processing schemes +======= +**Real world examples:** +* [CyclicBarrier] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept callback that will be triggered every time when barrier is tripped. + +## <a name="execute-around">Execute Around</a> [↑](#list-of-design-patterns) +**Intent:** Execute Around idiom frees the user from certain actions that should always be executed before and after the business method. A good example of this is resource allocation and deallocation leaving the user to specify only what to do with the resource. + + + +**Applicability:** Use the Execute Around idiom when +* You use an API that requires methods to be called in pairs such as open/close or allocate/deallocate. + +## <a name="property">Property</a> [↑](#list-of-design-patterns) +**Intent:** Create hierarchy of objects and new objects using already existing objects as parents. + + + +**Applicability:** Use the Property pattern when +* when you like to have objects with dynamic set of fields and prototype inheritance + +**Real world examples:** +* [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) prototype inheritance +>>>>>>> origin/master # Frequently asked questions @@ -481,8 +515,14 @@ The difference is the intent of the patterns. While Proxy controls access to the * [Let’s Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf) * [Pattern Languages of Program Design](http://www.amazon.com/Pattern-Languages-Program-Design-Coplien/dp/0201607344/ref=sr_1_1) * [Martin Fowler - Event Aggregator](http://martinfowler.com/eaaDev/EventAggregator.html) +<<<<<<< HEAD * [TutorialsPoint - Intercepting Filter](http://www.tutorialspoint.com/design_pattern/intercepting_filter_pattern.htm) * [Presentation Tier Pattern](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) +======= +* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1) + + +>>>>>>> origin/master # License diff --git a/callback/src/main/java/com/iluwatar/App.java b/callback/src/main/java/com/iluwatar/App.java index 6a36d66d7..2d79d8edf 100644 --- a/callback/src/main/java/com/iluwatar/App.java +++ b/callback/src/main/java/com/iluwatar/App.java @@ -1,7 +1,7 @@ package com.iluwatar; /** - * Callback pattern is more native for dynamic languages where function are first-class citizen. + * Callback pattern is more native for functional languages where function is treated as first-class citizen. * Prior to Java8 can be simulated using simple (alike command) interfaces. */ public class App { diff --git a/execute-around/execute-around.png b/execute-around/execute-around.png new file mode 100644 index 000000000..fc048919d Binary files /dev/null and b/execute-around/execute-around.png differ diff --git a/execute-around/execute-around.ucls b/execute-around/execute-around.ucls new file mode 100644 index 000000000..680334e9b --- /dev/null +++ b/execute-around/execute-around.ucls @@ -0,0 +1,28 @@ +<?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="com.iluwatar.SimpleFileWriter" project="execute-around" + file="/execute-around/src/main/java/com/iluwatar/SimpleFileWriter.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="88" width="261" x="213" y="298"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </class> + <interface id="2" language="java" name="com.iluwatar.FileWriterAction" project="execute-around" + file="/execute-around/src/main/java/com/iluwatar/FileWriterAction.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="88" width="164" x="514" y="298"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </interface> + <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> \ No newline at end of file diff --git a/execute-around/pom.xml b/execute-around/pom.xml new file mode 100644 index 000000000..8d8862063 --- /dev/null +++ b/execute-around/pom.xml @@ -0,0 +1,18 @@ +<?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.0-SNAPSHOT</version> + </parent> + <artifactId>execute-around</artifactId> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/execute-around/src/main/java/com/iluwatar/App.java b/execute-around/src/main/java/com/iluwatar/App.java new file mode 100644 index 000000000..d3db00045 --- /dev/null +++ b/execute-around/src/main/java/com/iluwatar/App.java @@ -0,0 +1,30 @@ +package com.iluwatar; + +import java.io.FileWriter; +import java.io.IOException; + +/** + * The Execute Around idiom specifies some code to be executed before and after + * a method. Typically the idiom is used when the API has methods to be executed in + * pairs, such as resource allocation/deallocation or lock acquisition/release. + * + * In this example, we have SimpleFileWriter class that opens and closes the file + * for the user. The user specifies only what to do with the file by providing the + * FileWriterAction implementation. + * + */ +public class App { + + public static void main( String[] args ) throws IOException { + + new SimpleFileWriter("testfile.txt", new FileWriterAction() { + + @Override + public void writeFile(FileWriter writer) throws IOException { + writer.write("Hello"); + writer.append(" "); + writer.append("there!"); + } + }); + } +} diff --git a/execute-around/src/main/java/com/iluwatar/FileWriterAction.java b/execute-around/src/main/java/com/iluwatar/FileWriterAction.java new file mode 100644 index 000000000..3f4fcd16c --- /dev/null +++ b/execute-around/src/main/java/com/iluwatar/FileWriterAction.java @@ -0,0 +1,15 @@ +package com.iluwatar; + +import java.io.FileWriter; +import java.io.IOException; + +/** + * + * Interface for specifying what to do with the file resource. + * + */ +public interface FileWriterAction { + + void writeFile(FileWriter writer) throws IOException; + +} diff --git a/execute-around/src/main/java/com/iluwatar/SimpleFileWriter.java b/execute-around/src/main/java/com/iluwatar/SimpleFileWriter.java new file mode 100644 index 000000000..c4be8a9fa --- /dev/null +++ b/execute-around/src/main/java/com/iluwatar/SimpleFileWriter.java @@ -0,0 +1,23 @@ +package com.iluwatar; + +import java.io.FileWriter; +import java.io.IOException; + +/** + * + * SimpleFileWriter handles opening and closing file for the user. The user + * only has to specify what to do with the file resource through FileWriterAction + * parameter. + * + */ +public class SimpleFileWriter { + + public SimpleFileWriter(String filename, FileWriterAction action) throws IOException { + FileWriter writer = new FileWriter(filename); + try { + action.writeFile(writer); + } finally { + writer.close(); + } + } +} diff --git a/execute-around/src/test/java/com/iluwatar/AppTest.java b/execute-around/src/test/java/com/iluwatar/AppTest.java new file mode 100644 index 000000000..3f256e7aa --- /dev/null +++ b/execute-around/src/test/java/com/iluwatar/AppTest.java @@ -0,0 +1,19 @@ +package com.iluwatar; + +import java.io.IOException; + +import org.junit.Test; + +/** + * + * Tests execute-around example. + * + */ +public class AppTest { + + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/pom.xml b/pom.xml index 80f25c549..58cf19777 100644 --- a/pom.xml +++ b/pom.xml @@ -41,8 +41,14 @@ <module>null-object</module> <module>event-aggregator</module> <module>callback</module> +<<<<<<< HEAD <module>intercepting-filter</module> </modules> +======= + <module>execute-around</module> + <module>property</module> + </modules> +>>>>>>> origin/master <dependencyManagement> <dependencies> diff --git a/property/etc/property.jpg b/property/etc/property.jpg new file mode 100644 index 000000000..e3da03e0c Binary files /dev/null and b/property/etc/property.jpg differ diff --git a/property/etc/property.ucls b/property/etc/property.ucls new file mode 100644 index 000000000..0ad1d61eb --- /dev/null +++ b/property/etc/property.ucls @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<class-diagram version="1.1.8" icons="true" automaticImage="JPEG" always-add-relationships="false" + generalizations="true" realizations="true" associations="true" dependencies="false" nesting-relationships="true"> + <class id="1" language="java" name="com.iluwatar.Character" project="property" + file="/property/src/main/java/com/iluwatar/Character.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="497" y="232"/> + <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="2" language="java" name="com.iluwatar.Character.Type" project="property" + file="/property/src/main/java/com/iluwatar/Character.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="320" y="231"/> + <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> + <enumeration id="3" language="java" name="com.iluwatar.Stats" project="property" + file="/property/src/main/java/com/iluwatar/Stats.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="695" y="205"/> + <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> + <interface id="4" language="java" name="com.iluwatar.Prototype" project="property" + file="/property/src/main/java/com/iluwatar/Prototype.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="495" y="487"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </interface> + <association id="5"> + <end type="SOURCE" refId="1" navigable="false"> + <attribute id="6" name="type"/> + <multiplicity id="7" minimum="0" maximum="1"/> + </end> + <end type="TARGET" refId="2" navigable="true"/> + <display labels="true" multiplicity="true"/> + </association> + <association id="8"> + <end type="SOURCE" refId="1" navigable="false"> + <attribute id="9" name="prototype"/> + <multiplicity id="10" minimum="0" maximum="1"/> + </end> + <end type="TARGET" refId="4" navigable="true"/> + <display labels="true" multiplicity="true"/> + </association> + <nesting id="11"> + <end type="SOURCE" refId="1"/> + <end type="TARGET" refId="2"/> + </nesting> + <realization id="12"> + <end type="SOURCE" refId="1"/> + <end type="TARGET" refId="4"/> + </realization> + <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </classifier-display> + <association-display labels="true" multiplicity="true"/> +</class-diagram> \ No newline at end of file diff --git a/property/pom.xml b/property/pom.xml new file mode 100644 index 000000000..a26894f0a --- /dev/null +++ b/property/pom.xml @@ -0,0 +1,18 @@ +<?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.0-SNAPSHOT</version> + </parent> + <artifactId>property</artifactId> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/property/src/main/java/com/iluwatar/App.java b/property/src/main/java/com/iluwatar/App.java new file mode 100644 index 000000000..95ff4355f --- /dev/null +++ b/property/src/main/java/com/iluwatar/App.java @@ -0,0 +1,49 @@ +package com.iluwatar; + +import com.iluwatar.Character.Type; + +/** + * Example of Character instantiation using Property pattern (as concept also known like Prototype inheritance). + * In prototype inheritance instead of classes, as opposite to Java class inheritance, + * objects are used to create another objects and object hierarchies. + * Hierarchies are created using prototype chain through delegation: every object has link to parent object. + * Any base (parent) object can be amended at runtime (by adding or removal of some property), and all child objects will be affected as result. + */ +public class App { + + public static void main(String[] args) { + /* set up */ + Prototype charProto = new Character(); + charProto.set(Stats.STRENGTH, 10); + charProto.set(Stats.AGILITY, 10); + charProto.set(Stats.ARMOR, 10); + charProto.set(Stats.ATTACK_POWER, 10); + + Character mageProto = new Character(Type.MAGE, charProto); + mageProto.set(Stats.INTELLECT, 15); + mageProto.set(Stats.SPIRIT, 10); + + Character warProto = new Character(Type.WARRIOR, charProto); + warProto.set(Stats.RAGE, 15); + warProto.set(Stats.ARMOR, 15); // boost default armor for warrior + + Character rogueProto = new Character(Type.ROGUE, charProto); + rogueProto.set(Stats.ENERGY, 15); + rogueProto.set(Stats.AGILITY, 15); // boost default agility for rogue + + /* usage */ + Character mag = new Character("Player_1", mageProto); + mag.set(Stats.ARMOR, 8); + System.out.println(mag); + + Character warrior = new Character("Player_2", warProto); + System.out.println(warrior); + + Character rogue = new Character("Player_3", rogueProto); + System.out.println(rogue); + + Character rogueDouble = new Character("Player_4", rogue); + rogueDouble.set(Stats.ATTACK_POWER, 12); + System.out.println(rogueDouble); + } +} diff --git a/property/src/main/java/com/iluwatar/Character.java b/property/src/main/java/com/iluwatar/Character.java new file mode 100644 index 000000000..ac8abaa0e --- /dev/null +++ b/property/src/main/java/com/iluwatar/Character.java @@ -0,0 +1,117 @@ +package com.iluwatar; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents Character in game and his abilities (base stats). + */ +public class Character implements Prototype { + + public enum Type { + WARRIOR, MAGE, ROGUE + } + + private final Prototype prototype; + private final Map<Stats, Integer> properties = new HashMap<>(); + + private String name; + private Type type; + + public Character() { + this.prototype = new Prototype() { // Null-value object + @Override + public Integer get(Stats stat) { + return null; + } + @Override + public boolean has(Stats stat) { + return false; + } + @Override + public void set(Stats stat, Integer val) { + } + @Override + public void remove(Stats stat) { + }} + ; + } + + public Character(Type type, Prototype prototype) { + this.type = type; + this.prototype = prototype; + } + + public Character(String name, Character prototype) { + this.name = name; + this.type = prototype.type; + this.prototype = prototype; + } + + public String name() { + return name; + } + + public Type type() { + return type; + } + + @Override + public Integer get(Stats stat) { + boolean containsValue = properties.containsKey(stat); + if (containsValue) { + return properties.get(stat); + } else { + return prototype.get(stat); + } + } + + @Override + public boolean has(Stats stat) { + return get(stat) != null; + } + + @Override + public void set(Stats stat, Integer val) { + properties.put(stat, val); + } + + @Override + public void remove(Stats stat) { + properties.put(stat, null); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (name != null) { + builder + .append("Player: ") + .append(name) + .append("\n"); + } + + if (type != null) { + builder + .append("Character type: ") + .append(type.name()) + .append("\n"); + } + + builder.append("Stats:\n"); + for (Stats stat : Stats.values()) { + Integer value = this.get(stat); + if (value == null) { + continue; + } + builder + .append(" - ") + .append(stat.name()) + .append(":") + .append(value) + .append("\n"); + } + return builder.toString(); + } + +} diff --git a/property/src/main/java/com/iluwatar/Prototype.java b/property/src/main/java/com/iluwatar/Prototype.java new file mode 100644 index 000000000..ef9d2d7b6 --- /dev/null +++ b/property/src/main/java/com/iluwatar/Prototype.java @@ -0,0 +1,12 @@ +package com.iluwatar; + +/** + * Interface for prototype inheritance + */ +public interface Prototype { + + public Integer get(Stats stat); + public boolean has(Stats stat); + public void set(Stats stat, Integer val); + public void remove(Stats stat); +} diff --git a/property/src/main/java/com/iluwatar/Stats.java b/property/src/main/java/com/iluwatar/Stats.java new file mode 100644 index 000000000..3c5648148 --- /dev/null +++ b/property/src/main/java/com/iluwatar/Stats.java @@ -0,0 +1,9 @@ +package com.iluwatar; + +/** + * All possible attributes that Character can have + */ +public enum Stats { + + AGILITY, STRENGTH, ATTACK_POWER, ARMOR, INTELLECT, SPIRIT, ENERGY, RAGE +} diff --git a/property/src/test/java/com/iluwatar/AppTest.java b/property/src/test/java/com/iluwatar/AppTest.java new file mode 100644 index 000000000..6db5ad214 --- /dev/null +++ b/property/src/test/java/com/iluwatar/AppTest.java @@ -0,0 +1,12 @@ +package com.iluwatar; + +import org.junit.Test; + +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +}