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

登場人物のプロファイル

 
  登場人物はどんな人  
 

オブジェクトは大きく分けると 2 つの顔を持っています。2 つとは外向きの顔と内向きの顔です。外向きの顔は他のオブジェクトから呼び出される顔で、メンバ関数や属性の前に public という修飾詞がついているものがこれに相当します。

内向きの顔には 2 種類あって、自分自身だけが呼び出せるものと、自分を派生したオブジェクトが呼べるものがあります。前者は private, 後者が protected という修飾詞がつきます。

この 2 つの顔、特にメンバ関数についてはシーケンス図から読み取ることができます。シーケンス図で他のオブジェクトからメッセージセンディングされるものが、public な関数になります。また、自分自身にメッセージセンディングしているものが、private な関数となります。たとえば、図 1-7 のシーケンス図では 6 種類のメッセージセンディングが行われています。この中で他のオブジェクトから呼ばれているものは次の 3 種類になり、

  • 回答を問い合わせる
  • Blow, Hit を提示
  • 正解かどうかを提示

自分自身で呼び出しているものは残りの 3 種類になります。

  • 正解を作成
  • Blow, Hit の算出
  • 正解かどうかを調べる

前者が public な関数になり、後者が private な関数となります。

属性に関しては前節で説明したように情報隠蔽を行うため、よほどの理由がない限り public にすることはありません。

それでは、それぞれのクラスについて定義を行っていきましょう。

 

 
  問題作成者 Mastermind クラス  
 

まず、クラスの名前を考えてみましょう。クラスの名前はなるべく分かりやすく、かつそのクラスの機能をよく表している名前を選びましょう。変に省略したりすると分かりにくくなってしまうので、省略はしない方がいいと思います。少しぐらいクラスの名前が長くなったとしても、分かりやすさを重視したほうが後々のためになります。

たとえば、何かのカウントを持つクラスを CntHolder としたとします。Cnt は Count の省略です。もし、これを第三者が見たときに Cnt はなんだか悩んでしまうと思いませんか。Continue か Control か Cascade Naming Template (特に意味はないのですけど...) かもしれません。自分で作ったのだから自分ではすぐ分かると思うでしょうけど、もしこのクラスを作ってから 1 年後に見直したときにすぐに分かるでしょうか。

それよりも、はじめから CountHolder にした方が無駄な誤解を受けなくてすみますね。

本題に戻って問題作成者のクラス名を決めましょう。

問題を作成するから QuestionMaker にしましょうか。それとも.... こんなとき筆者は片手に辞書をもちながら決めていきます。和英辞書でもいいのですが、英英辞書や類語辞典なども役に立ちます。さっそく、辞書で調べてみましょう。

Mastermind ってどういう意味だと思いますか。はじめてこの言葉を聞いたときには Master Mind と 2 つの単語だと筆者は思っていたのですが、あらためて辞書を引くと Mastermind で一語のようです。意味は「立案者」、「指導者」というものです。動詞もあって、「... を巧み立案する」という意味です。

ということは Mastermind は問題を巧みに作成する人という意味でゲームの名前に使われていることが分かります。問題作成者のクラス名にぴったりではないですか。それでは、問題作背者は Mastermind クラスにしましょう。

次は Mastermind クラスの属性を決めましょう。

一番重要な属性は正解ですね。正解は数字なので int 型にし用と思ったのですが、一番初めの数字が 0 の場合もあるので、そのままではちょっと問題がありそうです。そこで、それぞれの桁の数字を配列で持たせることにしましょう。

正解は属性にすべきであるとすぐ分かるのですが、それ以外はどうでしょうか。Mastermind が持ちうる変数としては回答者オブジェクト、回答できる回数、回答した回数などがあります。これらの変数はすべて一つの関数の中だけで使用されるローカル変数として扱うこともできます。回答できる回数は定数としておきたいので、属性にしておきましょう。

どれを属性にするかローカル変数にするかを決めるのはなかなか大変です。ただ、設計時に決めた属性を絶対に守らなくてはいけないわけではありません。もし、実装時に属性が新たに必要であったり、属性を削除してもかまわないことがあれば、柔軟に変更してもかまわないと思います。

最後にメンバ関数を定義しましょう。

メンバ関数の候補はシーケンス図に表れています。図 1-7 で問題作成者に向かうメッセージセンディングは

  • 正解を作成
  • Blow, Hit の算出
  • 正解かどうかを調べる

の 3 種類です。これらの手続きはみな自分自身に向かっているので、private な関数になります。Blow と Hit の算出は別々の関数にしたとして、それぞれ関数は

  • makeCorrectAnswer
  • countBlow
  • countHit
  • checkAnswer

としましょう。

makeCorrectAnswer 関数が正解を作るときに必要な情報はないので、引数はなしになります。処理の結果である正解は属性なので、関数の戻り値としなくても、関数内で属性に直接書き込むことができます。したがって、makeCorrectAnswer 関数の戻り値はありません。

countBlow 関数はどうでしょうか。Blow を数えるには、正解と回答が必要ですね。正解は属性なので、引数としては必要がないため、回答だけを引数にします。回答も正解と同じように、int 型の配列で扱うことにしましょう。戻り値は Blow の数になるのが自然なので int 型になります。

countHit 関数は countBlow 関数とまったく同じ引数と戻り値の型をとります。

最後の checkAnswer 関数はどうでしょうか。正解かどうかを調べるには、Hit 数と正解の桁数が分かればいいのですが、政界の桁数は正解があれば求めることができます。それなので、checkAnswer 関数の引数は Hit 数になります。戻り値は正解か不正解かを表せばいいので、boolean 型にして、true だったら正解、false だったら不正解としましょう。

ところで、Mastermind クラスには public な関数はないのでしょうか。実はあります。

図 1-7 のシーケンス図は Mastermind のすべての動作を記述しているわけではないと前節で説明しました。図 1-7 は問題を作成して、複数回ある回答の問い合わせの内、はじめの問い合わせについて記述してあります。実際には 2 から 6 のメッセージセンディングは複数回行われます。この問い合わせを複数回行うループを記述する関数、つまりゲームを管理する関数が実際には存在するわけです。この関数を呼ぶことでゲームが始まり、関数から戻るときにゲームが終了します。この関数は playGame という名前にしましょう。

関数名は通常何らかの動作を行うことから [動詞] + [目的語] の形になります。Java では小文字ではじまって、単語の区切りで大文字にするのが一般的です。関数名もクラス名と同じく、なるべく分かりやすい名前にしましょう。変な省略を使うべきでないのもクラス名と同じです。

属性、メンバ関数をまとめると Mastermind クラスは次のようになります。

Mastermind クラス

Mastermind のゲームを管理するクラス
正解の生成や回答者からの回答を問い合わせを行い、Blow と Hit を算出する

属性 private int[] correctAnswer 正解となる数字
要素は正解の桁の数字
private static final int answerFigure 正解の桁数
メンバ関数 public void playGame() Matermind を開始して、ゲームを行う
private void makeCorrectAnswer() 正解を作成する
private int countBlow(int[] answer) 回答から Blow をカウントする
private int countHit(int[] answer) 回答から Hit をカウントする
private boolean checkAnswer(int hit) 正解かどうかを調べる
 
  回答者 Solver クラス  
 

Mastermind クラスの次は、もう一人の登場人物である回答者です。

Mastermind クラスと同じように名前から決めていきましょう。あまり難しく考えないで、ここでは単純に問題を解く人なので Solver という名前にしようと思います。

次は属性です。属性の候補としては

  • 回答
  • Blow 数
  • Hit 数
  • 正解かどうかを示すフラグ

があります。フラグというのは、「正しい/間違っている」とか、「処理をした/しない」など 2 つのとりうる値があるときによく使われる言葉です。通常、Java ではフラグは boolean 型の変数で表されます。ここでは「正解/不正解」を表すために使われます。

これらの候補はさしあたり属性にできない理由もないので、すべて属性にしてしまいましょう。もし実装の時に不都合があれば、その時にまた考え直して見ましょう。

さて次はメンバ関数です。ここでも Mastermind クラスと同じようにシーケンス図からメンバ関数を抽出していきましょう。図 1-7 のシーケンス図で回答者に向かっているメッセージセンディングは次の 3 種類です。

  • 回答を問い合わせる
  • Blow, Hit を提示
  • 正解かどうか提示

この 3 種類のメッセージセンディングはすべて問題作成者からのものなので、すべて public な関数となります。関数の名前は次のようにしてみました。

  • getAnswer
  • setBlowAndHit
  • setResult

getAnswer 関数は回答を渡すだけなので、引数はなし、戻り値は int 型の配列で表される回答になります。

setBlowAndHit 関数はどうでしょうか。Solver クラスから見れば、Blow 数と Hit 数を設定するのですから、Blow 数と Hit 数は必要になります。したがって、引数は int 型の Blow 数 とやはり int 型の Hit 数になります。

setResult も同じように考えると、正解かどうかのフラグを設定するので、引数には boolean 型の結果が必要になります。

この段階ではこの 3 種類の関数だけを定義しておきましょう。実装の時に必要になる関数がでてくれば、その時にまた追加していきます。

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

 

Solver クラス

Mastermind ゲームの回答者
問題作成者に対し、回答を行う。

属性 private int[] answer 回答
各桁の数字が配列の要素になる
private int blow Blow の数
private int hit Hit の数
private boolean result 回答が正解かどうかを示すフラグ
メンバ関数 public int getAnswer() 回答を行う
public void setBlowAndHit(int blow, int hit) Blow 数と Hit 数の設定を行う
public void setResult(boolean result) 正解かどうかを示す

やっと登場人物からクラスを導入することができました。本節で出てきたことをまとめると

  • メンバ関数はシーケンス図に表れる
  • クラスや関数、変数の名前はなるべく分かりやすくし、分かりにくい省略などはしないようにしよう
  • 他のオブジェクトからメッセージセンディングされるものだけ public な関数にしよう
  • 属性は基本的には private

ここまでくれば、半分ぐらいは終わったも同然です。次節から、待ちに待った実装に入ることにしましょう。

(Aug. 2000)

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