I took the example from the first chapter of Spring in Action and added the class QuestExpedition to separate the behavior of how the quest is achieved from the quest as an objective or goal. One design being better than the other would depend on the ubiquitous language of the domain driven design. As I envision it, the personnel participating in the quest and the itinerary belong to a separate concern. I created QuestExpedition to encapsulate such concerns.
In Craig's code, the knight knows upon which quest he is embarked. In real life a knight would know the details of a quest on which he might embark but, in software, we only model the required behavior of the application, not real life. I removed the Knight.embarkOnQuest() method because it only calls Quest.embark(). In my application, I have the QuestExpedition embark rather than have the Knight embark.
QuestApp.java
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class QuestApp { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); QuestExpedition expedition = (QuestExpedition)ctx.getBean("expedition"); expedition.embark(); expedition.reportStatus(); } }
QuestExpedition.java
import java.util.*; public class QuestExpedition { Quest quest; Listknights; List minstrels; List destinations; public void embark() { System.out.println(toString()); } public void setQuest(Quest quest) {this.quest = quest;} public void setKnights(List knights) {this.knights = knights;} public void setMinstrels(List minstrels) {this.minstrels = minstrels;} public void setDestinations(List destinations) {this.destinations = destinations;} public void reportStatus() { for (Minstrel m : minstrels) for (Knight k : knights) m.praiseKnight(k, quest); } @Override public String toString() { String comma = ""; StringBuilder sb = new StringBuilder(); sb.append("Embarking on quest to " + quest.toString()); sb.append("\n with Knights: "); for (Knight k : knights) { sb.append(comma + k.toString()); comma = ", "; } sb.append("\n and with Minstrels: "); comma = ""; for (Minstrel m : minstrels) { sb.append(comma + m.toString()); comma = ", "; } sb.append("\n Itinerary: "); comma = ""; for (String dest : destinations) { sb.append(comma + dest); comma = ", "; } return sb.toString(); } }
beans.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="expedition" class="QuestExpedition"> <property name="quest" ref="quest"/> <property name="knights"> <list> <ref bean="count"/> <ref bean="duke"/> <ref bean="earl"/> </list> </property> <property name="minstrels"> <list> <ref bean="troubadour"/> </list> </property> <property name="destinations"> <list> <value>Rome</value> <value>Crete</value> <value>Malta</value> </list> </property> </bean> <bean id="quest" class="SlayDragonQuest"> <constructor-arg value="Puff" /> </bean> <bean id="count" class="Knight"> <constructor-arg value="Count" /> <constructor-arg value="Chuck" /> </bean> <bean id="duke" class="Knight"> <constructor-arg value="Duke" /> <constructor-arg value="Danny" /> </bean> <bean id="earl" class="Knight"> <constructor-arg value="Earl" /> <constructor-arg value="Earle" /> </bean> <bean id="troubadour" class="Minstrel"> <constructor-arg value="Troubadour" /> <constructor-arg value="Steve" /> </bean> </beans>
Quest.java
public abstract class Quest { String name; String description; @Override public String toString() { return description + " " + name; } }
HolyGrailQuest.java
public class HolyGrailQuest extends Quest { HolyGrailQuest(String name) { this.name = name; this.description = "find and acquire the grail"; } }
RescueDamselQuest.java
public class RescueDamselQuest extends Quest { RescueDamselQuest(String name) { this.name = name; this.description = "rescue the damsel"; } }
SlayDragonQuest.java
public class SlayDragonQuest extends Quest { SlayDragonQuest(String name) { this.name = name; this.description = "slay the dragon"; } }
Person.java
public class Person { final private String name; final private String honorific; Person(String honorific, String name) { this.name = name; this.honorific = honorific; } @Override public String toString() { return honorific + " " + name; } }
Minstrel.java
public class Minstrel extends Person { Minstrel(String honorific, String name) { super(honorific, name); } public void praiseKnight(Knight knight, Quest quest) { System.out.println("Bold " + knight.toString() + " pledged himself to " + quest.toString()); } }
Knight.java
public class Knight extends Person { Knight(String honorific, String name) { super(honorific, name); } }
ANT build.xml
<project name="GrailsQuest" default="run" basedir="."> <property name="src" location="src/main/java"/> <property name="test.src" location="src/test/java"/> <property name="build" location="build/classes"/> <property name="tests" location="build/tests"/> <property name="spring" location="${user.home}/dev/lib/spring-framework-3"/> <path id="classpath"> <pathelement location="${build}"/> <fileset dir="${spring}" includes="**/*.jar"/> </path> <path id="test.classpath"> <pathelement path="${classpath}"/> <pathelement location="${user.home}/dev/lib/junit/junit-4.6.jar"/> <pathelement path="${build}"/> <pathelement path="${tests}"/> </path> <target name="clean"> <delete dir="${build}"/> </target> <target name="init" depends="clean"> <mkdir dir="${build}"/> <mkdir dir="${tests}"/> </target> <target name="compile" depends="init"> <javac srcdir="${src}" destdir="${build}" classpathref="classpath"/> <javac srcdir="${test.src}" destdir="${tests}" classpathref="test.classpath"/> </target> <target name="run" depends="compile"> <java classname="QuestApp"> <classpath><path refid="classpath"/></classpath> </java> </target> </project>
Running the ANT Command for the Project
[greg:Quest] ant . . . [java] Embarking on quest to slay the dragon Puff [java] with Knights: Count Chuck, Duke Danny, Earl Earle [java] and with Minstrels: Troubadour Steve [java] Itinerary: Rome, Crete, Malta [java] Bold Count Chuck pledged himself to slay the dragon Puff [java] Bold Duke Danny pledged himself to slay the dragon Puff [java] Bold Earl Earle pledged himself to slay the dragon Puff BUILD SUCCESSFUL Total time: 5 seconds