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

ドラクエで学ぶオブジェクト指向(1) 勇者を作ろう!

»

 深い理由はないが、Javaで遊びたくなった。エンジニアの与太話でドラクエとオブジェクト指向の関連性みたいな話をするのは好きだ。つい最近もそんな冗談をどこかでつぶやいた記憶がある。何も考えないでお酒でも飲みながら組んでみようと思う。

 まずは、我らがヒーローを定義することから。基本となる勇者クラスだ。

class Hero

 次にアクセス制限。やはり勇者はいろんなオブジェクトと交流して成長させたいのでpublicにする。privateな引きこもり勇者なんていらない。

public class Hero

 ここまでは、お約束なのであまり深く考えていない。ただ、Heroだと女性の勇者を表現できないことは懸念事項とする。ヒーローとヒロインで挙動が違っていればありがたいが……。

■引数つきコンストラクタ

 次に、勇者の名前を入れる変数を用意する。フィールドは特別な理由がない限り privateにするのは基本である。

private String name = null;

 もちろん、クラスを定義しただけでは、勇者は現世にあらわれない。勇者インスタンスを実体化する手続きであるコンストラクタを定義しよう。

 「なまえを いれてください」

 最初の仕事は名前を登録することだろう。やはり、コンストラクタの引数に名前が欲しい。

public Hero(String name) {
    this.name = name;
}

 名前は4文字の制限があるので、入力チェックをかけたい。名前が4文字以上や空文字の場合は例外を投げる。パラメータ不正なので、IllegalArgumentExceptionをスローする。記号など不適切な文字のチェックもいるかもしれない。

public Hero(String name) {
    if( null == name || name.equals("") || name.length() > 4 )
    {
        throw new IllegalArgumentException();
    }
    this.name = name;
}

 そもそも、入力画面は決められた文字しか入力できない仕様であることを思い出す。必須チェック、文字数チェックもやっているのでパラメータの検証は不要な気がしてきた。ひとまず保留にする。あとで消すことは分かっているが雰囲気は残しておきたい。おそらく普通にValidatorクラスを作ることになるだろう。

■getter/setter

 続いて、名前以外の属性を検討する。すばやさとか攻撃力とかいろいろとあるが、初期段階なのでHP(ヒットポイント)、MP(マジックポイント)だけ定義する。

private int hp = 0;
private int mp = 0;

 Javaの場合、初期化されていないint型は0が代入される。しかし、気持ち悪いので0を設定してみた。Javaで書くときはいつも迷う。

 フィールドにアクセスする場合は専用のメソッドを作成する。getter/setterと呼ばれるメソッドを作成し、値の取得と設定を行う。作り方は簡単で、get + フィールド名(先頭は大文字)、set +フィールド名(先頭は大文字)。setterにはフィールドに代入する引数を用意する、というルールを守るだけ。

public int getHp() {
    return hp;
} public void setHp(int hp) {
    this.hp = hp;
}

 最初からフィールドをpublicにすればよい、という意見もあるが、メソッド経由のメリットはたくさんある。コンパイル時の違いやパターンに持っていきやすいことを説明するのは大変なので省略する。

 それに、HPやMPは0以下にはならない。HPが負の数になれば0にする処理を追加する。

public void setHp(int hp) {
    this.hp = hp;
    if( this.hp < 0 ){
        this.hp = 0;
    }
}

 しかし、この書き方だと問題があることに気づく。hpが一瞬でもマイナスになる可能性がある。マルチスレッドでは致命的だ。チェックしてから代入という順番に書き直す。

public void setHp(int hp) {
    if( hp < 0 ){
        this.hp = 0;
    } else {
        this.hp = hp;
    }
}

 HPが残り少なくなってきた場合や0になった場合に特別な処理をさせたいという要望があるらしい。ここにトリガーを置くことになるかもしれない。HPへの代入は他のパターンも考えられるので、それなりの仕掛けが必要になるだろう。

 同様に最大HP以上に設定されることはないのでフィールドを宣言して上限にも制限をかける。最大HPはまだ決まっていないので仮で0にする。

private int max_hp = 0;
public void setHp(int hp) {
    if( hp < 0 ){
        this.hp = 0;
    } else if ( hp > this.max_hp  ) {
        this.hp = this.max_hp;
    } else {
        this.hp = hp;
    }
}

■趣味で……

 3項演算子にしてみた。こういう書き方は好き。

public void setHp(int hp) {
    this.hp = ( hp < 0 ) ? 0 : ( hp > this.max_hp  ) ? this.max_hp: hp;
}

 3項演算子を知らないエンジニアに時々出会うことがある。入門書に書いてあることを質問されても説明する時間がもったいないので仕事ではあんまり使わないようにしている。本を紹介しても読んでもらえないことが多いし……。昔は丁寧に解説していたが、最近は、あきらめモードに入っている。そういうストレスもここで解消しておこう。

 同様にMPのsetterにも範囲制限を追加した。

public void setMp(int mp) {
    this.mp = ( mp < 0 ) ? 0 : ( mp > this.max_mp  ) ? this.max_mp: mp;
}

■コンストラクタの修正

 フィールドの初期化はコンストラクタの中で設定する。最大HP、MPに代入する10は仮の数字。最終的に外から初期値を取得することになるだろう。

public Hero(String name) {
    if( null == name || name.equals("") || name.length() > 4 )
    {
        throw new IllegalArgumentException();
    }
    this.name = name;
   
    this.max_hp = 10;
    this.max_mp = 10;
    this.hp = this.max_hp;
    this.mp = this.max_mp;
}

■読み込み専用

 最後に、名前へのアクセス手段をまだ作っていなかったことに気づく。ここもgetter/setterのを作りたい。しかし、一度決めた名前は変更できないというビジネスルールが存在する。勇者の名前はRead Onlyなのでsetterメソッドは作らない。

public String getName() {
    return name;
}

 これで、コンストラクタで初期化した名前からは変更できなくなった。もちろん、クラス内部から名前の編集は可能である。大切なのは外部から操作できないということだ。名前変更は特殊なビジネスルールがあると聞いている。詳細はわからないが、クライアントからのリクエストがあれば専用メソッドを用意して、それ相応の手続きを行うことになるだろう。

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

Hero.javaをダウンロード

Comment(0)

コメント

コメントを投稿する