2014-01-16

http://anond.hatelabo.jp/20140116080422

意見くださって、ありがとうございます

例にあげたRPGモデル設計http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1178047006を参考にしました。

ですが自分で書くとしても、十中八九そのような設計にするでしょう。

X
モンスター.attack(勇者.attackPower)
O
勇者.attack(モンスター)

だと主張します。その根拠を説明します。

理由1: 読みやす

主語.動詞(目的語)

英語の文のようで読みやすい。

理由2: 変更が分散しにくい

モデルに対する命令は、ふつう入力Controllerのイベントハンドラに書かれる。attackに関する命令はどちらの設計をとったとしても"攻撃が選択された"イベントハンドラに書かれるだろう。

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

だと、たとえばモンスターダメージを与える要素として、他に「すばやさ」などを加えようとしたら、モンスターのattackメソッドを変更する必要が生じる。

また、FF5には"竜騎士ジャンプ"という攻撃方法があった。ジャンプを選択すると、竜騎士空高く飛び、一定時間が経ったのちに落ちてきて、空中からモンスターを攻撃する、というもの。これを実装しようとすると、入力Controllerのイベントハンドラの直前の部分で別スレッドに投げるなり、タイマーを起こすなりして処理したくなる。

味方同士の”連携攻撃”を実装しようとするとやはり、入力Controllerのイベントハンドラを修正したくなるだろう。

まり、悪い方の設計ではModel勇者」への設計変更の都合で、Modelモンスター」や入力Controllerを修正する必要が出てきてしまう。

勇者.attack(モンスター)

では、どの設計変更の例でも、勇者側を変更するだけで良い。この設計には、クラス間の責任の分担がはっきりするという利点がある。

あれ?それならリストは??

リストライブラリは、

list.append(elem)

でしょ。

リスト実質的にはオブジェクトというより、ただのデータ構造

リストやディクショナリ(連想配列)は便利な道具だが、これを前面に押し出し設計は複雑さを招く場合がある。

たとえば、"複数のモンスターの集まり"や"ソートされたリスト"を、リスト継承して実装しようとすると、とたんにドツボにハマる。

Onスライムが仲間を呼んだ(){

    スライムの集まり.append(スライム)
    スライムの個数ラベル.更新(スライムの集まり.length)
    if(スライムの集まり.length == 1 and スライムの集まり.get(0).name == "キングスライム"){
        スライム名前ベル.更新("キングスライム")
    }

}

スライムは8匹集まるとキングスライムになるので、最初の一行でスライムの集まりを変更して以降、スライムの個数を気にする必要があることに注意して欲しい。

上記のコードを洗練させるために、Modelスライムの集まり」で個数が変わった時にイベント"OnLengthChange"を発行するようにしても良いが、そのようにすると、lengthメソッド安全に呼べるのはそのイベントハンドラのみ、ということになる。プログラマに注意を促すために、lengthメソッドコメントにその旨を注意書きしておく必要がある。

"スライムの集まり"リストクラスは、プログラマが期待するだろうリストクラスの振る舞い《append後の個数はappend前+1》を破壊している。

"ソートされたリスト"は、appendメソッドオーバーライドして、追加時に勝手に順番を並び替えてくれるようなものだが、これも《リストに要素を追加した時には、その要素は末尾に置かれる》というリストクラスの振る舞いを破壊する。スライムの例と同じように、イベントの発行とメソッドの注意書きが必要になる。プログラマはそれらのクラスを使うときに、より注意深くならなくてはいけない。

このように、クラスの振る舞いを壊してしまうようなクラス設計は、プログラマ地雷原におくりこむことになる。

時間的な流れの分かりやすさ v.s. 責任分担

はいえ、リストのようなデータ構造を便利に使えるシーンもある。

はじめからおわりまで100行程度のスクリプトで、とくにイベントリブンにするほどの複雑さがない場合。順序を追って読めるので、時間的な流れがわかりやすい。

一方、RPGの例で示したような設計は、オブジェクト指向をフルに使って、コード責任に応じて役割分担しやすくなる利点がある。

前者は"トランザクションスクリプト"的設計と呼ばれ、後者は"ドメインモデル"設計と呼ばれる。

よく利点・欠点比較され、それぞれを推進する派閥があったりする。

google:トランザクションスクリプト ドメインモデル

記事への反応 -
  • 例えばスーファミのFFみたいな、古典的なRPGの戦闘シーンを作っていて、「勇者がモンスターを攻撃する」。 勇者.attack(モンスター); という設計だったとする。 これに「モンスターが毒...

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

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

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

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

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

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

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

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

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

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