たとえば、現実的ユースケースには、表示したり入力したりする項目を明らかにした画面イメージが添付されていたりします。
この詳細な画面イメージというのは必須ではありません。
大まかな画面(に限らずユーザーインターフェース一般)を説明したもの(ストーリーボードといいます)を作っておいて、もっと実装が進んだ段階でユーザーインターフェースの詳細を定義していく方法もあります。
実装前に画面の承認を得たいなどの理由で、詳細にインターフェースを記述しておきたい場合には、この現実的ユースケースは便利です。
Buy Items with Cash(商品を現金で購入する) | |
---|---|
Actor | お客さん(イニシエータ:ユースケースを開始する人のこと)、操作員 |
目的 | 販売情報を把握し、現金の支払いを受ける |
概要 |
お客さんは購入しようとする商品をもってレジに行く。 操作員は購入される商品を記録し、支払いを受け取る。 終わったら、お客さんは商品をもってその場を去る。 |
分類 | プライマリおよび現実的 |
Actorの行動 | システムの応答 | ||
---|---|---|---|
1 |
お客さんがレジへ商品を持ってくる。商品は複数ある場合もある。 |
|
|
2 |
操作員は図1のAに商品コードを入力する。 同じ商品がある時は、操作員は個数をBに入力できる。 情報を入力し終わったら、Hボタンを押す。 |
3 | 商品の価格を判別し、現在の取引トランザクションに商品情報を追加する。商品の説明をCに表示し、価格をDに表示する。 |
4 | 商品入力が終了すると操作員はレジに商品入力の完了を通知するためにIボタンを押す。 | 5 | 販売金額の合計を計算し、Eに表示する。 |
6 | 操作員はお客さんに合計金額を知らせる |
|
|
7 | お客さんは合計金額を現金で支払う。お釣が必要な場合もある。 |
|
|
8 | 操作員は現金を受け取り、その額を記録するために、Fに入力し、Jボタンを押す。 | 9 |
お客さんへのお釣の金額をGに表示する。 不足の場合はマイナスで表示する。 不足でない場合はレシートを出力する。 |
10 | 操作員はお釣の分の現金を取出す。操作員はお釣とレシートをお客さんへ渡す。 | 11 | 完了した取引を保存する。 |
12 |
お客さんは購入した商品を持って去る。 操作員はKボタンを押して終了させる。 |
|
|
まあこんな画面は実際にはないでしょうが、どんな画面でどんな動きをするのか、見えてくるのがこの「現実的」ユースケースです。
協調図も、シーケンス図も、オブジェクト同士がどんなふうに関連しあいながら相互的に作用するのかということを説明するためのものです。
で、この相互作用図、協調図でもシーケンス図でも同じことを説明できます。
…っとそうそう、いきなり相互作用図を書くのはダメです。
そうそう、今回からなのですが、作画ツールを「Pattern Weaver」というものに変えました。
ツールの紹介はさておき、ちょっと説明をしてみましょう。
だれが計算するか。
この小計を計算するのは「入力商品」そのものがスマートですよね。数量を持っているのは「入力商品」ですし、自明細のなかでいくらなのかというのは、「入力商品」の責任分担として知っておいていいことだと思います。
「現金支払」は「合計金額」を持っていません。
さあ終わりました…次はいよいよ……っと、いえいえ、もう一つ、大事な協調図が残っています。
そうそう、クラス図の点線の説明をしましょう。
ここで上げたのは本当に駆け足ですし、いちおう「オブジェクト指向でやってみた」というところです。
協調図
協調図は「コラボレーション図」、略して「コラボ図」と呼ばれることもあります。
これは、相互作用図(インタラクションダイアグラム)の一つで、相互作用図にはこの協調図とシーケンス図があります。
どちらも、オブジェクト同士の相互作用──つまり、動的な関係を示す図です。
概念図、クラス図で静的な関係を示し、そして動的な関係を相互作用図で示すのが、オブジェクト指向設計での常道です。
動的な関連を「オブジェクトが通信する」とか「メッセージ」と呼びます。オブジェクト同士が何かメッセージをやり取りしているというイメージなんですね。
一つ一つの説明のわかりやすさでいえば、シーケンス図のほうが上でしょう。
#分析フェーズでやった「システムシーケンス図」はシーケンス図の一種です。
では協調図のメリットは何でしょうか。
まず1つはスペース効率です。同じスペースなら、たいてい協調図のほうが多くのことが書けます。
もう1つは補助情報の豊富さです。条件付きや繰り返しメッセージなども協調図のほうが書きやすいです。
まあ、シーケンス図は、分析フェーズで使ってみましたから、こんどは協調図を書いてみましょう。
これ、面白いから書きたくなるんですけど、我慢。
概念モデルと、現実的(または分析フェーズでつかった本質的のでもいいです)ユースケースが必須です。
そう、これは、ユースケースをどうやって各々のオブジェクト(概念)の連携で実現するかということを示す、いわば図で書いたプログラムのようなものなんです。
ですから、これを書く前に、どんなユースケースなのかという前提条件、なにが使えるのかという概念図をきっちりと仕上げましょう。
ちなみに、システムシーケンス図があると、どこから書けばいいのかわかりやすいです。
そう、アクターがシステムに働きかけるところが、入り口です。
協調図 - enterItem
すいません使い慣れないツールを使っているもので、横長になってしまいました。
次回からは縦長っぽくなるようにがんばろうと思います…(^^;
Javaで組まれているのですが、フリー(印刷するときに版権がインポーズされるタイプ)で、クラス図だけでなく様々な図を書くことができます。
テックアーツという会社のサイトからダウンロードできます。
製品のサイトはこちらです。
紹介してくれた若月さん、ありがとうございました。
さりげなく「デザインパターン」を2つ(クリエータとコンテナ)使ってますのでお判りのかたはほくそえんでいただいて。
まず操作員からでてる矢印の「enterItem」。これには番号がありません。これは外からのメッセージなので番号を付けないというルールです。
番号がついているのは、基本的に内部のメッセージです。
最初に、番号無しのメッセージが飛んできて、それをミナモトとして、番号順にメッセージが飛んで処理を行うというふうに読んでいただければいいかと。
そう難しいものではありませんね。
ちょっと特殊な表記の部分だけを説明します。
1: [new sale] create()
というのがありますね。この[]は条件を指定するものです。ここでは、最初のsaleの場合のみ飛んでくるという意味です。
あと、オブジェクトが「:オブジェクト」となっているのにお気づきでしょうか?
これは、「インスタンス」を表します。「クラス」ではなくて、クラスから生成された個々のオブジェクト(=インスタンス)だということを示してます。
もう一つ。四角が重なってるのがあります。「:商品情報」とか。これは、複数のインスタンスがあるということを示します。
enterItemに関してはこんなところでしょうか。
意図的に説明を省いた箇所もありますが…雰囲気がつかめたら、自分で手を動かして書いてみてください。
協調図 - endSale
endSaleってなにをするんでしたっけ。
…システムシーケンス図によると…、「商品入力が終了すると捜査員はレジに入力商品の完了を通知する」っと。
えいや、と。
ふふん、楽勝ですよね♪
メモでコーディングまでしてしまっていますが、こういうのもありです。
で、この後どうするかというと…「操作員はお客さんに合計金額を知らせる」っと…おお!?合計金額って、いままでの協調図では見えませんねぇ。
endSaleに付随する協調図
とりあえず、どんなふうに表示するかは置いといて、合計金額を操作員が知っていなければならないんですね。
どんな相互作用でこれを求めるか(まあ直感でわかってしまう程度の単純なものですが)、順を追って分析してみましょう。
えーと計算に必要なのはなにか。
合計金額ってなんでしょうか。すべての「入力商品(販売明細)」の小計を合計したものですね。
で、「販売明細の小計」ってなによ。それは明細にある数量×商品の単価ですね。
(概念図を思い出してください、「入力商品」は数量を持っているんです)
計算するのは、「売上取引」が適任かと思います。
これはExpertパターン(GoFじゃなくてGRASPですが)によっていますが、要するに、「入力商品」をすべて把握しているからです。
ですので、「売上取引」に「total」という操作があると考えるのがスマートでしょう。
ところで、「売上取引」が合計金額を計算するには具体的にどうやればいいでしょうか。それには「入力商品」の小計を「売上取引」が知らなければなりませんよね。
ところで、私はあえて価格を概念図の入力商品に入れていません。
さて、この価格をどうするかが一つのポイントですね。価格は「入力商品」が持っていることにするか?
それとも入力商品は商品の価格は持っていなくて、どこかから調べてくることにするか?
最近の現実のPOSでは、前者が多いですが、ここはあえて後者でいってみましょう。売価変更などのユースケースを考慮にいれていない以上、そっちのほうが自然でしょうから。
Actorは匿名です。これはシステムイベントが起点になっていないからなのですが、別に協調図は必ずシステムイベントからはじまらなければならないという決まりはありません。
相互作用を記述する必要があるシーンを記述すればいいんです。
システムイベントは、基本的に記述する必要があるシーンばかりではありますが。
「商品情報:商品情報」がわかりにくいですね、これは私の名前付け失敗例です。
enterItemの協調図を見直してください。私は「商品情報」という名前の「商品情報クラスのインスタンス」を使ってしまっていて、しかもそれを「入力商品」に渡してしまっています。
番号でいえば2と2.1と3と3.1です。うかつでした。本当はこういった重複は避けるべきです。皆さんお手元でおつくりの場合は他の名前に修正してください。
まあ、失敗の紹介ということで、わたしは涙をのんでこのまま続けます(T-T)
協調図 - enterPayment
enterPaymentがすることはなんでしょう。
支払い金額を記録して、お釣りを計算して、レシートを出力する、ですね。
また、概念図を見てください。
enterPaymentのあとは、「現金支払」が出来ていなければなりませんね。
そしてそれは、「売上取引」と関連をもっているわけです。
このことに注意して、協調図を書いてみましょう。
さあ出来た。なんだ簡単じゃないですか。
終わり終わりっと…いえいえそれは油断です(笑)
お釣りができてません(^^;
お釣りの計算
さて、お釣りを計算しましょう。
だれが計算するといいでしょうか。
協調図からいくと、
のどちらかがよさそうですよね。
ところで、お釣り計算の実際を考えてみましょう。
もらった金額(入力金額)から、買い物の合計金額を引けばいいわけです。
で、「売上取引」でやる場合を考えましょう。
「売上取引」は、「現金支払」を作成しています。
ですから、「売上取引」は、「現金支払」を管理していると思っていいでしょう。
そうすると、「売上取引」がお釣りを計算するなら、「売上取引」の合計金額を入力金額から引けばいいわけです。
これはスマートに見えますね。もうこれでいっちゃいましょうか…いやいや、これはトレーニングですから、現金支払で行う場合も考えてみることにしましょう。
ですので、合計金額を「売上取引」に聞かなければいけません。
つまり、「売上取引」のことを管理しなければならなくなります。
つまり「売上取引」は「現金支払」を管理し、「現金支払」は「売上取引」のことを管理しなければならなくなります。
もぉコレは面倒そうです。また、お互いに依存しあってしまいますので、あるオブジェクトだけを他の用途に使う、というようなこともし難くなると予想できますね。
あるいは、createされるときにパラメータで合計金額をもらってもいいんですが、合計金額と入力金額は別のものですし、意味の切り分けがうまくいきません。
というわけで、やはり、ここは「売上取引」で行うというのがいいでしょう。
それはstartUpと呼ばれるものです。
協調図 - startUp
ほとんどのシステムには、プログラムの起動に関する最初の処理があります。
startUpはまさにそれをあらわしているものです。
これは、システムの最初に行われるものですが、協調図を作るのは、他の協調図を作り終わってからのほうがうまくいきます。
他の協調図が暗黙のうちに「在るもの」として使っているものが何であるか、出揃ってから作ったほうが効率がいいんです。
最初に作るべきオブジェクトはなにか、何が何を作り出すのがスマートなのかというのは、実はなかなか興味深い哲学的な問題なのですが…まああんまり悩まずに作ってみましょう。
てな感じです。
レジが「商品情報カタログ」を持っているというのは、概念図からみるとちょっと不思議な気がしますね。
これは「概念図」があくまでも分析フェーズなのに対し、いまの行程は設計フェーズに入っているため、差異が出てきているからです。
分析フェーズにおいては、商品情報カタログを持っているのは(あえて図示してはいませんが)「お店」でした。
店に、カタログが置いてあるという分析です。
しかし今考えているのは、プログラムとしての世界です。
実際には、レジに対して入力し、金額や商品情報を取得するのですから、設計フェーズでは商品情報カタログの持ち主はレジであるとしたほうが便利そうです。
正しいかどうかという判別はできず、便利かどうか、というほうが重要だというのは分析フェーズでも述べてきたとおりです。
設計クラス図
設計クラス図と概念図の違いはなんでしょうか。
設計クラス図は、JavaやC++, そして最近ではC#, VB.NETなどのオブジェクト指向プログラミング言語のクラスおよびインターフェースの仕様を説明します。
概念図が、現実世界のモデル化を目的にしていたのに対して、設計クラス図は、あくまでもソフトウェアシステムのプログラム、プログラム群、クラス群を表します。
さて書き方は、というと…。
なんだ簡単じゃないですか。
#もちろんこれは、システムがわだけのクラスを洗い出す。
#慣れてくると、パッと見て閃きます。で、大体合ってます。
#閃かない場合は、じっと見つめます。じわじわと浮かんできます。だいたい正解です。
#それでもだめなら、図をいじりまわしてみましょう(笑)
#このとき、どちらがどちらを持っているのか判るように矢印を追加する
ええ簡単ですとも。簡単簡単(自己暗示をかけてます)。
というわけでえいやっと。
まあこんなところでしょうか。
ここまで来るとコーディングが見えてきたりします。
勢いのままコーディングになだれ込んでみる
コーディングすると特定の言語の話になってしまいますが、えーと、まあ、デタラメ言語でコーディングしてみましょうか。
設計クラス図を作ったときに、協調図を参照してますよね。
コーディングするときも協調図も見ます。
ほんとは永続性とかいろいろこれから考えなきゃいけないこともあるんですが、とりあえず、とりあえず…。
class レジ {
売上取引 torihiki;
商品情報カタログ katarogu;
enterItem(商品コード,数量) {
if (FIRST) {
torihiki = new 売上取引();
}
商品情報 shouin = katarogu.searchItem(商品コード);
torihiki.createItem(shouin, 数量);
}
endSale() {
torihiki.completeSale();
合計金額 = torihiki.computeTotal();
}
enterPayment(金額) {
torihiki.enterPayment(金額);
}
}
class 商品情報カタログ {
商品情報[] shouhin;
searchItemInfo(商品コード) {
for each (商品情報 s in shouhin) {
if (s.商品コード = 商品コード) {
return s;
}
}
}
}
class 商品情報{
String 商品名;
Currency 価格;
String 商品コード;
}
class 売上取引 {
入力商品[] nyuryoku;
現金支払 shiharai;
Date 日付;
Time 時間;
Boolean SaleCompleted;
completeSale() {
SaleCompleted = true;
}
createItem(商品情報, 数量) {
入力商品 nyuitem = new 入力商品(商品情報, 数量);
nyuryoku.add(nyuitem);
}
computeTotal() {
Currency Total = 0;
for each (入力商品 nyu in nyuryoku) {
Total = Total + nyu.comtputeSubtotal();
}
return Total;
}
enterPayment(金額) {
shiharai.金額 = 金額;
}
}
class 入力商品 {
商品情報 shouhin;
Integer 数量;
computeSubtotal() {
return shouhin.価格 * 数量;
}
}
…すんません、本当に適当なんですが…まあその、そういうところにはちょっと目をつぶっていただいて。
こうして、分析からコーディングまで、一貫して進められるのがオブジェクト指向のメリットでもあります。
分析、設計からコーディングの間が小さくて、つまり、プログラマがモノにしやすい設計手法でもあります。
クラス図を見ると、「レジ」と「売上取引」から「商品情報」に点線が引いてありますよね。
これは、「属性として持ってるわけじゃないけど使う」って意味です。
コーディングを見てください。いや協調図でもいいですけれど。
「レジ」は属性としては商品情報をもっていません。
「レジ」は「商品情報カタログ」の「searchItemInfo」を使って「商品情報」を取得しています。属性として持っているわけじゃありません。
また、「売上取引」も「レジ」から「商品情報」を受け渡されるだけで、やはり属性として持っているわけじゃないですよね。
こういうとき、点線を使って表現します。
そう、ようやく、入り口です。
拡張性は?再利用性は?生産性は?
まだ、この段階では考えられていません。
それは、オブジェクト指向入門3…いやもうこの辺まで来ると入門じゃなくなります。
「実戦に役立つオブジェクト指向」として次コンテンツにしましょうか。
「ちゃんといろいろ考えたモデリングをしよう」ということで。
Copyright (c) 2000 - 2002 Takao Tamura