asatoの技術的な日常日記

「成長に最大の責任をもつ者は、本人であって組織ではない。自らと組織を成長させるためには何に集中すべきかを、自らに問わなければならない」  非営利組織の経営 - ピーター・ドラッカー

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

コード進化:向きを変える

コード進化を考えるパート3ぐらい。ちなみに、「コード進化のパターン」と「コード進化」は、関連するけど、区別してます。


今回は、ちょっとアプリケーション特化(ゲーム)な内容だけど、機能追加の要求がどのようにしてコードに影響を与えるのかを考察したい。

どんな機能追加か。次の図のような感じ。
look.png

追加したい機能は、右側のヤツ。既に左側の機能は実現済み。

機能追加を要求変化だと考えると、要求の変化は次のような感じ。
look_req.png


この要求変化に伴って、コード側もこの要求を満たすように変化しないといけない。ここでは、コード=デザインとしてる。
look_req_design.png


結論から言えば、コードの変化があったのは次の二つのクラス:
-KeyManager:どのキーがどんな機能に対応するのか管理 or キーコンフィグの実装。
-NormalModeKeyHandler:通常モードで押されたキーの処理を扱う。

追加されたクラスは:
-LookKeyHandler:通常モードでキャラの方向を変えるキーが押されたときの処理を扱う

先ほどの図で表すとこうなる:
look_req_design2.png


いつものように、デザイン構造の変化を DSM (Design Structure Matrix) で確認してみる。DSM は、要素間の依存関係を表現するための方法。
before:
look_dsm_before.png

after:
look_dsm_after.png


別の表現だとこんな感じ。

before:
look_dep_before.png

after:
look_dep_after.png


一応コードも一部載せてみる:
before:


public class KeyManager {

// アクションキー
private static int ATTACK_KEY = KeyEvent.VK_Z;
private static int MAP_KEY = KeyEvent.VK_A;
private static int STATUS_KEY = KeyEvent.VK_X;
private static int ITEM_KEY = KeyEvent.VK_C;
private static int WAIT_KEY = KeyEvent.VK_S;
private static int SYSTEM_KEY = KeyEvent.VK_SPACE;

// その他のキーフィールド

private static int MOVE_UP_KEY = KeyEvent.VK_UP;
private static int MOVE_DOWN_KEY = KeyEvent.VK_DOWN;
private static int MOVE_LEFT_KEY = KeyEvent.VK_LEFT;
private static int MOVE_RIGHT_KEY = KeyEvent.VK_RIGHT;

// その他のキーチェックメソッド

// 前後左右
public boolean isMoveUpKey(KeyEvent e) {
return isKey(e, MOVE_UP_KEY, MOVE_UP_KEY2, MOVE_UP_KEY3);
}
public boolean isMoveDownKey(KeyEvent e) {
return isKey(e, MOVE_DOWN_KEY, MOVE_DOWN_KEY2, MOVE_DOWN_KEY3);
}
public boolean isMoveLeftKey(KeyEvent e) {
return isKey(e, MOVE_LEFT_KEY, MOVE_LEFT_KEY2, MOVE_LEFT_KEY3);
}
public boolean isMoveRightKey(KeyEvent e) {
return isKey(e, MOVE_RIGHT_KEY, MOVE_RIGHT_KEY2, MOVE_RIGHT_KEY3);
}

// ... ナナメ
}
public class NormalModeKeyHandler implements GameModeKeyHandler {

private ItemMenu itemMenu;

public NormalModeKeyHandler(ItemMenu itemMenu) {
this.itemMenu = itemMenu;
}

public void handleKey(KeyManager keyManager, GameManager gameManager, KeyEvent e) {
if (keyManager.isStatusKey(e)) {
// ...

} else if (keyManager.isItemKey(e)) {
// ...

} else if (keyManager.isMapKey(e)) {
// ...

} else if // その他のキー
// ...

} else if (keyManager.isMoveKey(e)) {
new MoveKeyHandler(gameManager, keyManager).handle(e);
}
}
}
public class MoveKeyHandler {

private GameManager gameManager;
private KeyManager keyManager;

public MoveKeyHandler(GameManager gameManager, KeyManager keyManager) {
this.gameManager = gameManager;
this.keyManager = keyManager;
}

public void handle(KeyEvent e) {

if ( keyManager.isMoveDownKey(e) ) {
tryMove( Direction.DOWN );
}
else if ( keyManager.isMoveUpKey(e) ) {
tryMove( Direction.UP );
}
else if ( keyManager.isMoveLeftKey(e) ) {
tryMove( Direction.LEFT );
}
else if ( keyManager.isMoveRightKey(e) ) {
tryMove( Direction.RIGHT );
}
// ... ナナメ
}

private void tryMove(Direction dir) {
// ... 移動できるなら移動
}
}


after:


public class KeyManager {

// その他のキーフィールド

private static int MOVE_UP_KEY = KeyEvent.VK_UP;
private static int MOVE_DOWN_KEY = KeyEvent.VK_DOWN;
private static int MOVE_LEFT_KEY = KeyEvent.VK_LEFT;
private static int MOVE_RIGHT_KEY = KeyEvent.VK_RIGHT;


private static int LOOK_UP_KEY = KeyEvent.VK_KP_UP;
private static int LOOK_DOWN_KEY = KeyEvent.VK_KP_DOWN;
private static int LOOK_LEFT_KEY = KeyEvent.VK_KP_LEFT;
private static int LOOK_RIGHT_KEY = KeyEvent.VK_KP_RIGHT;



// その他のキーチェックメソッド

// 左右前後
public boolean isMoveUpKey(KeyEvent e) {
return isKey(e, MOVE_UP_KEY, MOVE_UP_KEY2, MOVE_UP_KEY3);
}
public boolean isMoveDownKey(KeyEvent e) {
return isKey(e, MOVE_DOWN_KEY, MOVE_DOWN_KEY2, MOVE_DOWN_KEY3);
}
public boolean isMoveLeftKey(KeyEvent e) {
return isKey(e, MOVE_LEFT_KEY, MOVE_LEFT_KEY2, MOVE_LEFT_KEY3);
}
public boolean isMoveRightKey(KeyEvent e) {
return isKey(e, MOVE_RIGHT_KEY, MOVE_RIGHT_KEY2, MOVE_RIGHT_KEY3);
}

public boolean isLookUpKey(KeyEvent e) {
return (isMoveUpKey(e) && isShiftDown(e)) || isKey(e, LOOK_UP_KEY);
}
public boolean isLookDownKey(KeyEvent e) {
return (isMoveDownKey(e) && isShiftDown(e)) || isKey(e, LOOK_DOWN_KEY);
}
public boolean isLookLeftKey(KeyEvent e) {
return (isMoveLeftKey(e) && isShiftDown(e)) || isKey(e, LOOK_LEFT_KEY);
}
public boolean isLookRightKey(KeyEvent e) {
return (isMoveRightKey(e) && isShiftDown(e)) || isKey(e, LOOK_RIGHT_KEY);
}

public boolean isMoveKey(KeyEvent e) {
// ...
}
public boolean isLookKey(KeyEvent e) {
// ...
}


// ... その他のメソッド
}
public class NormalModeKeyHandler implements GameModeKeyHandler {

private ItemMenu itemMenu;

public NormalModeKeyHandler(ItemMenu itemMenu) {
this.itemMenu = itemMenu;
}

public void handleKey(KeyManager keyManager, GameManager gameManager, KeyEvent e) {
// ...

if (keyManager.isStatusKey(e)) {
// ...

} else if // その他のキー
// ...
} else if (keyManager.isLookKey(e)) {
new LookKeyHandler(gameManager, keyManager).handle(e);


} else if (keyManager.isMoveKey(e)) {
new MoveKeyHandler(gameManager, keyManager).handle(e);
}
}
}
public class LookKeyHandler {

private GameManager gameManager;
private KeyManager keyManager;

public LookKeyHandler(GameManager gameManager, KeyManager keyManager) {
this.gameManager = gameManager;
this.keyManager = keyManager;
}

public void handle(KeyEvent e) {

// ...

if ( keyManager.isLookDownKey(e) ) {
look( Direction.DOWN );
}
else if ( keyManager.isLookUpKey(e) ) {
look( Direction.UP );
}
else if ( keyManager.isLookLeftKey(e) ) {
look( Direction.LEFT );
}
else if ( keyManager.isLookRightKey(e) ) {
look( Direction.RIGHT );
}
}

private void look(Direction dir) {
// ... 方向変更
}
}




さらに違う表現をしてみよう。この表現の目的は、仕様間のオーバーラップを表すこと。キャラ攻撃の(既にある)仕様を加えていることに注意。これは、KeyMangaer と NormalModeKeyHandler が依存している仕様を少し複雑に見せたいため。
before:
look_concern_before.png

after:
look_concern_after.png


さて、この最後の図を基に、さらに仕様(or コンサーン)のオーバーラップを複雑にしていきたいと思う。目的は、コンサーンの分離の可能性を考えること。現在の図からは KeyMangaer と NormalModeKeyHandler は複数の仕様(キャラ移動、キャラ攻撃、キャラ向き変更に依存していることが分かる。

この依存をどう考えるべきか。たとえば、 ある仕様を機能と考えると、要求に合わせて取ったり付けたりしやすいか。ソフトウェア工学の言葉でいえば、プロダクトラインを構築できるのか。各機能をフィーチャモジュール(「Understanding Feature Modularity」を参照(すると良いかも。僕もまだちゃんと読んでないけど)としてモジュール化できるか。

というところを考えたいんだけど、力尽きたので、図だけ。
look_concern_more.png



やりたかったけどできなかったこと:
-フィーチャ合成・分割(feature composition/decompostion)について。たとえば、防御キーをフィーチャとするなど。あと、操作説明のビューへの影響について。

-仕様 or コンサーン依存について。

-アーキテクチャのルールが課すことによる進化の方向付けについて。
スポンサーサイト

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバックURLはこちら
http://asatohan.blog77.fc2.com/tb.php/40-79f04fcd
この記事にトラックバックする(FC2ブログユーザー)

FC2Ad

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。