#この記事の対象者
仕事上、UMLでシーケンス図を書いて、それを基にプログラミングしている人。
かつ、プログラミングする時に、シーケンス図から実装対象のインスタンスが読み取れない状況の人。
モデリングやUMLの学習者。
なお、結論だけ読みたい人は、最後の「まとめ」だけ読んでも構いません。
#ライフラインのヘッダとは
シーケンス図の点線の上にある矩形。これが、ライフラインのヘッダです。
#現場でヘッダによく書かれている内容
大体ですが、このどちらかです。
これじゃ、実装するときに、どのインスタンスを呼べばよいのか分からないですよね...。
仮に、クラス名Piyo でコードを考えてみましょう。
class Hoge {
Piyo m_piyo = new Piyo();
Piyo m_piyopiyo = new Piyo();
List<Piyo> m_piyos = new List<Piyo>();
public void Function()
{
???.Execute(); // シーケンス図からは、どのPiyoをコーディングすればいいのか分からない...。
}
}
class Piyo {
public void Execute(){ ... }
}
#UML表記法でのルール
UMLの表記法は、OMG(Object Management Group)のUML仕様書(参考文献を参照)に書かれています。
その中を読むと、ライフラインのヘッダの書きかたも定義されています。
ライフラインのヘッダの中身の書きかた
<lifelineident> ::= ([<connectable-element-name>[‘[’<selector>‘]’]] [:<class-name>][decomposition]) | ‘self
<selector> ::= <expression>
<decomposition> ::= ‘ref<interactionident>[‘strict’]
読み慣れないかも知れませんが、BNF記法で書かれています。
<lifelineident>
が、ライフラインヘッダに書くべき内容を示しています。
ざっくりと読みやすくすると、
ライフラインヘッダに書くべき内容 = コネクタブルエレメント名[セレクタ] : クラス名 ref 内部展開したシーケンス図名
となります。(※この和訳は、公式な訳ではありません)
ってか、読みやすくなってないですよね...(^^;;。
段階的に説明していきますので、辛抱して読んでください。
1.クラス名
2.コネクタブルエレメント名(のちに「関連端名」表現しなおす)
3.セレクタ
4.内部展開したシーケンス図名
で、話を進めます。
##クラス名
クラス名は、「:」を付けて「:クラス名」と書きます。
class Hoge {
Piyo m_piyo = new Piyo(); // Piyoは1つしか存在しないので、暗黙でこれだと分かる。
public void Function()
{
m_piyo.Execute();
}
}
class Piyo {
public void Execute(){ ... }
}
クラス名だけを示す書きかたは、暗にインスタンスを特定できる場合のみ許されます(が、分かりにくいですよね)。
##コネクタブルエレメント名
そもそも、コネクタブルエレメントとは何でしょうか。
まず、UML仕様書のLifeline(ライフライン)の項を読むと、
Lifelineには、「InternalStructures::ConnectableElement」がありました。
次に、UML仕様書で「ConnectableElement (from InternalStructures)」の項を見ると、
A ConnectableElement is an abstract metaclass representing a set of instances that play roles of a classifier.
ConnectableElementは、分類子の役割を演ずるインスタンスの集合を表す抽象的メタクラスである(OMG 西原 2006)
とあります。その他にも、ConnectorEnd(関連端)や、UML自体を説明するクラス図(があるのですが)を読むと、
connectable-element-name は、関連端名(厳密には ConnectorEndのroleの名前)で良さそうです。
よって、ここから下では、
「コネクタブルエレメント」を「関連端」
「コネクタブルエレメント名」を「関連端名」
と表記します。
※関連端名が分からない人は、クラス図の書きかた(関連端名のつけ方と確認のしかた)を参照。
関連端名は、ヘッダの一番左側に書きます。
「:クラス名」の左側に、「関連端名:クラス名」です。
クラス図とコードとシーケンス図を並べて書いてみると、以下のようになります。
class Hoge {
Piyo m_piyo = new Piyo();
public void Function()
{
m_piyo.Execute(); // 具体的に、どのPiyoインスタンスか分かって実装している。
}
}
class Piyo {
public void Execute(){ ... }
}
こうなると、クラス名だけのときに比べて分かりやすくなりましたね。
では、次に配列やリストのときに進みましょう。
##セレクタ
では、次に「セレクタ」です。
セレクタは、関連端名の右側に"[ ]"を使って書きます。
「関連端名[ ]:クラス名」です。
セレクタは、関連端が配列やリストなどのとき、インスタンスを特定するためのものです。
[ ] 内にインスタンスを特定する情報を書きます。
<selector> ::= <expression>
と定義されていて、expressionは「一つのノードを表す」との記述がありますので、
<selector>
には「配列やリスト内の特定の要素を示す数字や文字列」を書きます。
###要素番号でセレクトする場合
class Hoge {
List<Piyo> m_piyos = new List<Piyo>();
public void Function()
{
m_piyos[2].Execute(); // 要素数のチェックは省略
}
}
class Piyo {
public void Execute(){ ... }
}
class Hoge {
Dictonary<string,Piyo> m_piyos = new Dictonary<string,Piyo>();
public void Function()
{
m_piyos[u].Execute(); // key : string の値が "u"のものを実行。
// 要素有無のチェックは省略
}
}
class Piyo {
public void Execute(){ ... }
}
セレクタを書くと、どのインスタンスを呼ぶ実装をすればよいかが分かるようになりましたね。
###セレクタを省略した場合
セレクタを省略すると、UML仕様書には「代表値を示す」と書いてあるが、現実的には具体的に何を指すか分からなくなる。
また、セレクタの内容である expression の定義によると
If there are no operands, it represents a terminal node.
何も書かないと、終端のノードを示す
とのことだが、これまた、配列やリストでは、終端とは何を示すのかが明らかではないので、実務には適さないと思われます。
##内部展開したシーケンス図名
(※表記法として説明しますが、この表記を使っている実務現場を経験したことはありませんし、個人的には習得しなくてよいと考えます。)
ライフラインのクラスが、下図のように内部構造を持っていた場合、
このPiyoのExcecute()の中では、再び m_piyopiyoと、m_piyora との間のシーケンス図が存在するかも知れません。
仮に、PiyoクラスのExecute()が呼ばれた後の内部展開されたシーケンス図を描くのであれば、この内部展開したシーケンス図名を使うことができます。
内部展開したシーケンス図名は、ヘッダの一番右側に書きます。
「関連端名[ ]:クラス名 ref 内部展開したシーケンス図名」です。
class Hoge {
Dictonary<string,Piyo> m_piyos = new Dictonary<string,Piyo>();
public void Function()
{
m_piyos[u].Execute(); // 要素有無のチェックは省略
}
}
class Piyo {
Piyopiyo m_piyopiyo = new Piyopiyo();
Piyora m_piyora = new Piyora();
public void Execute(){
m_piyopiyo.Function();
m_piyora.Function();
}
}
##ヘッダ記載項目の省略
関連端名[セレクタ] : クラス名 ref 内部展開したシーケンス図名
の内、
表記法のルール上では、
「関連端名」か「:クラス名」を書けば、あとは省略してよいことになっています。
だが、それに甘んじていると、実務に混乱をきたすので、
下記、まとめを指標に推奨します。
#まとめ
シーケンス図のライフラインヘッダは、
通常(配列やリストではない場合): 関連端名 : クラス名
配列やリストの場合: 関連端名[セレクタ] : クラス名
を書けば、コーディングで間違うことはないでしょう。
(また、よく考えもせずにシーケンス図を書き終えることもないでしょう)
この基準に準じた例を以下に再掲します。
##通常(配列やリストではない場合): 関連端名 : クラス名
class Hoge {
Piyo m_piyo = new Piyo();
public void Function()
{
m_piyo.Execute();
}
}
class Piyo {
public void Execute(){ ... }
}
##配列やリストの場合: 関連端名[セレクタ] : クラス名
###要素番号でセレクトする場合
class Hoge {
List<Piyo> m_piyos = new List<Piyo>();
public void Function()
{
m_piyos[2].Execute(); // 要素数のチェックは省略
}
}
class Piyo {
public void Execute(){ ... }
}
class Hoge {
Dictonary<string,Piyo> m_piyos = new Dictonary<string,Piyo>();
public void Function()
{
m_piyos[u].Execute(); // 要素有無のチェックは省略
}
}
class Piyo {
public void Execute(){ ... }
}
※キーでセレクトする場合は、モデリングが良くできる人でしたら、クラス図に限定子を使ってもOKです。
#参考
『OMG Unified Modeling Language Sperstructure Ver2.4.1』
『UML2.0仕様書 2.1対応』Object Management Group (著), 西原 裕善 (翻訳) オーム社 (2006/11)