Go to Previous Page Go to Contents Go to Java Page Go to Next Page
Second Step of Java
 
 

じゃんけんのクラス抽出

 
  クラスはひとつ、オブジェクトはたくさん  
 

じゃんけんのクラスを決めていきましょう。やはり、Mastermind の時と同じように登場人物をオブジェクトと考えていきましょう。ただ、Mastermind と違うのは登場人物の 1 人であるプレイヤーは複数いるということです。

しかし、よく考えてみるとプレイヤー A とプレイヤー B は名前は違うだけで行う動作はまったく同じです。ということはプレイヤー A とプレイヤー B は同じクラスで定義することができます。プレイヤー A とプレイヤー B はクラスから生成した異なるオブジェクトと考えるわけです。

そうすると、じゃんけんで登場するクラスは

  • プレイヤー : Player クラス
  • 審判 : Judge クラス

の 2 つになります。

クラスの定義を行う前に、2 つのクラスのオブジェクトの関係を考えてみましょう。シーケンス図からオブジェクトの関係がある程度導き出すことができます。シーケンス図は大活躍ですね。

オブジェクト指向ではクラス間の関係として次にあげる 4 種類の関係を考えます。

  • 関連 Association
  • 汎化 Generalization
  • 実現 Realization
  • 依存 Dependence

関連はあるオブジェクトに他のオブジェクトが結びつくことです。たとえば、あるオブジェクトにメッセージセンディングをするときは、相手のオブジェクトが知らなくてはできません。この相手のオブジェクトのことを知っているということが関連をもつということになります。

シーケンス図から導き出すことができるのは、この関連です。

汎化はあるクラスをより具体的なものにしたときの関係を指します。たとえば、動物というクラスに対して、犬というクラスは汎化の関係にあります。犬は動物の一種なのですが、動物のある種をより具体的に表したものだからです。この関係を is a kind of と呼ぶこともあります。先ほどの例だと 「犬」 is a kind of 「動物」ですね。

Java だと汎化は extends で実装します。

次の実現はある操作を定義しているクラスと実際にその操作を実装したクラスとの間の関係になります。Java ではインタフェースを implements するときがこの実現に相当します。

依存はあるオブジェクトが他のオブジェクトを使用するときに当てはまります。

ここではじゃんけんで使われるクラス間の関連を考えていきます。

図 2-3 のシーケンス図で Judge オブジェクトは Player オブジェクトに対して「手の問い合わせ」を行っています。ということは、Judge オブジェクトは何らかの方法で Player オブジェクトを知っていなければ、メッセージを投げることができないはずです。

この知っているということが関連に相当します。

一方、Player オブジェクトは Judge オブジェクトからのメッセージに対して答えるだけで、自分から Judge オブジェクトに問いかけることはしません。とすると、Player オブジェクトは Judge オブジェクトを知っている必要がないということです。

この関係を図に表してみましょう。ここでも、UML で定義された図を使用しましょう。クラスとクラス間の関係を表した図をクラス図といいます。このクラス図でじゃんけんに登場するクラスを描いてみましょう。

じゃんけんのクラス図
図 2-4 じゃんけんのクラス図

クラス図ではクラスは四角で表されます。四角の中に書いてあるのがクラス名です。クラス間に関連がある場合は、クラスを実践でつなぎます。図 2-4 ではクラスを結ぶ線に矢印が描いてあります。これは明示的に単方向の関連であるということを示しています。

前述したように Judge オブジェクトは Player オブジェクトを知っています。しかし、Player オブジェクトは Judge オブジェクトを知りません。図 2-4 では Judge オブジェクトは Player オブジェクトを知っているが、Player オブジェクトは Judge オブジェクトを知らないというということを表しているわけです。

矢印がないときには、通常双方向の関連を示します。

線の上に書いてある数字はオブジェクトが結びつく相手のオブジェクトの数を表しています。この数字は省略することが可能です。

Player の側に書いてあるのは、Judge オブジェクトが 2 つ以上の Player オブジェクトを知っているということを表しています。

数字の書き方には次のようなものがあります。

記述法 意味
1 1 つのオブジェクト
* 複数のオブジェクト
0..* オブジェクトが 0 以上
1..* 1 つ以上のオブジェクト

図 2-4 のように 2..* という書き方はあまりしませんが、じゃんけんは 2 人以上いないとできないので、このように記しました。

このクラス間の関連は Java で実装するときには属性として、相手のオブジェクトの参照を持たせるようにします。相手が複数の時には配列や後で説明するコレクションを使用したりします。

クラス間の関連も分かったので、それぞれのクラスについて定義していきましょう。

 

 
  Player クラス  
  Mastermind の時と同じようにシーケンス図を利用してクラスの定義を行ってみます。

図 2-3 を見てみると、はじめに審判はプレイヤー A に手を問い合わせています (メッセージ 1)。プレイヤー A は手を選択し (メッセージ 2)、戻り値で審判に選択した手を渡します (メッセージ 3)。同じことを審判はプレイヤー B にも行います。プレイヤー B の処理もプレイヤー A とまったく同じです。審判はすべてのプレイヤーから手を問い合わせたら、判定を行います (メッセージ 7)。

このシーケンスからクラスに必要な属性とメンバ関数を引き出していきます。

Player オブジェクトは審判から手の問い合わせを受けますから、public なメンバ関数で定義することができます。関数名は getHand にしましょう。ところで、戻り値のじゃんけんの手ですが、どのように表せばいいでしょうか。いろいろ手法があるのですが、定数で「グー」「チョキ」「パー」を表しましょう。

    public final static int GOO = 0;
    public final static int CHOKI = 1;
    public final static int PA = 2;

int 型で「グー」「チョキ」「パー」を表せるため、getHand 関数の戻り値は int 型になります。

    public int getHand();

public なメンバ関数はこれだけですが、プレイヤーだけが使う private なメンバ関数も決めなくてはいけません。シーケンス図にも出てきている、手を選択するというメンバ関数が必要です。手を選択するので selectHand という名前にしましょう。戻り値は選択した手と考えるのが自然ですから、getHand 関数と同様 int 型にします。

    private int selectHand();

プレイヤークラスの属性はどうでしょうか。自分が出す手を属性として持っていてもいいのですが、審判が問い合わせたときに新たに手を選択するので、属性として持っている必要はなさそうです。でも、それではつまらないのでプレイヤーの名前を属性にしてみましょう。ついでに名前を取り出すための関数 getName もメンバ関数に付け加えてみます。

これをまとめるとプレイヤークラスは次のようになります。

Player クラス

じゃんけんを行うプレイヤー。
審判からの手の問い合わせに答えて、手を選択し、提示する。

属性 private String name プレイヤーの名前
public final static int GOO じゃんけんのグー
public final static int CHOKI じゃんけんのチョキ
public final static int PA じゃんけんのパー
メンバ関数 public int getHand() じゃんけんの手を提示する関数
戻り値はじゃんけんの手
private int selectHand() じゃんけんの手を選択する関数
戻り値は選択したじゃんけんの手
public String getName() プレイヤーの名前を返す関数

 
  Judge クラス  
 

次に Judge クラスに考えてみましょう。まずはメンバ関数からいきましょう。

Judge オブジェクトは他のオブジェクトからメッセージセンディングされることは図 2-3 を見てもわかるようにありません。したがって、public な関数はいらないはずです。

private なメンバ関数として図 2-3 の「判定を行う」処理があります。判定ですから judge という名前にしましょう。引数はプレイヤーの人数が決まっていれば

    private int judge(int handA, int handB);

のようにプレイヤーごとの手を引数にしてもいいのですが、プレイヤーの人数を決まっていないときにはこれは使えません。そこで、配列にしておきましょう。

    private List judge(List hands);

配列にしましょうといいながら List というクラス (正確には java.util.List) を使用しているのは、配列より List クラスの方が使いやすいからです。List クラスと配列を比較したときに、List クラスが使いやすいところをあげておきます。

  • 初期化時に要素数を気にしなくてもいい。
  • 要素を容易に追加、削除できる。
  • 複数のクラスのオブジェクトを同時に保持することができる。たとえば A クラスのオブジェクトと B クラスのオブジェクトをいっしょに持つことができる。
  • 要素を検索したりする関数や並べ替えのためのクラスが用意されている。

List クラスは実際はインタフェースで、実装クラスとしては ArrayList クラスや LinkedList クラスを使用します。一般的には ArrayList が使われることが多いです。このような複数の要素を保持するためのクラスをまとめたものをコレクション API といい、Java 2 から導入されました。Java 2 以前では Vector クラスなどが使用されていましたが、List の方が高機能なので、古いバージョンとの互換性を保つ意外には Vector クラスを使用する必要性はないと思います。

では、judge 関数に戻りましょう。引数は List オブジェクトの hands です。hands の各要素はじゃんけんの手を表し、Integer クラスのオブジェクトで表されています。

戻り値は勝者のリストにしましょう。あいこの場合は勝者はいないので、からっぽのリストになります。

ところで、本当に public な関数はないのでしょうか。図 2-3 に明確に示されていませんが、じゃんけんを行うという関数があるはずです。この関数の中でプレイヤーに手を問い合わせ、判定を行うという処理を行います。ゲームを行うので playGame という名前にしましょう。引数や戻り値は特に必要ないです。

次に属性です。Judge オブジェクトが持たなくてはいけない情報にはなにがあるでしょうか。クラス間の関連を調べたときに、Judge クラスは Player クラスへの関連があることが分かりました。ということは、Judge クラスの属性には Player オブジェクトのコレクションがあるということですね。

また、問い合わせた結果も属性に入れておきましょう。

Judge クラスについてまとめたものを次表に示しておきます。

Judge クラス

じゃんけんの審判。
プレイヤーに手の問い合わせを行い、判定を行って勝者を決める。

属性 private List players じゃんけんのプレイヤーを保持する。players の要素は Player オブジェクト。
private List hands プレイヤーが出した手を保持する。hands の要素は Integer オブジェクト
メンバ関数 private List judge(List hands) じゃんけんの手の判定を行う
戻り値は勝者のリスト。あいこの時には要素をもたないリストが返る。
public void playGame() じゃんけんを行うメインルーチン

図 2-4 のクラス図にクラス名しか記述しませんでしたが、属性やメンバ関数も記述することができます。図 2-5 に一般的なクラス図の記述法を示します。

クラスの記法

図 2-5 クラス図におけるクラスの記法


クラス表す四角を 3 つに区切って、1 番上にクラス名、2 番目に属性、最下段に操作 (メンバ関数) を記述します。属性などの型は名前の後にコロンで区切って表記します。また、属性や操作の public や protected などの可視性は次表の記号を属性や操作の前に記述することで表します。

記号 意味
+ public
# protected
- private

クラス図でクラスを表すときには、属性や操作の部分は省略可能です。しかし、属性か操作の一方だけを省略するときは属性か操作か分かるように区切りの線は記述しておくほうがいいと思います。

属性や操作を図 2-4 に加えたものを図 2-6 に示しました。

じゃんけんのクラス図

図 2-6 じゃんけんのクラス図


気づかれた方もいらっしゃると思いますが、図 2-6 の Judge クラスの属性には players がありません。Judge クラスから Player クラスへの関連を実現しているのが、Player オブジェクトへの参照を保持している属性 players だからです。すでに関連の線で Players オブジェクトへの参照が表されているので、わざわざ属性には描かないということです。

ただし、関連の線では属性名が分からなくなるので、線の根元のところに名前を記述することもあります (これをロール名とよびます)。

この節のまとめを次に示しました。

  • クラスの定義やクラス間の関係を表すためにはクラス図を使用する
  • クラス間の関係には関連、汎化、実現、依存がある
  • オブジェクトの振る舞いなどから関連を導出できる

ここまで、UML で定義された図のうち、ユースケース図、シーケンス図、クラス図が出てきました。アプリケーションを作るときには、少なくともこの 3 種類の図を (シーケンス図の代わりにコラボレーション図を使うことがありますが) 描いたほうがいいと思います。ソースを書く前に、まずこれらの図を記述することで、アプリケーションの構成や処理の流れを整理することができます。

どんな小さいアプリケーションでも欠かさず図を表す習慣を身につけましょう。

(Oct. 2000)

 
 
Go to Previous Page Go to Contents Go to Java Page Go to Next Page