ステートマシン図
|
UMLステートマシン図を使って、状態に基づく振舞いのモデルを作成する方法、さらに状態遷移表を使った効率的な、仕様の抜け漏れ確認の方法をチュートリアル形式でご紹介
|
はじめに
ソフトウェア開発は、次の3つの側面からシステムを捉えて設計を進めます。
- 機 能:どのようなサービスや処理を提供するか
- 構 造:どのような要素をどのように関係づけて構成するか
- 振舞い:どのような処理を実行するのか
本チュートリアルでは、このうち「振舞い」の設計に使うステートマシン図の描き方と、図を描いた後に、状態遷移表を使って仕様漏れなどを確認する方法を、CDプレーヤー操作システムを題材に用いて紹介します。
対象システム
このチュートリアルで扱う題材として、CDプレーヤーの操作パネルを考えましょう。
CDプレーヤーは電源が入っていて、CDは挿入されているものとし、曲を聴くときに使用する範囲の操作を取り扱うことにします。
CDプレーヤーは電源が入っていて、CDは挿入されているものとし、曲を聴くときに使用する範囲の操作を取り扱うことにします。
「CDプレーヤー操作パネル」の概観
題材のCDプレーヤーの操作パネルは次の図のようなものです。操作パネルには、再生ボタン、停止ボタン、LCDがついています。LCDには、現在実行中の操作、CD上の曲の通番、再生のときの経過時間を表示できます。
CDプレーヤーの操作手順 (1)
題材のCDプレーヤーの操作手順を次に示します。
- CDプレーヤーは、動作開始当初はCDを再生せずに停止している
- CDプレーヤーが停止しているときは、現在再生対象の曲の情報(通番と再生に要する時間)と停止状態を表示する
- CDプレーヤーが停止しているときに、利用者が再生ボタンを押すと、CDプレーヤーは現在再生対象の曲を再生し、その曲の再生時情報(通番と再生中の経過時間)と再生状態を表示する
- CDプレーヤーが曲を再生しているときに、利用者が停止ボタンを押すと、CDプレーヤーは音楽の再生をやめて停止する
- 手順2から繰り返す
ステートマシン図の作成
それでは、早速ステートマシン図を作成していきましょう。ステートマシン図をご存知ない方は、ページ末尾にある「振舞いの設計とステートマシン図について」に、図要素の種類や意味など基本的な情報を書きましたので、はじめにお読みください。
ステートマシン図をご存知の方は、このまま読み進めてください。
ステートマシン図をご存知の方は、このまま読み進めてください。
できごとを「待つ」場面を探す
状態に基づく振舞いのモデルを作成するときは、まず状態を探します。
分かりやすい方法は、システムが自身では進めることができないイベント(外部からの働きかけや時間の経過など)を待っている場面を探す方法です。ここで重要なのは、処理アクションや状態などの名前は、まだ考えないということです。処理が進められない場面と、そのとき起きてほしいイベントを先に探します。
まず、前述の操作手順の中から、処理が進まずに止まっている場面を探します。例えば、次の記述に着目します。
分かりやすい方法は、システムが自身では進めることができないイベント(外部からの働きかけや時間の経過など)を待っている場面を探す方法です。ここで重要なのは、処理アクションや状態などの名前は、まだ考えないということです。処理が進められない場面と、そのとき起きてほしいイベントを先に探します。
まず、前述の操作手順の中から、処理が進まずに止まっている場面を探します。例えば、次の記述に着目します。
- …利用者が再生ボタンを押すと、CDプレーヤーは現在再生対象の曲を再生し…
この場面では、CDプレーヤーは、利用者が再生ボタンを押すのを待っていて、この操作をしてもらわないと再生処理へ進むことができません。このような場面が状態の候補です。この状態をステートマシン図で表してみましょう。
最初に、状態を2つ配置し、その間に状態遷移を引きます。
最初に、状態を2つ配置し、その間に状態遷移を引きます。
「再生ボタンが押されたら…」が待ってるイベントが起きたということでしょう。
そこで、いま描いた状態遷移の名前を「再生ボタンが押された」に書き換えます。
そこで、いま描いた状態遷移の名前を「再生ボタンが押された」に書き換えます。
これで「状態0」が、イベント「再生ボタンが押された」を待っている状態になり、「状態1」がこのイベントが起きたときに遷移する次の状態になりました。
次に続く記述を調べましょう。
- …利用者が停止ボタンを押すと、…再生をやめて停止する
- 手順2から繰り返す
ここから「状態1」が「停止ボタンが押される」のを待っている状態であることがわかります。その後、手順2から繰り返すので、手順2の記述に戻るということです。
- CDプレーヤーが停止しているときは、現在再生対象の曲の情報…と停止状態を表示する
このことは、次に遷移する状態は「停止ボタンが押された」ときに「現在再生対象の曲の情報」や「停止状態を表示する」状態であることを意味します。しかも、手順2に戻ってこの状態になるということは、この状態は、先ほど「再生ボタンが押される」のを待っていた状態でもあるということです。以上のことから「状態0」が次の遷移先ということがわかります。
「状態1」から「状態0」への状態遷移を追加して、トリガーをイベント「停止ボタンが押された」に編集しましょう。
また、
- CDプレーヤーは、動作開始当初はCDを再生せずに停止している
という記述を考えると、状態0が、このステートマシン図の最初の状態ということがわかりますので、それを示すために開始疑似状態を付与します。
これで、与えられた記述から状態遷移を洗い出し、ステートマシン図に反映できました。
できごとが起きた時の処理を追加する
次に、それぞれの記述の処理を実施する部分に着目します。例えば、
- 利用者が再生ボタンを押すと、CDプレーヤーは現在再生対象の曲を再生し…
からは、「CDプレーヤーは現在再生対象の曲を再生し、その曲の再生時情報(通番と再生中の経過時間)を表示する」という処理は、「再生ボタンが押された」あとに実行するので「状態1」の処理になります。
今回のCDプレーヤーは、操作パネルとは別に用意されたコアコンポーネントの内部に、曲の再生処理、再生時情報の表示処理、再生状態の表示処理が用意してあります。コアコンポーネントの提供する「再生する」手続きを呼び出すと、これらの内部処理を使用して再生処理全体を実行できると想定してください。
操作パネルは、アクション「再生する」を用意し、これがコアコンポーネントの「再生する」手続きを呼び出すものとします(操作パネルは再生の手続きをコアコンポーネントに頼んでやってもらう)。
この処理は、イベントが起きたときに遷移してきた時点だけではなく、次の状態へ移るまで継続します。このように同じ状態にある間継続する処理の場合、状態の中の「doアクティビティ」に書いておきます。
この処理は、イベントが起きたときに遷移してきた時点だけではなく、次の状態へ移るまで継続します。このように同じ状態にある間継続する処理の場合、状態の中の「doアクティビティ」に書いておきます。
これで、何かを待っている「状態0」から「状態1」でアクションを実行するところまでを、ステートマシン図に描けました。
次に、「状態1」から「状態0」に戻ってきたときは、下の処理を実行することになります。
- 利用者が停止ボタンを押すと、…再生をやめて停止する
- CDプレーヤーが停止しているときは、現在再生対象の曲の情報…と停止状態を表示する
この部分の処理には、曲の停止、現在再生対象の曲の情報、停止状態を表示する処理を組合せて実行するコアコンポーネントの手続き「停止する」を呼び出すアクション「停止する」を用意して、これを割り当てることにします。この処理は、イベントが起きたときに一度実行すればよいので、状態の中の「entryアクション」に書いておきます。
これで、ステートマシン図にイベントと状態遷移と状態ごとのアクションを描くことができました。
イベントとアクションに基づいて状態名をつける
イベントとアクションが定まったので、これらに基いて各状態に名前をつけることができるようになりました。ひとつ目の方法は、待っているイベントのうち、一番期待しているイベントを基に「〜待ち」とする方法です。もう一つの方法は、その状態がイベントを待つ間に処理しているアクションを基に「〜中」としする方法です。それぞれの状態ごとに、いずれかの方法からふさわしいと思う方を選びます。
この命名方法を使うと「状態0」は「再生待ち」または「停止中」となり、「状態1」は「停止待ち」または「再生中」となります。今回はどんな操作を待っているかではなく、機器としての動作状況を表すほうが好ましいと考えて「停止中」「再生中」とします。
段階的に振舞いを追加する
これまでの操作パネルには再生ボタンと停止ボタンしかありませんでしたが、再生ボタンには一時停止の役割があったとしましょう。
CDプレーヤーの操作手順(2)
追加した機能に合わせて、手順4を次のように変更します。
- CDプレーヤーが曲を再生しているときに、利用者が停止ボタンを押すと、CDプレーヤーは音楽の再生をやめ、再生ヘッドは停止位置に移動する(再び再生するときは、再生中の曲の冒頭から再生する)
- CDプレーヤーが曲を再生しているときに、利用者が再生ボタンを押すと、CDプレーヤーは音楽の再生を一時停止し、再生ヘッドは現在位置にとどまる(再び再生するときは、現在の再生位置から再生する)
- CDプレーヤーが一時停止しているときに、利用者が再生ボタンを押すと、CDプレーヤーは、一時停止した時点の再生位置から再生を再開する
一時停止の処理には、曲の停止、現在再生対象の曲の再生時情報と一時停止状態を表示する処理を組合せたコアコンポーネントの「一時停止する」手続きを呼び出すアクション「一時停止する」を用意して、これを割り当てることにします。再び再生ボタンが押されて状態「再生中」に戻ったときは、曲の冒頭からではなく、現在の再生位置から再生することになります。
一方で、アクション「再生する」は、再生時に曲の冒頭から再生する処理に変わります。これは状態「停止中」のアクション「停止する」の処理で現在の再生位置を変更すれば実現できそうです。アクションの処理内容の変更があっても、状態遷移が影響を受けないことに注意しましょう。
続いて、こんどは、操作パネルに早送りボタンと早戻しボタンが、追加されたとしましょう。
この機能に合わせて、手順4に次の処理を追加します。
- CDプレーヤーが曲を再生しているときに、利用者が早送りボタンを押し続けると、CDプレーヤーは音楽の再生速度を早め、利用者が同ボタンを押すのをやめると、通常の再生に復帰する
- CDプレーヤーが曲を再生しているときに、利用者が早戻しボタンを押し続けると、CDプレーヤーは音楽を逆再生(再生速度は早送りと同等)し、利用者が同ボタンを押すのをやめると、通常の再生に復帰する
状態「再生中」は、これまでのイベントに加えて、早送りボタンと早戻しボタンが押されるのを待つ状態になりました。
また、利用者が、早送り/早戻しボタンを押している間、CDプレーヤーは利用者がこれらのボタンを押すのをやめる(離す)のを待っていることが分かります。ここで、ボタンを離すまでの間に実行する処理は、これまでに描いた状態には含まれていません。
つまり、新しい状態を作成することになります。
また、利用者が、早送り/早戻しボタンを押している間、CDプレーヤーは利用者がこれらのボタンを押すのをやめる(離す)のを待っていることが分かります。ここで、ボタンを離すまでの間に実行する処理は、これまでに描いた状態には含まれていません。
つまり、新しい状態を作成することになります。
そこで、新しい状態とイベントを追加しましょう。追加するイベントは「早送りボタンが押された」と「早戻しボタンが押された」とします。
追加した状態は、それぞれの「ボタンが離される」のを待っている状態でした。利用者がボタンを離すと、再び状態「再生中」へ遷移して通常の再生に戻るのですから、状態「再生中」への状態遷移とイベントを追加しましょう。追加するイベントは「早送りボタンが離された」と「早戻しボタンが離された」とします。
イベントと状態を追加できたので、アクションを追加します。現在再生中の曲の早送り再生と、再生時情報と早送り再生状態表示をする処理を組合せて実行するアクション「早送り」と、同様のアクション「早戻し」を用意します。ボタンが押されてから離されるまでの間処理が続くので、それぞれの状態のdoアクティビティに追加しましょう。
最後に状態名を追加します。命名方法に従えば「早送りボタン離し待ち」か「早送り中」になります。ここではボタンの操作を待っていることよりも、その間早送りしていることのほうがより伝えたい処理と考えて「早送り中」を採用しましょう。また、同様にして「早戻し中」とします。
段階的に状態を考え、ステートマシン図に表すことで、操作パネルの操作に関する振舞いのモデルが徐々にできあがってきたのがわかったと思います。
今回は、段階的に仕様が与えられていましたが、同じ方法は、予め与えられている仕様が大きく複雑な場合にも応用できます。仕様が大きく複雑な場合は、最初に基本的な操作や重要な機能に対してステートマシン図を作成し、基本的な処理ができるモデルになっているかを確認します。その後で、残りの仕様について段階的にイベント、状態、アクションを追加していくのです。
この手順を使うと、システムにとって重要な機能や処理への関心を維持し、他の処理との調整が取りやすくなります。
この手順を使うと、システムにとって重要な機能や処理への関心を維持し、他の処理との調整が取りやすくなります。
状態遷移表を用いて状態遷移を確認する
ここまでは、振舞いのモデルの作成にステートマシン図を使ってきました。状態の遷移は、ステートマシン図以外にも「状態遷移表」を使って表すこともできます。ステートマシン図と状態遷移表は、共に同様の情報を提供する記法ですが次のような違いがあります。
- ステートマシン図:どの状態間にどんなイベントで状態遷移があるか把握しやすい
- 状態遷移表:全ての状態とイベントの組合せが網羅でき、期待していないイベントの対処も確認できる
これらの特徴を踏まえると、実際に振舞いのモデルを作るときは、開発を進めている状況が変化するのに応じて、ステートマシン図と状態遷移表を交互に使うのがよいと思います。市販のUML用のツールの多くは、ステートマシン図と状態遷移表を双方向に往来できるように作ってあり、交互に使うことは容易になってきています。
「前状態―後状態」の状態遷移表を使って状態遷移を確認する
さて、前節で作成したステートマシン図を「前状態―後状態」の状態遷移表で表してみます。
この表は、縦軸に前状態、横軸に後状態、表中のセルには前状態から後状態へ遷移するときにトリガーになるイベントが書いてあります。空欄のセルは、そのセルの縦軸の状態から横軸の状態への状態遷移は想定していないことを示しています。
この表は、縦軸に前状態、横軸に後状態、表中のセルには前状態から後状態へ遷移するときにトリガーになるイベントが書いてあります。空欄のセルは、そのセルの縦軸の状態から横軸の状態への状態遷移は想定していないことを示しています。
たとえば、縦軸が状態「再生中」で、横軸が状態「停止中」のセルを調べてみます。
イベント「停止ボタンが押された」が割り当てられていて、状態遷移が起きることがわかります。
イベント「停止ボタンが押された」が割り当てられていて、状態遷移が起きることがわかります。
また、この表のセルをひとつずつ調べると、状態遷移の要不要を確認できます。
たとえば、前状態が「一時停止中」で、後状態が「停止中」の場合に対応するセルは空欄なので、「一時停止中」から「停止中」へ遷移するような処理がないということですが、これが意図通りか設計の漏れなのか確認できます。
それぞれのセルについて同様に確認することで、想定しているが設計から漏れている処理が発見できるでしょう。
それぞれのセルについて同様に確認することで、想定しているが設計から漏れている処理が発見できるでしょう。
「状態―イベント」の状態遷移表を使って状態遷移を確認する
次は、前節で作成したステートマシン図を「状態―イベント」の状態遷移表で表してみます。
この表は、縦軸に状態、横軸にイベント、表中のセルにはイベントが発生した場合に遷移する先の状態が書いてあります。
空欄のセルは、縦軸の状態において、横軸のイベントの発生を想定していないことを示しています。
空欄のセルは、縦軸の状態において、横軸のイベントの発生を想定していないことを示しています。
たとえば、状態「再生中」にイベント「再生ボタンが押された」が発生すると、状態「一時停止中」へ遷移することがわかります。
また、この表のそれぞれのセルを調べると、ステートマシン図では書き表せていないイベントが発生した場合の対応を決めることができます。
たとえば、停止中に早送りボタンが押されたときは、再生位置を次の曲の冒頭に変更し、引き続き停止させておきたかったとします。この表の縦軸の状態「停止中」と横軸のイベント「早送りボタンが押された(forward_button_pushed)」の交差するセルを確認すると、このセルが空欄で、状態遷移が抜けていることがわかります。そこで、不足していた状態遷移を追加して、再びこのセルに状態が割り当てられたことを確認します。
たとえば、停止中に早送りボタンが押されたときは、再生位置を次の曲の冒頭に変更し、引き続き停止させておきたかったとします。この表の縦軸の状態「停止中」と横軸のイベント「早送りボタンが押された(forward_button_pushed)」の交差するセルを確認すると、このセルが空欄で、状態遷移が抜けていることがわかります。そこで、不足していた状態遷移を追加して、再びこのセルに状態が割り当てられたことを確認します。
また、ある状態において想定しないイベントが起きたときの対応を決めることもできます。
たとえば、停止中に停止ボタンを押しても無視したいとします。このときは、状態「停止中」とイベント「停止ボタンが押された」が交差するセルに「無視する(ignore)」と書いておきます。セルに「無視する」が割り当てられている場合、イベントが発生しても発生しなかったように扱う(このイベントは消費してしまう)ことが多いです。
もうひとつの例として、再生中には、早送りボタンが押されることなしにボタンが離されることはない(そのようなことが起きるのはおかしい)としましょう。このときは「再生中」とイベント「早送りボタンが離された」が交差するセルに「起き得ない(cannot happen、can't happen、CHなど)」と書いておきます。セルに「起き得ない」が割り当てられている場合、イベントが発生したら問題が起きていることが判るよう、エラーを発生させたり、処理を中断することが多いです。
まとめ
このチュートリルでは、ステートマシン図を使って、状態に基づく振舞いのモデルを作成する方法を紹介しました。
また、ステートマシン図から状態遷移表を作成して活用する方法についても触れました。
また、ステートマシン図から状態遷移表を作成して活用する方法についても触れました。
ステートマシン図を作成する手順
状態を識別するには、イベントの発生を待つ場所探すとよいことを紹介しました。以下に、この考え方に沿ったステートマシン図の作成手順を整理しておきます。
- 「~が起きたら~をする」を洗い出す
- 「~が起きたら」を「~が起きた」というイベントにし、遷移の線を引いてイベントを記入する
- 「~をする」をアクションにし、遷移先の状態に書く
- ある状態からの状態遷移が洗い出せたところで、状態名を考える
- 状態名は待っているイベントから「~が起きるの待ち」とするか、処理するアクションから「~の処理中」とする
段階的に設計を進める
全ての状態やイベントを一度に探すのではなく、状態をみつけては状態遷移とイベントを書いていくという段階的な進め方を紹介しました。これは、仕様を徐々に追加していく場合にも、大きく複雑な仕様を段階的にモデル化する場合にも役に立ちます。
試してみる
このチュートリアルでは、UMLモデリングツールastah*を使ってステートマシン図を作成しました。
状態遷移表は「ソフトウェア品質向上支援プラグイン」として提供している「状態遷移表プラグイン(無償)」で自動生成したものです。作成した状態遷移表はEXCELのワークシート形式でエクスポートすることもでいます。なお、このチュートリアルでは紹介していませんが、状態遷移図上の状態遷移の経路(パス)を追跡することで、振舞いのテストに関するテストケースを洗い出せます。
astah*は、50日間ご利用いただけますので、ぜひお試しください。また、既にastah*をお使いの方も、状態遷移表/パスプラグインをインストールするだけで、状態遷移表/パスを自動生成できます。ぜひご活用ください。
状態遷移表は「ソフトウェア品質向上支援プラグイン」として提供している「状態遷移表プラグイン(無償)」で自動生成したものです。作成した状態遷移表はEXCELのワークシート形式でエクスポートすることもでいます。なお、このチュートリアルでは紹介していませんが、状態遷移図上の状態遷移の経路(パス)を追跡することで、振舞いのテストに関するテストケースを洗い出せます。
astah*は、50日間ご利用いただけますので、ぜひお試しください。また、既にastah*をお使いの方も、状態遷移表/パスプラグインをインストールするだけで、状態遷移表/パスを自動生成できます。ぜひご活用ください。
振舞いの設計とステートマシン図について
状態遷移図やステートマシン図をご存知ない方のために、簡単に説明しておきましょう。
さらに詳しい記法については、専門の解説や書籍を参考にするとよいでしょう。
さらに詳しい記法については、専門の解説や書籍を参考にするとよいでしょう。
イベント
システムやその構成要素は、処理の途中で外部からの働きかけや時間の経過などを待つことがあります。このとき待っているできごとを「イベント」と呼びます。イベントには、外部からの入力、一定時間の経過などがあります。
状態
システムがイベントの発生(発火ともいいます)を待っている場面を「状態」と呼びます。状態には、状態名とその状態で実行するアクションを書きます。
アクション
イベントが起きたとき、状態遷移システムが実行する処理を「アクション」と呼びます。アクションと似た処理に「アクティビティ」があります。実行中にイベントの発生などで処理が中断することを想定しています。状態に配置するアクションは3種類あります。
- 入場動作(entryアクション):ある状態に遷移したときに実行する
- 実行活動(doアクティビティ):イベントが発生するまでの間継続して実行する
- 退場動作(exitアクション):イベントが発生したとき、状態が遷移する前に実行する
状態遷移
状態に基づく振舞いのモデルは次のような動作の繰り返しによって処理が進みます。このようにイベントが発生して状態から次の状態へ移動する動作を「状態遷移」と呼びます。
- ある状態において、実行活動を実行しながらイベントの発生を待つ
- 待っていたイベントが発生し、ガード条件が真(またはない)だと、状態が遷移する
(イベントが発生しても、ガード条件が偽なら状態は遷移しない) - 状態遷移に配置されているアクションを実行する
- 次のイベントを待つために次の状態へ移る
- 遷移した先の状態に配置されているアクションを実行する
状態遷移図とステートマシン図
状態遷移図は、発生するイベント群による状態の遷移と、状態や状態遷移に付随するアクションによって動作を表すタイプの振舞いのモデルといえます。UMLのステートマシン図は、状態遷移図に対して状態の階層化やいくつかの疑似状態を追加して拡張したものです。