diff --git a/README.md b/README.md index 910e9d485..85a7aa1bf 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ system independent of how its objects are created, composed, and represented. * [Prototype](#prototype) * [Property](#property) * [Singleton](#singleton) +* [Step Builder](#step-builder) * [Multiton](#multiton) * [Object Pool](#object-pool) @@ -206,6 +207,14 @@ access to it. **Real world examples:** * [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) +## <a name="step-builder">Step Builder</a> [↑](#list-of-design-patterns) +**Intent:** An extension of the Builder pattern that fully guides the user through the creation of the object with no chances of confusion. +The user experience will be much more improved by the fact that he will only see the next step methods available, NO build method until is the right time to build the object. + + + +**Applicability:** Use the Step Builder pattern when the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled the construction process must allow different representations for the object that's constructed when in the process of constructing the order is important. + ## <a name="adapter">Adapter</a> [↑](#list-of-design-patterns) **Intent:** Convert the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn't otherwise because of @@ -1008,6 +1017,7 @@ other words, version numbers are used only for project planning sake. * [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) * [Spring Data](http://www.amazon.com/Spring-Data-Mark-Pollack/dp/1449323952/ref=sr_1_1) * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) +* [Marco Castigliego - Step Builder](http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html) # License diff --git a/pom.xml b/pom.xml index 13105f139..1059edc58 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,7 @@ <module>async-method-invocation</module> <module>business-delegate</module> <module>half-sync-half-async</module> + <module>step-builder</module> </modules> <dependencyManagement> diff --git a/step-builder/etc/step-builder.png b/step-builder/etc/step-builder.png new file mode 100644 index 000000000..b7b623657 Binary files /dev/null and b/step-builder/etc/step-builder.png differ diff --git a/step-builder/etc/step-builder.ucls b/step-builder/etc/step-builder.ucls new file mode 100644 index 000000000..bb3560a2e --- /dev/null +++ b/step-builder/etc/step-builder.ucls @@ -0,0 +1,181 @@ +<?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="true" nesting-relationships="false"> + <interface id="1" language="java" name="com.iluwatar.stepbuilder.CharacterStepBuilder.SpellStep" + project="step-builder" file="/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java" + binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="301" y="280"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </interface> + <class id="2" language="java" name="com.iluwatar.stepbuilder.CharacterStepBuilder.CharacterSteps" + project="step-builder" file="/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java" + binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="95" y="345"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </class> + <class id="3" language="java" name="com.iluwatar.stepbuilder.CharacterStepBuilder" project="step-builder" + file="/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java" binary="false" + corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="92" y="56"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </class> + <interface id="4" language="java" name="com.iluwatar.stepbuilder.CharacterStepBuilder.AbilityStep" + project="step-builder" file="/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java" + binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="409" y="418"/> + <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> + <interface id="5" language="java" name="com.iluwatar.stepbuilder.CharacterStepBuilder.ClassStep" + project="step-builder" file="/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java" + binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="406" 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> + </interface> + <interface id="6" language="java" name="com.iluwatar.stepbuilder.CharacterStepBuilder.WeaponStep" + project="step-builder" file="/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java" + binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="509" y="279"/> + <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> + <interface id="7" language="java" name="com.iluwatar.stepbuilder.CharacterStepBuilder.NameStep" project="step-builder" + file="/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java" binary="false" + corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="404" y="49"/> + <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> + <interface id="8" language="java" name="com.iluwatar.stepbuilder.CharacterStepBuilder.BuildStep" + project="step-builder" file="/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java" + binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="412" y="550"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </interface> + <class id="9" language="java" name="com.iluwatar.stepbuilder.Character" project="step-builder" + file="/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="710" y="217"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </class> + <realization id="10"> + <bendpoint x="252" y="168"/> + <end type="SOURCE" refId="2"/> + <end type="TARGET" refId="5"/> + </realization> + <realization id="11"> + <bendpoint x="255" y="419"/> + <end type="SOURCE" refId="2"/> + <end type="TARGET" refId="4"/> + </realization> + <realization id="12"> + <bendpoint x="197" y="123"/> + <end type="SOURCE" refId="2"/> + <end type="TARGET" refId="7"/> + </realization> + <dependency id="13"> + <end type="SOURCE" refId="3"/> + <end type="TARGET" refId="7"/> + </dependency> + <dependency id="14"> + <end type="SOURCE" refId="5"/> + <end type="TARGET" refId="1"/> + </dependency> + <realization id="15"> + <bendpoint x="398" y="346"/> + <end type="SOURCE" refId="2"/> + <end type="TARGET" refId="6"/> + </realization> + <dependency id="16"> + <end type="SOURCE" refId="7"/> + <end type="TARGET" refId="5"/> + </dependency> + <realization id="17"> + <bendpoint x="261" y="554"/> + <end type="SOURCE" refId="2"/> + <end type="TARGET" refId="8"/> + </realization> + <dependency id="18"> + <end type="SOURCE" refId="3"/> + <end type="TARGET" refId="2"/> + </dependency> + <dependency id="19"> + <end type="SOURCE" refId="1"/> + <end type="TARGET" refId="4"/> + </dependency> + <dependency id="20"> + <bendpoint x="512" y="477"/> + <end type="SOURCE" refId="6"/> + <end type="TARGET" refId="8"/> + </dependency> + <dependency id="21"> + <end type="SOURCE" refId="4"/> + <end type="TARGET" refId="8"/> + </dependency> + <realization id="22"> + <end type="SOURCE" refId="2"/> + <end type="TARGET" refId="1"/> + </realization> + <dependency id="23"> + <bendpoint x="597" y="547"/> + <end type="SOURCE" refId="8"/> + <end type="TARGET" refId="9"/> + </dependency> + <dependency id="24"> + <bendpoint x="97" y="602"/> + <bendpoint x="712" y="596"/> + <end type="SOURCE" refId="2"/> + <end type="TARGET" refId="9"/> + </dependency> + <dependency id="25"> + <end type="SOURCE" refId="5"/> + <end type="TARGET" refId="6"/> + </dependency> + <dependency id="26"> + <end type="SOURCE" refId="6"/> + <end type="TARGET" refId="4"/> + </dependency> + <dependency id="27"> + <bendpoint x="303" y="475"/> + <end type="SOURCE" refId="1"/> + <end type="TARGET" refId="8"/> + </dependency> + <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <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/step-builder/pom.xml b/step-builder/pom.xml new file mode 100644 index 000000000..8466266a7 --- /dev/null +++ b/step-builder/pom.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>java-design-patterns</artifactId> + <groupId>com.iluwatar</groupId> + <version>1.5.0</version> + </parent> + <artifactId>step-builder</artifactId> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java b/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java new file mode 100644 index 000000000..439c5bd11 --- /dev/null +++ b/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java @@ -0,0 +1,69 @@ +package com.iluwatar.stepbuilder; + +/** + * Step Builder Pattern + * + * <p><b>Intent</b> + * <br/> + * An extension of the Builder pattern that fully guides the user + * through the creation of the object with no chances of confusion. + * <br/> + * The user experience will be much more improved by the fact that + * he will only see the next step methods available, NO build method + * until is the right time to build the object. + * + * <p><b>Implementation</b> + * </br> + * <ul>The concept is simple: + * + * <li>Write creational steps inner classes or interfaces where each + * method knows what can be displayed next.</li> + * + * <li>Implement all your steps interfaces in an inner static class.</li> + * + * <li>Last step is the BuildStep, in charge of creating the object + * you need to build.</li> + * </ul> + * + * <p><b>Applicability</b> + * <br/> + * Use the Step Builder pattern when the algorithm for creating a + * complex object should be independent of the parts that make up + * the object and how they're assembled the construction process must + * allow different representations for the object that's constructed + * when in the process of constructing the order is important. + * + * http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html + */ +public class App { + public static void main(String[] args) { + + Character warrior = CharacterStepBuilder.newBuilder() + .name("Amberjill") + .fighterClass("Paladin") + .withWeapon("Sword") + .noAbilities() + .build(); + + System.out.println(warrior); + + Character mage = CharacterStepBuilder.newBuilder() + .name("Riobard") + .wizardClass("Sorcerer") + .withSpell("Fireball") + .withAbility("Fire Aura") + .withAbility("Teleport") + .noMoreAbilities() + .build(); + + System.out.println(mage); + + Character thief = CharacterStepBuilder.newBuilder() + .name("Desmond") + .fighterClass("Rogue") + .noWeapon() + .build(); + + System.out.println(thief); + } +} diff --git a/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java b/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java new file mode 100644 index 000000000..70727f386 --- /dev/null +++ b/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java @@ -0,0 +1,82 @@ +package com.iluwatar.stepbuilder; + +import java.util.List; + +/** + * The class with many parameters. + */ +public class Character { + + private String name; + private String fighterClass; + private String wizardClass; + private String weapon; + private String spell; + private List<String> abilities; + + public Character(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFighterClass() { + return fighterClass; + } + + public void setFighterClass(String fighterClass) { + this.fighterClass = fighterClass; + } + + public String getWizardClass() { + return wizardClass; + } + + public void setWizardClass(String wizardClass) { + this.wizardClass = wizardClass; + } + + public String getWeapon() { + return weapon; + } + + public void setWeapon(String weapon) { + this.weapon = weapon; + } + + public String getSpell() { + return spell; + } + + public void setSpell(String spell) { + this.spell = spell; + } + + public List<String> getAbilities() { + return abilities; + } + + public void setAbilities(List<String> abilities) { + this.abilities = abilities; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("This is a "); + sb.append(fighterClass != null ? fighterClass : wizardClass); + sb.append(" named "); + sb.append(name); + sb.append(" armed with a "); + sb.append(weapon != null ? weapon : spell != null ? spell : "with nothing"); + sb.append(abilities != null ? (" and wielding " + abilities + " abilities") : ""); + sb.append("."); + return sb.toString(); + } +} diff --git a/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java b/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java new file mode 100644 index 000000000..e042758d9 --- /dev/null +++ b/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java @@ -0,0 +1,168 @@ +package com.iluwatar.stepbuilder; + +import java.util.ArrayList; +import java.util.List; + +/** + * The Step Builder class. + */ + +public class CharacterStepBuilder { + + private CharacterStepBuilder() { + } + + public static NameStep newBuilder() { + return new CharacterSteps(); + } + + /** + * First Builder Step in charge of the Character name. + * Next Step available : ClassStep + */ + public interface NameStep { + ClassStep name(String name); + } + + /** + * This step is in charge of setting the Character class (fighter or wizard). + * Fighter choice : Next Step available : WeaponStep + * Wizard choice : Next Step available : SpellStep + */ + public interface ClassStep { + WeaponStep fighterClass(String fighterClass); + SpellStep wizardClass(String wizardClass); + } + + /** + * This step is in charge of the weapon. + * Weapon choice : Next Step available : AbilityStep + * No weapon choice : Next Step available : BuildStep + */ + public interface WeaponStep { + AbilityStep withWeapon(String weapon); + BuildStep noWeapon(); + } + + /** + * This step is in charge of the spell. + * Spell choice : Next Step available : AbilityStep + * No spell choice : Next Step available : BuildStep + */ + public interface SpellStep { + AbilityStep withSpell(String spell); + BuildStep noSpell(); + } + + /** + * This step is in charge of abilities. + * Next Step available : BuildStep + */ + public interface AbilityStep { + AbilityStep withAbility(String ability); + BuildStep noMoreAbilities(); + BuildStep noAbilities(); + } + + /** + * This is the final step in charge of building the Character Object. + * Validation should be here. + */ + public interface BuildStep { + Character build(); + } + + + /** + * Step Builder implementation. + */ + private static class CharacterSteps + implements NameStep, ClassStep, WeaponStep, SpellStep, AbilityStep, BuildStep { + + private String name; + private String fighterClass; + private String wizardClass; + private String weapon; + private String spell; + private List<String> abilities = new ArrayList<>(); + + @Override + public ClassStep name(String name) { + this.name = name; + return this; + } + + @Override + public WeaponStep fighterClass(String fighterClass) { + this.fighterClass = fighterClass; + return this; + } + + @Override + public SpellStep wizardClass(String wizardClass) { + this.wizardClass = wizardClass; + return this; + } + + @Override + public AbilityStep withWeapon(String weapon) { + this.weapon = weapon; + return this; + } + + @Override + public BuildStep noWeapon() { + return this; + } + + @Override + public AbilityStep withSpell(String spell) { + this.spell = spell; + return this; + } + + @Override + public BuildStep noSpell() { + return this; + } + + @Override + public AbilityStep withAbility(String ability) { + this.abilities.add(ability); + return this; + } + + @Override + public BuildStep noMoreAbilities() { + return this; + } + + @Override + public BuildStep noAbilities() { + return this; + } + + @Override + public Character build() { + Character character = new Character(name); + + if (fighterClass != null) { + character.setFighterClass(fighterClass); + } else { + character.setWizardClass(wizardClass); + } + + if (weapon != null) { + character.setWeapon(weapon); + } else { + character.setSpell(spell); + } + + if (!abilities.isEmpty()) { + character.setAbilities(abilities); + } + + return character; + } + } +} diff --git a/step-builder/src/test/java/com/iluwatar/stepbuilder/AppTest.java b/step-builder/src/test/java/com/iluwatar/stepbuilder/AppTest.java new file mode 100644 index 000000000..61fe82aaf --- /dev/null +++ b/step-builder/src/test/java/com/iluwatar/stepbuilder/AppTest.java @@ -0,0 +1,12 @@ +package com.iluwatar.stepbuilder; + +import org.junit.Test; + +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +}