Spring Shellを使用したアプリを作成してみました。
DDDを意識した構成にしてみましたので、興味のある方は
ソースを覗いて見てみてください。
説明はdocを参照ください(書けてない部分も多いですが)

アプリについて

プレイヤーは魔術師として
コマンド入力を駆使して、ひたすら敵を倒していく
サンプルアプリです。

ソース

https://github.com/keisuke-hirata/wizard
時間が足らず雑な部分もありますが、ご了承下さい

起動方法

git clone https://github.com/keisuke-hirata/wizard
cd wizard/build/libs
java -jar java -jar build/libs/wizard-0.0.1-SNAPSHOT.jar

git入れてない人はgithubからダウンロード後、jarを実行して下さい

プレイイメージ

起動が面倒だけど、具合が見てみたいと言う人向けに
スクショと何をしているのかを簡単にコメントしてみました。

スクリーンショット 2017-12-06 20.43.21.png
jarを起動するとbootユーザーには同じみのロゴが表示されます。
すると早速最初のWizardパートに移行しました。

スクリーンショット 2017-12-06 20.43.59.png
いつでもどこでも、
コンソールでhelpコマンドを叩くと、用意されているコマンドが表示されます。
パートごとに使用可能なコマンドに制限があり、使用可能でないコマンドを叩くと、
赤字英語で怒られます。

スクリーンショット 2017-12-06 20.46.54.png
最初のパートで魔法使いを作成します。
bornコマンドを名前を添えて実行。ミック、クリエイトされました。
statusコマンドを実行してパラメータを確認してみました。ミックですね。

スクリーンショット 2017-12-06 20.47.37.png
魔法使いを作成するとAdventureパートに移行します。
このパートでは、questコマンドにて敵を探すことになります。

quest先は森(forest)か洞窟(cave)を用意していますが、
出現する敵はどちらも変わりません。時間が無くて。。。
設定上は毎秒1/10の確率で敵と遭遇します。
このあたり、Enemyにレア度を設けているので、組み込んでみると良いかも。

バット現る

スクリーンショット 2017-12-06 20.48.38.png
レベル1ではfireのみが使用できます。
魔法はfire、flame、explosionを用意しています(レベルが上がると使える)。
fireを発動すると、batにダメージが与えられました。
analyzeで敵のステータスが表示されます。HPが20減っていますね。
おや?statusを見るとMPが減ってるのは良いですが、HPも減ってますね。
メッセージの出し忘れのようで、いつの間にかHP減っていくようです。

スクリーンショット 2017-12-06 20.54.46.png
fireを連発し、無事にbatを倒してレベルが上がりました!
これでflameが使えます。よかったですね。

スクリーンショット 2017-12-06 20.55.43.png
早速、batに遭遇してflameを使ってみました。
fireよりも威力がありますので、早めに片付きました。
 
 
そんな感じで延々と敵を倒して経験値を稼いでいきます。
特にエンディングはありませんので悪しからず
 

コードの補足

実装上、思案した点について記載してみました。

パッケージ構成

スクリーンショット 2017-12-05 21.51.03.png

4つのレイヤーで構成しています。

  • アプリケーション
  • ドメイン
  • インフラストラクチャ
  • プレゼンテーション

インフラストラクチャ
今回は基盤となる機能は無かったので何もありません

プレゼンテーション
consoleのみとなります。余裕ができたらSPAでも良いですね

リファクタリングされたレイヤーは、各クラスの然るべき格納場所を定義し
共同作業において、保守性に貢献します。

モデルの結合度

一般的に疎結合が良いとされています。
ドメインパッケージの集約ルートを参照ください。
WizardとEnemyには攻撃とダメージを受けた際のinterfaceを実装しています。OOMの入門書にみるような直接攻撃の対象とする事も可能ですが

attack(Enemy dqn)

この場合、壁や柱、車といった他のオブジェクトを攻撃対象とする場合、
別のメソッドか対応するスーパークラスを用意する必要が出てきます。
単一継承の特性がありますので、スケールを考えるならinterfaceを実装するのが良さそうです。

シェルコマンドの表示制御

プレゼンテーションのコマンドパッケージを参照下さい。
いくつかのspring shellによるコマンドを用意していますが
デフォルトの設定では全て閲覧・実行できる状態にあります。
本アプリではパート(探索、戦闘などのシーン)があり、
各パートで実行可能なコマンドを制御したい要求があるため、
その実現として、spring shellの機能 availableとgroupを採用しています。

クロージャの代替

アプリケーションパッケージのApplicationManagerおよびEnemyEncounter(とEncountTask)を参照ください。
Timerを使用しているところで、タスクが完了した段階で特定の処理を実行したい場合、JSであればコールバック関数、javaではSupplier辺り(?)を使うかと思いますが、今回はspringのEventPublisherを使用してみました。
publishされたイベントは@EventListenerでハンドルすることが可能です。

あとがき

突貫で作ったということもあり、
機能不足、モデリング不備が、存在しています。
興味を持った人はクローンして色々頑張ってみてください。

 

以上