このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

ユースケース図もどき、とか

Catyでユースケース図もどきの絵が描けるようになったことを紹介。ついでに、昨日「悟りやヒラメキがほんとに大キライだ」とか騒いでいた前後の経緯というか、文脈をちょっと説明しておきます。


いくつかの理由があって、Catyにユーザーロール(user role)という概念を導入しました。これは、ユースケース図のアクターに相当するもので、設計上の概念です。とはいえ、純粋に設計上の概念ならWebフレームワークに入れてもしょうがないもので、テストや権限管理を自動化する下心があるので導入したのです。

ユーザーロールはアクターと同様な概念ですから、「ユースケース図を自動描画できるといいな」と思ったわけです。誤解がないように言っておくと、図を描くのが主たる目的ではありません。図はあくまでオマケです(けっこううれしいオマケですが)。

Catyの管理下には、ユースケースに対応するメタオブジェクトが存在しないので、ほんとのユースケース図は描けません。ユースケース図を実装寄りに詳細化したロバストネス図なら描けます。ロバストネス図から、アクターとバウンダリオブジェクトだけ抜き出して描いたものが、Catyのユースケースもどきです。メモ編に図の例を載せておきました。

もうひとつ別な例をここに挙げておきましょう。レストランのランチを予約する(つもりのナンチャッテ)システムの例です。

このシステムのユーザーロール(アクター)と画面(バウンダリオブジェクト)を抜き出すと次のようになります。

アイコンの形とかは全然違うのですが、描かれている内容はユースケース図に近いと思います。この図のもとになったモジュールのソースコードは、このエントリーの最後に貼り付けておきます。


さて、ユースケース図とか持ちだしたので、Catyが保持しているメタオブジェクトをUML図に対応付け、Catyが想定している設計作業をUMLベースの設計プロセス内に位置付けると説明に好都合だろう、と思ったのです。それで、ニワカ勉強を開始したら、曖昧な記述に接してムカムカしてしまったわけです。思い起こせば、似たようなムカムカは以前も経験したのでした。

そのことを https://bitbucket.org/project_caty/dev/issue/184 に愚痴として書いて、さらに「悟りやヒラメキがほんとに大キライだ」にも書いてしまった、という次第。

もう少し具体的なことを書くと: ユースケース図と一緒に出てくるイベントフローという概念、それとシナリオ(と呼ばれているナニカ)、あとシーケンス図で表現する時系列、これらは同義語ですか? 違うとしたらどこがどう違うのでしょう? また、その違いはどんな場合に意味を持つのでしょうか? アクターやユースケースのあいだの関連である <<extends>>、<<includes>>、<<uses>> って、アレ何ですか? なんか意味論がありますか? 関連を満たしていることを判定するアルゴリズムはありますか? -- ハッキリとした説明が見つかりませんでした*1

イベントフローには(シナリオにも)メインのイベントフローと代替イベントフローという区別があるそうです。僕にはその区別が出来そうにありません。それと、メインのイベントフローの定義(らしきもの)を文字通りに解釈すると、なんだか奇妙なことになります。状態遷移モデルで解釈すると、始状態から終状態に至るサイクルなしの経路(path, walk, trajectory)なんですが*2、そんなものがいつでも一意に取れるとは思えないし、取れても(僕の目的では)うれしくないのです*3。「そういう解釈をするのがマズイ」と言われればすぐに引き下がりますが、「じゃ、どういう解釈をするのか?」は知りたいところです。「悟れ」と言われたら怒ります。



// -*- coding: utf-8 -*-

/** ランチの予約&注文のシステム
*/
module lunch-reserve in cara;


/*
* == 型の定義 ==
*/

/**
* ランチの注文データ
* http://d.hatena.ne.jp/m-hiyama/20101213/1292201095
*/
type LunchOrder = {
/** 会員識別情報 */
@[label("ランチ会員情報")]
"ident" : {
@[label("お名前")]
"name" : string,
@[label("会員番号")]
"memberNum" : integer
},
/** 今日の注文 */
@[label("本日のご注文")]
"order" : {
@[label("メイン")]
// (注意) 現状、ユニオン型の選択肢にはアノテーションが付けられない
"mainDish" : (
// @[label("日替わり")]
"special"|
// @[label("魚")]
"fish"|
// @[label("肉")]
"meat"
),
@[label("お飲み物")]
"drink" : (
// @[label("紅茶 (ホット)")]
"teaHot"|
// @[label("紅茶 (アイス)")]
"teaIce"|
// @[label("コーヒー (ホット)")]
"coffeeHot"|
// @[label("コーヒー (アイス)")]
"coffeeIce"
),
@[label("デザート")]
"dessert" : (
// @[label("プリン")]
"pudding"|
// @[label("苺ショートケーキ")]
"strawberryShortcake"|
// @[label("ババロア")]
"bavarianCream"|
// @[label("ガトーショコラ")]
"gateauChocolat"
)
}
};


/*
* == ユーザーロール ==
*/

userrole 顧客;
userrole スタッフ;
userrole 店長; // 店長は特に出番なし


/*
* == 画面(クライアント状態) ==
*/

state 開始 :: void links {
+ 予約開始 --> 予約ページ.取得;
+ 登録開始 --> メニュー登録ページ.取得;
};

state 終了 :: void;

/* === 顧客用 === */

state 予約注文画面 for 顧客 :: defered links {
+ 予約 --> 予約処理.予約;
};

state 確認画面 for 顧客 :: LunchOrder links {
+ 確定 --> 予約処理.確定;
+ やり直し --> 予約ページ.取得;
};

/* === スタッフ用 === */

state メニュー登録画面 for スタッフ :: deferred links {
+ 登録 --> 予約処理.メニュー登録;
};


/*
* == リソースとアクション ==
*/

resource 予約ページ ("/reserve.html") {
action 取得("/GET")
:: @[in, out] #0 void -> void produces 予約注文画面 ;
};

resource メニュー登録ページ ("/register.html") {
action 取得("/GET")
:: @[in, out] #0 void -> void produces メニュー登録画面 ;
};

resource 予約処理 ("/process.cgi") {
action 予約("reserve/POST")
:: @[in] #in LunchOrder -> _ relays [ng, ok],
@[out] #ng _ -> void redirects 予約ページ.取得,
@[out] #ok _ -> LunchOrder produces 確認画面
;

action 確定 ("commit/POST")
:: @[in, out] #0 void -> void produces 終了 ;

action メニュー登録 ("register/POST")
:: @[in, out] #0 void -> deferred produces 終了 ;
};

// End of module

*1:[追記]「図の見た目が違う」とか「利用場面が違う」とかは分かる、というか当たり前ですが、それぞれの言葉や図が指し示すもの(denotation)が同じか違うか、違うなら関係がありやなしや、関連があるならどんな関係か、といったことが知りたいのです。[/追記]

*2:最短経路とは限りません。そもそも、最短性を定義するための距離概念を入れられる保証がありません。

*3:[追記]状態遷移モデルで考えるとして、メインのイベントフローの存在を要請しちゃうと、これが足枷になって、かなり不便なことになります。部分から全体を合成する方法にけっこうなダメージを与えます。[/追記]