ボウリングのゲームスコアを記録するスコアシートは、どのような要素で構成されているでしょうか。 また、それらの構成要素の間にはどのような関係があるのでしょうか。 構成要素や構成要素の関係を表すには「構造のモデル」を使います。 構造のモデルを使って、スコアシートの構成要素や、構成要素間の関係を検討してみましょう。
Note
|
このチュートリアルでは、クラッシックスコアリング( [classic_scoring] )の場合について考えることにします。 |
ゲーム中のスコアシートの例を スコアシートの例 に示します。 この例では、2人でプレーしています。 いまは、2ゲーム目のプレー中で、1人目の6フレーム目の第1投目までゲームが進んでいます。 また、彼らは1フレームずつ交代で投球していることがわかります。 もし、彼らがもう1ゲーム分プレーする場合には、このスコアの下にもう一度2人分のスコアが追加されるでしょう。
Note
|
ボウリングでは、「ゲーム」が、各自が10フレーム分プレーした結果を指す場合と、複数名で10フレーム分プレーしたひと組の結果を指す場合があるようです。 これらを呼び分ける方法がわからなかったので、このチュートリアルでは、前者を「スコア」、後者を「ゲーム」と呼ぶことにします。 実際のボウリング競技において、これらの呼び分方をご存じの方は、その呼び分け方を使うとよいでしょう。 |
スコアシートを観察してわかることをまとめました。
-
スコアシートには、プレー開始日時が記載されている。
-
スコアシートには、プレーヤー名とその人の10フレーム分の投球を記録する欄がある。
-
これを 「スコア」 と呼ぶことにする。
-
-
1つのゲームの進行中は、1フレームごとにプレーヤーが交代して投球する。
-
この方式は 「ベーカー方式」 と呼ばれている。
-
-
スコアシートには、複数人のスコアが記録されている。
-
同時に進行している複数名のスコアのまとまりを 「ゲーム」 と呼ぶことにする。
-
-
プレーヤーの組を単位として複数回ゲームをプレーできる。
これらを元に、スコアシートの構成要素や構成要素の間の関係をモデル図で表してみましょう。
スコアシートの構造を調べるには、まず実際のスコアシートがどうなっているかを調べるのがよいでしょう。 スコアシートに記入されている具体的なデータを使って図を作成するには、オブジェクト図が向いています。
それでは、スコアシートの例を参照しながら、スコアシートのオブジェクト図を作成してみましょう。
まず、 {astah} で作成したプロジェクトに設計モデルを追加しましょう。
Tip
|
「設計モデル」ということばが表すものは、開発に使用する手法や分野によって少しずつ異なっています。このチュートリアルでは、次の2つの側面について表したものを設計モデルと呼ぶことにします。
設計に関わる構成要素は、対象とする業務やサービスを分析して得られることもあれば、開発に採用する資源や方式から得られることもあります。 |
-
構造ツリー上で、プロジェクトを選択する。
-
右クリックしてポップアップメニューを開き、「モデルの追加>モデル」を選択する( プロジェクトにモデルを追加した )。
-
構造ツリー上で、追加したモデルを選択した状態で、プロパティーの「ベース」タブを選択する。
-
「名前」を編集して「設計モデル」とする( モデルに設計用のモデルとして名前をつけた )。
-
入力が確定すると、構造ツリーの表示にも反映される。
「オブジェクト図(インスタンス図と呼ぶこともあります)」を使ってスコアシートに登場するオブジェクトを表してみましょう。 {astah} でオブジェクト図を作成するときは、「クラス図」を使います。
それでは、プロジェクトにクラス図を追加して、オブジェクト図を描いてみましょう。
まず、プロジェクトにオブジェクト図を描くのに使うクラス図を追加します。
-
構造ツリーから設計モデルを選択し、右クリックしてポップアップメニューを開く( モデルにクラス図を追加する )。
-
「図の追加>クラス図」でクラス図が追加される。
追加したオブジェクト図に、図の名前を設定します。 描こうとしているのはオブジェクト図ですので、それがわかるような名前をつけます。
-
追加したクラス図のプロパティーの「ベース」タブを開く。
-
名前を編集して「ゲームスコアのオブジェクト図」とする( 図の名前を「ゲームスコアのオブジェクト図」にする )。
-
ダイアグラムエディタのタイトルやタブにも反映される。
スコアシートの例 や「スコアシートを観察してわかること(決めたこと) 」を観察しながら、スコアシートに記載されている要素をオブジェクト図に追加してみましょう。 まず、「スコアシート」オブジェクトを追加します。
こんどは、図にスコアシートを表すオブジェクトを追加してみましょう。
-
パレットから「インスタンス仕様」を選択して、図に配置する( インスタンス仕様を追加する )。
-
「インスタンス仕様0」という名前のオブジェクトが配置される(末尾の数字は作るたびに新しいものに変わる)。
実際のスコアシートでは、個々のスコアシートを区別するようなはっきりとした名前はついていないこともあるでしょう。スコアシート番号みたいな通番をつけている場合もあるかもしれません。
ここでは、追加したオブジェクトが特定のスコアシートを表すことがわかるよう、他のシートと区別できるような名前(オブジェクト名)をつけましょう。
-
追加したオブジェクトを選択し、プロパティーから名前の編集欄を選択する( 名前を「scoresheet01」に変更する )。
-
オブジェクトの名前に、 スコアシートの例 のスコアシートを示す固有の入力する。ここでは「scoresheet01」とした。
-
オブジェクトの名前の入力を確定すると、ダイアグラムエディタ中のオブジェクトの名前にも反映される。
Tip
|
個々のオブジェクトを識別するためにつける名前のことを、オブジェクト名(またはインスタンス名)と呼びます。 |
追加したオブジェクトは、名前はつけたものの、たくさんあるスコアシートの仲間であるとみなす方法がありません。 それは、このオブジェクトがどんなクラスに属するか決まっていないからです。 それが分かるよう「スコアシート」を表すクラスを定義して割り当てておきましょう。
まず、図中のオブジェクトに割り当てるクラスを用意します。
-
オブジェクト「scoresheet01」を選択した状態で、プロパティーから「新規作成」ボタンをクリックする( オブジェクトを選択してクラスを「新規作成」する )。
-
クラスを定義するダイアログが表示される( クラス「ScoreSheet」を定義する )。
-
「ベース」タブを選択し、名前に「ScoreSheet」を入力する。
スコアシートクラスがどのような情報を保持するのかはまだ検討が必要ですが、ひとますプレー開始日時を保持することは必要そうです。 そこで、プレー開始日時を表す属性を追加しましょう。 ここでは、プレー開始日時を表す属性の名前を「play_date」とします。
-
スコアシートクラスの「属性」タブを選択する。
-
「+」ボタンを押すと属性が追加されるので、名前に「play_date」を入力する( 属性「play_date」を追加する )。
追加した属性も、どんな型なのかを設定しておきましょう。 プレー開始日時を表す属性の型の名前を「Time」とします。
Note
|
「Time」は、Rubyのクラスライブラリのクラスのひとつで、日付と時刻を操作するためのクラスです。このクラスに対応づけする見込みで割当ててみます。 |
-
追加した属性の「型」欄を選択する。
-
「型」欄を編集状態にして「Time」を入力すると、ダイアログに「型になるTimeを新規作成しますか?」というメッセージが表示される( クラス「Time」の追加を促すダイアログ )。
-
「はい」をクリックしてダイアログを閉じる。
-
クラス「Time」が作成される。( クラス「Time」が追加された )。
-
構造ツリーにも追加されたことを確認する。
オブジェクト図においても、構造ツリーにおいても、追加したオブジェクトの名前が変わっているを確認します。
-
「閉じる」をクリックして、クラス定義のダイアログを閉じる。
-
オブジェクトの表示が「scoresheet01 : ScoreSheet」に変わっている。
-
属性「play_date」の属性値を保持する欄(スロットと呼ぶ)も追加されている。
-
構造ツリーにもTimeクラスが追加されている( オブジェクトにクラスやスロットが割当てられた )。
追加した属性「play_date」のインスタンス(スロットと呼びます)に、属性値として具体的な日付データを設定します。
-
オブジェクト「scoresheet01」を選択して、プロパティーから「ベース」タブを開く。
-
属性「play_date」の値に日時、たとえば「2022/02/04 16:18」などと入力する( オブジェクトのスロットに属性値が追加された )。
次に、「スコアシート」を追加したのと同じ手順で「ゲーム」オブジェクトを追加します。 スコアシートの例 の場合ゲームが2組あるので、ゲームのオブジェクトを「game01」、「game02」としましょう。 クラス名は「Game」としましょう。
最初の(1組目の)ゲームを追加してみましょう。
-
パレットから「インスタンス仕様」を選択して図に配置する。
-
オブジェクト名を「game01」とする。
-
クラスを追加して「Game」とする( ゲームを表すオブジェクト「game01」とクラス「Game」を追加した )。
次のゲーム(つまり、2組目)のオブジェクトも追加してみましょう。
-
同様の手順で「game02」を追加する。
-
プロパティーから既存のクラスをプルダウンし、「Game」クラスを選択する( ゲームを表すオブジェクトを追加し、既存のクラスを割当てた )。
こんどは、プレーヤーひとり分のスコアを記録しているスコア部分を追加しましょう。 スコアシートの例 の場合、スコアは4つあります。
このチュートリアルでは、プレーヤー名はスコアの属性と考えることにしておきます。 (もちろん、プレーヤーを独立したクラスと考え、複数のスコアとを関連づけた方がもっとよいでしょう)
それぞれのプレーヤーごとにスコアがありますので、まず、プレーヤーごとのスコアのオブジェクトを追加します。
-
パレットから「インスタンス仕様」を選択して図に配置して「score01」とする。
-
「Score」クラスを追加して、「score01」に割当てる( スコアを表すオブジェクト「score01」を追加した )。
-
「Score」クラスに属性「player」を追加し、「String」クラスを追加して割当てる( 属性「Player」を追加し、クラス「String」を追加して割当てる。 )。
追加したオブジェクトの「player」スロット(スコアクラスの属性playerの、このオブジェクトにおける属性値)を設定します。
-
「score01」のスロット「player」の値に「くぼあき」を設定する( 「player」のスロットの値にプレーヤー名を設定した )。
-
ほかのスコアのオブジェクトも作成する( 残りのスコアのオブジェクトを作成した )
次に、各スコアに記載されているフレームを追加しましょう。 やはり、スコアシートの例 や「スコアシートを観察してわかること(決めたこと) 」を観察しながら、作成します。 ですが、フレームのオブジェクトの数が多いので、この場で作成してみるのは一部だけにします。
それぞれのスコアは、フレームを持っていますので、これを表すオブジェクトを追加します。
-
パレットから「インスタンス仕様」を選択して図に配置して「frame0101」とする。
-
「Frame」クラスを追加して、「frame0101」に割当てる( フレームを表すオブジェクト「frame0101」を追加した )。
-
「Frame」クラスの属性に「frame_no」、「first」、「second」、「spare_bonus」、「strike_bonus」、「total」を追加する。
オブジェクト「frame0101」の各スロットに値を設定します。
-
1組目のゲームの「くぼあき」さんの第1フレームは、1投目7ピン、2投目ミス(0ピン)。
-
第1フレームのトータルは7ピン。ボーナスはなし( 追加したオブジェクトのスロットに値を設定する )。
-
同様にして、ほかのフレームのオブジェクトも作成する( オブジェクト「frame0101」の各スロットの値を設定した )。
オブジェクト図に、スコアシート、ゲーム、スコアのオブジェクトが追加できました。 スコアシートの例 を見ながら、これらの間にはどのようなつながりがあるか考えてみましょう。
-
スコアシートは複数のゲームを記録できるので、スコアシートとゲームにはつながりがありそうです。
-
ゲームでは、複数のプレーヤーのスコアを記録するので、ゲームとスコアにはつながりがありそうです。
-
スコアにはフレームごとのピン数などを記録するので、スコアとフレームにはつながりがありそうです。
つながりがありそうなオブジェクトをリンクを引いてつないでみましょう。
オブジェクト図では、オブジェクト間のつながりを表すには、「リンク」と呼ぶ線でつないで表します。 これは、クラス図におけるクラス間のつながりを表す「関連」のインスタンスにあたるものです(クラス図は、オブジェクト図を元に作成します)。
まず、スコアシートとゲームの間にリンクを引きましょう。
まず、「scoresheet01」から「game01」へリンクを引いてみましょう。
-
パレットから「リンク」を選択する。
-
「scoresheet01」の内部へマウスカーソルを移動し、青枠が表示されるのを待つ( リンクの引き始めのオブジェクトで青枠を表示させる )
-
青枠が表示されたら、マウスのボタンを押したまま「game01」の内部へマウスカーソルを移動する( 青枠が表示されたらマウスのボタンを押したままマウスカーソルをドラッグする )。
-
「game01」にも青枠が表示されたら、マウスのボタンを離すとリンクが引かれる( リンク先のオブジェクトにも青枠が表示されたらマウスのボタンを離す )。
「game02」へも 作成したオブジェクトの間にリンクを引く と同様の手順でリンクを引きましょう( ゲームからスコアへリンクを引く )。
-
1ゲーム目のスコアは、1つ目のゲームにリンクを引く。
-
2ゲーム目のスコアは、1つ目のゲームにリンクを引く。
-
スコアからフレームへもリンクを引きます( 残りのつながりについてリンクを引く )。
これで、 スコアシートの例 の要素を反映したオブジェクト図が作成できました。
「スコアシートの構造をオブジェクト図で表す 」では、実際のスコアシートがどうなっているかを調べるために、スコアシートに記載された内容を使ってオブジェクト図を作成しました。 しかし、特定のスコアを記載した図では、プログラムで扱うような、より一般的なスコアシートを扱えません。 代わりに、オブジェクト図の作成時に考えたクラスを使って、クラス図を使います。 そこで、作成したオブジェクト図 残りのつながりについてリンクを引く を元に、ゲームスコアのクラス図を作成してみましょう。
設計モデルに、「ゲームスコアのクラス図」と「スコアシートクラス」を追加しましょう。
まず、「ゲームスコアのクラス図」を追加します。
-
構造ツリーで、「設計モデル」でポップアップメニューを開き、「図の追加>クラス図」でモデルにクラス図を追加する( モデルにクラス図を追加する )。
-
追加した図を「ゲームスコアのクラス図」とする( 追加した図を「ゲームスコアのクラス図」とした )。
追加したクラス図に、ゲームスコアクラスを追加します。
-
構造ツリーから、オブジェクト図を作成したとき登録した「ScoreSheet」クラスをさがし、選択する。
-
選択したクラスをドラッグ&ドロップして、クラス図に追加する( 既存のクラスを構造ツリーからクラス図に追加する )。
-
構造ツリーから、「Game」クラスを選択する。
-
選択したクラスをドラッグ&ドロップして、クラス図に追加する( 既存のクラスを構造ツリーからクラス図に追加する )。
作成したオブジェクト図( ゲームからスコアへリンクを引く )に記載されているリンクを参照して、リンクでつながっているオブジェクトが属するクラスの間に関連を引きます。
まず、「ScoreSheet」クラスから「Game」クラスへ関連を引きます。
-
パレットから矢印付きの関連を選択する。
-
「ScoreSheet」クラスから「Game」クラスへ向かって関連を引く( 「ScoreSheet」クラスから「Game」クラスへ関連を引いた )。
スコアシートには1ゲーム以上の複数のゲームを記録できます。 クラス間の関連において、このことを表には多重度を使います。
-
「ScoreSheet」クラスから「Game」クラスへ関連を引いた で引いた関連を選択した状態で、プロパティーからターゲットが「Game」の関連端のタブを開く。
-
「多重度」を「1..*」に設定する( 「ScoreSheet」からみた「Game」の多重度を「 1..* 」に設定した )。
スコアシートがゲームを参照するときに使う名前を決めるために、関連端名を設定します。
-
「ScoreSheet」クラスから「Game」クラスへ関連を引いた で引いた関連を選択した状態で、プロパティーからターゲットが「Game」の関連端のタブを開く。
-
ゲームを複数回記録できることを反映して、「名前」を「games」に設定する( 「ScoreSheet」からみた「Game」の関連端名を「games」に設定した )。
スコアシートには、複数のゲームを追加できます。 このことをクラス図に反映する方法がほしいところです。
まず、スコアシートが複数のゲームをとりまとめていること表すのに、「スコアシートには複数のゲームを集約する」役割があるというように捉えてみましょう。 このような役割を表すために、クラスの関連端には「集約」を表すための記法が用意されています。
さらに、同じスコアシートで追加でゲームをやりたいときは、新しいゲームを始めた時点で新しいゲームの記録を始めます。 つまり、スコアシートが作成される時点と、あるゲームが開始される時点が同時とは限らないわけです。 このように、集約する側(ここではスコアシート)のインスタンスと、集約される側(ここではゲーム)のインスタンスが作成される時点が同じではないことを「スコアシートとゲームはライフサイクルが異なる」といいます。 一方で、集約する側とされる側が同時に作成され、同時に破棄されるなら、「ライフサイクルが同じ」と考えます。 集約の記法には、ライフサイクルが異なる場合の「aggregate」、ライフサイクルが同じ場合の「composit」を表すオプションが用意されています。
ここでは、スコアシートが複数のゲームをとりまとめていて、スコアシートとゲームのライフサイクルが異なることを反映して、スコアシート側の関連端に「aggregate」オプションを指定した集約を設定しましょう。
-
「ScoreSheet」クラスから「Game」クラスへ関連を引いた で引いた関連を選択した状態で、プロパティーからターゲットが「ScoreSheet」の関連端のタブを開く(描画時の順序によって該当する関連端がAの場合とBの場合がある)。
-
「集約」のプルダウンメニューから「aggregate」を選択する( 「ScoreSheet」が「Game」を集約していることを示した)。
-
「ScoreSheet」から「Game」への関連が引けた( 「ScoreSheet」から「Game」への関連が引けた )。
ゲームとスコア、スコアとフレームの間にも関連を引いて、関連端も設定してみましょう。
こんどは、「Game」クラスから「Score」クラスへ向かって関連を引きましょう。 1組のゲームには、複数プレーヤーのスコアが記録できます。 つまり、ここにも、スコアシートとゲームの間と同じような関連が引けそうですね。
* 「Score」クラスと「Game」クラスの間に関連を引く。 * 「Score」クラス側の関連の多重度を「1..*」に設定する。 * 「Score」クラス側の関連端には関連端名として「scores」を設定する(複数のスコアが記録できることを反映した)。 * 「Game」クラス側の「集約」のプルダウンメニューから「aggregate」を選択する( ゲームとスコアの間に関連を引いた )。
最後に、「Score」クラスから「Frame」クラスへ向かって関連を引きましょう。 フレームのオブジェクトをいつ用意するのかには、いくつか選択肢があります。 たとえば、ゲームを用意したときに全フレーム分用意すると決めることもできるでしょう。 あるいは、ゲームのい進行に合わせて、その都度フレームを追加すると決めることもできるでしょう。 いくつかの選択肢があるとき、それらからいずれを選択するのかを決定するのは、多くの場合設計時の決定事項です(このこと設計事項、設計項目、設計事由などと呼ぶ人たちもいます)。
このチュートリアルでは、スコアのオブジェクトを用意したときは、常に10フレーム分のフレームのオブジェクトも一緒に用意することとします。 この考えを選択したのは、表示や出力の際に、未投球のフレームについても、それらのフレームのオブジェクトを参照すれば、未投球の処理で特別な扱いが減られると考えたからです。 この場合、フレームとスコアは同時に作成されるので(ライフサイクルが同じなので)、集約の設定もコンポジションにしておきます。
-
「Game」クラスと「Frame」クラスの間に関連を引く。
-
「Frame」クラス側の関連端の多重度を「10」に設定する。
-
「Frame」クラス側の関連端には関連端名として「frames」を設定する(複数のフレームが記録できることを意識した名前にした)。
-
集約のプルダウンメニューの中から「composite」を選択する( スコアとフレームの間に関連を引いた )。
-
「Score」クラス側の関連端の表示が、黒塗りのダイアモンド(コンポジションのシンボル)に変わる。
-
これで、いったん、ボウリングのゲームスコアを表した構造のモデルができました。
ボウリングのゲームスコアを記録するスコアシートの構造を検討しました。
構造のモデルに登場する構成要素や要素間の関連を見つけ出すために、 スコアシートの構造のモデルを作成した手順 のような手順を使いました。
-
スコアシートを観察して、どのような要素で構成されているか洗い出した。
-
スコアシートの実例をそのまま使ってオブジェクト図で表した。
-
オブジェクト図の要素や要素間のつながりを観察して、クラス図を作成した。
まず、オブジェクト図をつくることで、オブジェクトを洗い出し、オブジェクト同士のつながり(リンク)を発見しました。 そして、オブジェクト図を参照しながら、クラス図を作成しました。 クラス間の関連の関連端名や多重度を検討する際は、オブジェクト図におけるリンクの数などを判断材料にしました。