2014-01-16

実はMVCってしっくりこないんです

例えばスーファミFFみたいな、古典的RPG戦闘シーンを作っていて、「勇者モンスターを攻撃する」。

勇者.attack(モンスター);

という設計だったとする。

これに「モンスターが毒を受けて、自死する」という挙動を追加するケースを考える。

毒.attack(モンスター);

やや複雑な変更になりそうだが、勇者の攻撃とのタイミングの差で、

のようになるとしよう。

さて、変更する箇所は...

まず、インターフェイス"攻撃者"をつくって、「勇者」、「毒」をその実装とする。加えて...

Model勇者への変更:

斬撃後に死んでいた場合(*) 、納刀する

入力Controllerへの変更:

攻撃前に既に死んでいた場合、攻撃できないようにする

Modelモンスターへの変更:

しかしこの変更は非常に複雑だ...

困ったこと1

この設計変更によって、

が、

の4パターンに増えた(※ただし最後パターンに関しては今回特に修正の必要はなかった)

攻撃者がひとつ増えるごとに、これらの分岐は倍、倍に増えていく怖れがある。

困ったこと2

あらたに攻撃者を追加すると、既存の攻撃者を変更しなければならない。

困ったこと3

この変更によって、全体の時間的な流れがよくわからなくなった。

例えばさらに攻撃者「呪い」を追加したとする。

するとModelモンスターには

  • 既に毒を受けているとき呪いを受けたら
  • 呪いを受けたあとに斬撃で死んだら

等々の分岐が並ぶことになる。この分かりにくさは単にStateパターンを導入しただけでは本質的には改善しないだろう。

考察

MVCにおけるモデル、は振る舞いを持ったデータだ。モデルが状態変化したときイベントが発生する。

しかしその状態変化が別のモデルの状態変化のタイミングに影響をうけるときモデル設計はとても複雑になり、読みにくくなってしまう。

モデルで発生するイベントが多く、複雑になるほど、ビューとの関連は密になる。

このような複雑さをプログラマが支配するのは、とても大変だ。

みなさんも体験したことがないだろうか、GUIで「ホニャララしているときにホゲホゲするとバグる」みたいな挙動を。

例えば私が愛用しているiOSはてなブックマークアプリは、

  1. 「人気」タブを選択して
  2. 記事のリストを(更新するために)下にドラッグしながら
  3. ドラッグしている指を離さないで、(空いてる別の手で)「新着」タブを選択する
  4. そのあと指を離して、再び記事のリストを下にドラッグ

すると、上部に表示される「更新時刻」の表示位置がおかしくなる。

RPG設計は、たとえば下記のように記述できるDSLを導入すると、劇的に改善する。

タイミング:毒 ⇒ 死ぬ ⇒ 斬撃 の順なら、納刀する
タイミング:毒 ⇒ 斬撃 ⇒ 死ぬ の順なら、変色した血が飛び散る

オブジェクト指向ではこれはStateパターンStrategyパターンを組み合わせることで実現できる。

でも、このような設計にすると、勇者モンスターモデルオブジェクト責任は極端に少なくなってしまう。

加えてオブジェクト間のやりとりの複雑さがDSLに押し込められるので、オブジェクト主体性殆ど無くなってしまい、

結果、モデルオブジェクト存在意義が怪しくなるだろう。

うん、ただのデータでいいや。

---------------------------

(*) 攻撃時に相手の状態を問い合わせるのが醜い。これを避けるには、モンスターが毒で死んだ時に勇者イベントを通知して、勇者を”戦意喪失”状態に変化させる手がある。

  • MVC以前にオブジェクト指向がしっくりきてないでしょ。 「勇者がモンスターを攻撃する」。 勇者.attack(モンスター); という設計だったとする。 これが既にオブジェクト指向的じゃな...

    • ご意見くださって、ありがとうございます。 例にあげたRPGのモデル設計はhttp://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1178047006を参考にしました。 ですが自分で書くとしても、十中八九そ...

      • 英語として読みにくいなら、メソッド名のほうを変えるよ。 モンスター.attackedBy(勇者.attackPower) 攻撃方法が増えるなら、攻撃方法を抽象化するでしょ。 攻撃方法型を作って モンスター....

      • 一理あるご意見をいただきました。ありがとうございます。 英語として読みにくいなら、メソッド名のほうを変えるよ。 モンスター.attackedBy(勇者.attackPower) 誤解を招いてしまったよ...

        • わかりやすさについてとやかく言わないけどさ、攻撃するってことは、モンスターのHPだとかが減るんだよね? 勇者.attack(モンスター) って書いたとしてもさ、勇者.attackメソッドの中で、...

          • 守備力だとか、耐性だとかは、モンスターの属性なので、影響はモンスター側で面倒見てもらいたいから、 攻撃でHPを減らすのは、モンスターのメソッド経由になると思う。 結局、 c...

  • モデルで発生するイベントが多く、複雑になるほど、ビューとの関連は密になる。 ここの所は、今回の例ではいまいち見えづらい気がする。今回の例で複雑になっているのはモデルだ...

    • 元増田です。ありがとうございます。 モデルで発生するイベントが多く、複雑になるほど、ビューとの関連は密になる。 ここの所は、今回の例ではいまいち見えづらい気がする。 ...

記事への反応(ブックマークコメント)

ログイン ユーザー登録
ようこそ ゲスト さん