技と名がつくと深入りしてしまうスキルマニアのエンジニア

ドラクエで学ぶオブジェクト指向(3) 勇者とモンスターは似たもの同士

»

 デザパタの前に継承の話をしていなかったことに気付く。プログラムの入門書を書くつもりはないので、基本的に順番はデタラメである。技術はつまみぐいが面白いのだ。

■継承 インヘリタンス

 第1回第2回で勇者クラスについて考えてきた。このまま肉付けして作り込んでいくのもいいが、このあたりでざっくり大枠を決めておきたいと思う。

 Heroクラスに、いくつかの属性を定義してきた。しかし、まだまだ足りてはいない。ドラゴンクエストI 公式ガイドブックによると、ちから、すばやさ、みのまもり、攻撃力、守備力、経験値、Gというパラメータを持っている。

 一方、モンスターにはHP、MP、すばやさ、攻撃力、守備力、経験値、Gがある。共通の項目を見つけたら抽象化のチャンスだ。

 勇者とモンスターの共通項目として、Battlerクラスを定義する。ひとまずメソッドは考えないで、共通要素を属性にする。Battlerクラスは実体がないので抽象クラスだ。

abstract public class Battler

 勇者クラスとモンスタークラスはBattlerクラスを継承する。

public class Hero extends Battler implements Serializable

public class Monster extends Battler

 当然、モンスターは冒険の書には保存しないのでシリアライズする必要はない。

■プルアップでリファクタリング

 ちから、みのまもりは勇者だけが持つのでHeroクラスで宣言した。

/** ちから */
private int power = 0;
/** みのまもり */
private int gurd = 0;
/**
* @return the power
*/
public int getPower() {
    return power;
}
/**
* @return the gurd
*/
public int getGurd() {
    return gurd;
}

 もともと、勇者クラスで定義していたプロパティとsetter/getterをBattlerクラスに持ち上げる。共通処理は親クラスへ。サブクラスで重複する処理を書く必要はない。

/** 名前 */
protected String name = null;
/** ヒットポイント */
private int hp = 0;
/** マジックポイント*/
private int mp = 0;
/** 最大ヒットポイント */
protected int max_hp = 0;
/** 最大マジックポイント */
protected int max_mp = 0;

 最大HP、最大MPは継承したクラスでも使いたいので protected にした。protectedにするのは属性にするかsetterにするかは悩ましい。HP、MPはロジックが入るため、サブクラスでもアクセサ経由でアクセスさせいたい。アクセス修飾子はprivateのままにする。

 もっと悩ましいのは、経験値と所持金であるG(ゴールド)の取り扱いだ。

 勇者にとって、経験値とGは獲得するものであるが、モンスターの経験値とGは与えるものである。モンスターが勝利しても勇者の経験値が得られない。同じものでも考え方が違うので、モンスター側ではgivenEx、givenGとした。

Classdiagram3_1_13

 Gは勇者ひとりで独占しているのではない。パーティ全体のお金である。II 以降のシリーズを考えた場合に勇者クラスに属性Gがあるのは好ましくないだろう。パーティ全体を表現するクラスがいずれ必要になるだろうが、これも保留とする。

 勇者とモンスターそれぞれのサブクラスに属性を追加し、getterメソッドだけを用意した。setterは必要になったときに追加すればよいと思っている。もっともパラメータ上昇の処理はどうするか未定のままだ。そんなのが多くてスイマセン。

■オーバーライド

 次は、攻撃力と守備力にとりかかろう。ここは今までと事情が違う。Battlerクラスでまずgetterを抽象メソッドとして定義する。

/**
* @return the offense
*/
abstract public int getOffense();
/**
* @return the deffense
*/
abstract public int getDeffense();

 なぜ、抽象メソッドにするか?

 モンスターは攻撃力と守備力というパラメータをダイレクトに持っている。

  • モンスター攻撃力=攻撃力
  • モンスター守備力=守備力

 であるが、勇者には武器と防具が存在する。

  • 勇者攻撃力=ちから+武器の攻撃力
  • 勇者守備力=みもまもり+防具の攻撃力

 同じ攻撃力、守備力でもアイテムによる補正が入る。

 処理が違うので抽象メソッドを使い、サブクラスで実装することを親クラス側で強制するためだ。これもBattler型変数で勇者とモンスターを扱うための方法である。

 モンスタークラスは特に考える必要はない。攻撃力と守備力を定義してメソッドを用意するのみ。

/** 攻撃力 */
protected int offense = 0;
/** 守備力 */
protected int deffense = 0;
@Override
public int getDeffense() {
    return deffense;
}
@Override
public int getOffense() {
    return offense;
}

 ちなみに@Overrideというのは、親クラスのメソッドをオーバーライドしていることを表すアノテーションである。今回は、継承の話がしたいのでスルーにした。便利な存在なので知っておいて損はない。それどころか、最近のOOPでは必須事項である。覚えておかないと仕事にならない。詳しく説明したいがキーワードの紹介だけに留めておく。

 武器防具の実装は、当然のごとくノープランなので形だけ定義しておく。「ちから」と「みのまもり」を返すようにしてみた。

@Override
public int getDeffense() {
    // TODO 武器力の補正値を追加
    return power;
}
@Override
public int getOffense() {
    // TODO 鎧、カブト、盾の補正値を追加
    return gurd;
}

 他にも、バイキルトやスカラ系の呪文などの要素が考えられる。敵味方入り乱れるともっと大変なことになるだろう。今の段階では、こういうことを意識しているだけで十分である。

 大事なことなのでもう一度繰り返しておこう。Battlerクラスを使うことによって hoge.getOffense()で勇者でもモンスターでも意識せずに攻撃力の取得ができる。この考え方を推し進めると勇者クラス、モンスタークラスに関係なく、Battlerクラスで戦闘を行うことができるのだ。いわゆるポリモルフィズムというやつである。

 すばやさの順でBattlerクラスをキューに詰めて、勇者とモンスターの違いを意識することなく、こうげきメソッドを実行させることができるだろう。

■登場人物たち

 すべての命のためにCreatureクラスを導入する。Creatureと命名したのはモンスターも含まれているからだ。

public abstract class Creature

 ここからさらに、王様や武器屋の商人のためにこのクラスを継承したPersonクラスを定義する。ぱふぱふ娘も、このクラスを継承することになるだろう。

public class Person extends Creature

 あわせて、BattlerクラスもCreatureクラスを継承する。

abstract public class Battler extends Creature

 人間とモンスターを同じくくりになることに違和感を感じる人もいるかもしれない。もちろん、城の兵士や、村人を継承して勇者というクラス設計も悪くない。人間とモンスターという対立図式も自然な考え方である。今回は戦闘システムに着目したのでBattlerクラスで闘うものたちをまとめたにすぎない。

 もっと考察を進めていけば、よりよい設計をみつけることができるのかもしれない。しかし、クラス設計というものは視点によって変わることもありえると思っている。究極の再利用性を求めるのも悪くはないが、最初は戦闘システムがやりたい。

■基底クラス

 最後に、全体を統括するクラスを作り、Creatureクラスを継承させる。

public abstract class DqObject

public abstract class Creature extends DqObject

 DqObjectは武器や、防具、呪文クラスなどをまとめる基底クラスという役割を果たす。登場人物だけでなく、大道具や小道具も含めた総括的なアイテムを取り扱う。今の段階ではまったく必要のないクラスだが今後、システムが複雑になってきたときに役に立つときがくるだろう。

Classdiagram3_2_4

 どこか遠くからレベルアップを告げるアラート音が鳴り響く……。

hagane_dq_src3.zipをダウンロード

Comment(0)

コメント

コメントを投稿する