08

04

状態マシン図 演習 解答例2

2010.08.04(23:30)

より現実的に、複数の状態マシンが組み合わさってできているシステムを考えてみましょう。
組込みシステムでは別々のタスクによって別々の入出力を監視し、それぞれがイベントをやりとりしながら、全体としての責務を達成するようなシステムが一般的です。

【出題】
(4-1) 有料駐車場をモデリングしなさい。
・有料駐車場には
・入り口に満車表示がある。満車になると点灯し、そうでなければ消えている。
・入り口に発券機があり、満車でなければ、車はそこで駐車券を受け取って入る。
・出口には精算機があり、車はそこで駐車券をいれて精算したあと駐車場を出る。
・発券機や精算機は複数ある。

よくある有料駐車場です。問題文に書いていないものは、適当に補って考えます。
駐車場利用者は、駐車場へ車を止めることができます。
満車だったら待ちますが、空きがある状態になったなら、速やかに駐車場にいれたいでしょう。
利用者が混乱したり、迷惑を受けたりしてはいけません。そこらへんは常識で補います。

状態マシンを持つモデルの場合は、クラス図ではなく、状態マシン図を描くことから分析をはじめましょう。そのとき、状態マシン図という名前がよくないのですが、システムが持つ状態に着目するのではなく、状態を変化させるイベントに着目します。状態マシン図とはどのようなイベントで状態が変わるのかをあらわした図です。


システムの中でどのようなイベントがあるでしょうか。駐車場なので、車が入る、車が出る、というイベントに気がつきます。

イベントの中には、自分が見つけるものと、他人が見つけて自分に知らされるもの、自分が見つけて他人に知らせるものがあります。そのイベントの発信元、送信先はどこでしょうか。

車が出るほうから見てゆきましょう。車が出て行くのを見つけるのは、精算機ですね。

精算機は、料金精算口に駐車券がいれられるのを待っていて、やがて駐車券と料金がいれられて精算終了し、ゲートをあけます。さまざまなことをやっていて、この文章から名詞に下線をひいて、クラスを見つけたり、、、はしません。そうではなくて、精算機が扱うイベントの中で自分で見つけるものと、自分に伝えられるもの、自分から他人に伝えるものがあるか考えます。

精算機は、車が精算したことを見つける。(自分が見つける)
それと同時に、車が出て行ったことを知らせる。(他人に知らせる)


こんな感じです。

精算機の責務は、車が出て行ったことを伝える。の1つといえます。より細かくは精算機そのものやゲートの制御やいろいろあるのですが、他人に知らせるイベントに着目して、責務は1つになります。

どうして車が出て行ったことを知らせるのかというと、誰かが満車表示しなければならないからです。満車表示ができるようにするには、車がいっぱいになったかどうかを判定する必要があります。

駐車場には、車が出たり入ったりします。入った数から出た数を引けば、いま駐車場に何台の車がはいっているかわかりそうです。

駐車場には、発券機と精算機が複数あって、車が出たり入ったりすることは、そこから知らせてもらえそうです。

なので、駐車場は、いま何台入っているのか数える、という責務1つに集中することにします。

車が入ったことと、車が出たことは、ポーリングせずに、それぞれ、
発券機と精算機から通知してもらいます。イベントは、
・車が入ったよ (発券機から駐車場へ)
・車が出たよ  (精算機から駐車場へ)

また、駐車場は、満車になったかどうか判定するので、イベントとして
・入場許可中だよ (駐車場から発券機へ)
・入場制限中だよ (駐車場から発券機へ)
の2つを考えておきます。

複数の発券機から同時に車がはいってくることもあるでしょう。中には2台の駐車場スペースを1台で占めちゃうマナーの悪い客がいるかもしれませんね。しかしそこらへんは、設計段階で詳細に検討します。まずは、自分が見つけるイベントと、他人が見つけるイベントに着目します。


駐車場にとって、
・車が入ったよ
・車が出たよ
というのは時々刻々いつなんどき入ってくるかわからないイベントです。入場許可中も入場制限中もいつでも入ってくるかわかりません。なので、台数のカウントと、入場許可中~入場制限中の状態は直交状態として管理します。そうすることにより、いつでも台数カウントが確実にできます。台数カウントしながら、多少余裕を持たせて、入場許可と入場制限中を遷移させます。

入場許可と入場制限中を遷移するときに、発券機に対して、空きあり表示~満車表示の切り替えを指示します。DoRed()とDoGreen()の実装はのちほど確認します。

このように、誰かが何かをポーリングするのではなく、イベントを発見した人から、そのイベントを利用する人に向かってイベントを直接送信し、そのイベントを利用する人がラッチして覚えておくようにします。(Latch Stateパターン)

次は発券機です。

発券機にも、精算機同様、ゲートがついていたり、発券状態など細かい状態が考えられるのですが、さきほどと同様に、外部から送られてくるイベント、外部に伝えるイベントに着目します。


発券機には、空きあり表示~満車表示の切り替えがあります。これは、駐車場が知らせてくれるものをそのまま記憶しておくLatch Stateパターンです。

また、発券機は、車がやってきたことを検出し、空きあり表示のときは、ただちに発券し、満車表示のときは、待たせておいて、次回空きあり表示に変わったときに、すみやかに発券させます。

なので、発券機も2つの直交状態を持ちます。空きあり表示~満車表示を覚えておき、それを利用して、車が来たときに、すぐ発券するか、入場待ちにするか、分岐させます。

発券が終わると、駐車場に対して、車が増えたよ、を伝えます。

以上組み合わせて、以下のようなクラス図ができあがります。


上半分が分析クラス図、全体をあわせて設計クラス図です。今回の駐車場システムでは、精算機2つ、駐車場1つ、発券機2つで構成されています。

ツールの都合上、関連名が入っていませんが、モデルを読んで確認してみます。
・精算機は、駐車場に対し、車が出て行ったことを伝える。
・発券機は、駐車場に対し、車が入ってきたことを伝える。
・駐車場は、発券機に対して、空きあり表示~満車表示の切り替えを伝える
クラス間のイベントのやりとりはこれだけです。

実装をここで確認してみましょう。

精算機から駐車場は多対1の片方向関連です。ポインタで駐車場をさせばいいので、駐車場に車が出たよイベントを投入するのは、以下のような実装です。

  itsParking->GEN(evCarDec);

発券機から駐車場は多対1の双方向関連です。発券機側からは、ポインタで駐車場をさせばいいので、駐車場に車が入ったよイベントを投入するのは、

  itsParking->GEN(evCarInc);

駐車場から発券機へは、複数の発券機なので、コレクションクラスを使います。空きあり状態を伝えるDoGreen()と、満車状態を伝えるDoRed()は以下のような実装です。

void Parking::DoGreen() {
//#[ operation DoGreen()
OMIterator iter(itsTicketingDevice);
while(*iter) {
TicketingDevice* ticketingDevice = (*iter);
ticketingDevice->GEN(evGreen);
iter++;
}
//#]
}

void Parking::DoRed() {
//#[ operation DoRed()
OMIterator iter(itsTicketingDevice);
while(*iter) {
TicketingDevice* ticketingDevice = (*iter);
ticketingDevice->GEN(evRed);
iter++;
}
//#]
}

このような実装であれば、精算機、発券機が1台から複数台まで何台あっても同じメカニズムで処理することができます。駐車場から発券機へのイベント通知の形はクラス構造的には、Observerパターンといえます。今回の場合は、発券機の数が動的に変化したりしませんが、少なくとも駐車場は、発券機の台数が変化しても、実装を修正する必要がありません。



コメント

No title

駐車中の車の台数に対する排他制御が必要ではないですか?

コメント

Re: No title

> 駐車中の車の台数に対する排他制御が必要ではないですか?
別コンテキストから呼ばれるので排他制御が必要ですね。

コメントの投稿

非公開コメント

プロフィール

島敏博

Shima Toshihiro 島敏博
信州アルプスハイランド在住。HaskellとElixirが好き。組み込みソフトウェアアーキテクト、C++プログラマ、山歩き、美術館巡り、和食食べ歩き、日本赤十字社救急法指導員、インデックス投資、クラシック音楽、SESSAME会員、状態マシン設計、モデル駆動開発、ソフトウェアプロダクトライン、Rubyist、実践ビジネス英語

■ ツイッター
http://twitter.com/saltheads
■ Facebook
http://www.facebook.com/saltheads
■ Qiita
http://qiita.com/saltheads

印刷する場合は、ブラウザの印刷メニューではなく、このページの上から3cmくらいの青いところにある、「印刷」を押してみてください。少しうまく印刷できます。まだ完全ではないのですが、これで勘弁してください。


カテゴリ
最新記事
月別アーカイブ
最新コメント
検索フォーム
リンク
sessame
RSSリンクの表示