アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

UMLを描こう - Vol.2 シーケンス図

※より具体的な例は こちらの新しい記事 を参照してください。

シーケンス図とは?
シーケンス図とは,オブジェクトの動的な相互作用を表現するためのUML図です。
オブジェクト指向は,一言で言えば「役割分担」なので,オブジェクト同士のコミュニケーションが重要です。
シーケンス図では,オブジェクト同士のコミュニケーションによるインタラクション(相互作用)の様子を明確に表すことができます。
シーケンス図においてインタラクションは,メッセージ送受信のシーケンスとして図示されます。
クラス図がクラスの静的な定義とするなら,シーケンス図はオブジェクトの動的な振る舞いの定義と言えます。

3秒で分かるシーケンス図の描き方
まずは登場するオブジェクトを並べます。

長方形の中に「オブジェクト名:クラス名」と書いてオブジェクトを表します。
オブジェクト名とクラス名のどちらか一方を省略してもかまいません。
オブジェクトの下には「ライフライン」と呼ばれる線を引きます。
ライフラインは,オブジェクトがメモリ上に存在していることを表しています。

あるオブジェクトが別オブジェクトのメソッドを呼び出すときは,下図のように表します。

ここではcartControllerがcartのgetTotalPrice()メソッドを呼び出し,cartは戻り値を返しています。
メソッド呼び出しは,先端を黒く塗った矢印(callメッセージ)で表します。
returnは,点線矢印(replyメッセージ)で表します。replyメッセージは省略してもかまいません。
なお,ライフライン上に乗っている長方形は,活性区間バーと呼びます。
活性区間バーは,オブジェクトが処理を実行中であることを示します。

戻り値を受け取る変数名や,メソッドの引数は下図のように表します。

callメッセージのキャプションとして,
「戻り値を受け取る変数名 = メソッド名(引数1, 引数2, ...)」
と書くわけです。

クラスのインスタンスを生成するときは,
下図のように≪create≫ステレオタイプを付けて表します。

≪create≫ステレオタイプさえついていればよいので,
生成メッセージ名は「Java風に クラス名()」「PHP風に __construct()」「シンプルに new()」のどれでもかまいません。

メソッド呼び出し元のオブジェクトを明示しない場合は,矢印を黒丸から始めます。


上図においてcartControllerが自分のメソッドdialog()を呼び出したいとすると,
メッセージの矢印を自分に向ければOKです。


ちなみに,分岐や繰り返しを表記する記法も仕様としてあるのですが,
図が複雑になってしまうので,下図のようにコメントで表現することをお勧めします。


最後に,非同期callメッセージについて説明します。
非同期callメッセージの場合は,通常の(同期)callメッセージと違い,
メッセージを送るだけで返答を受け取らずそのまま次の処理に移ります。
非同期callメッセージは,矢印の先を黒くぬらずに表現します。

メソッド呼び出しは通常,同期callメッセージで表されるので,
非同期callメッセージは記憶の片隅に置いておく程度で問題ないです。

以上で,シーケンス図の描き方を説明しました。
シーケンス図を描くことで,処理全体のオブジェクト相互作用の様子をつかむことができます。
オブジェクトの振る舞いをコードではなくシーケンスとしてイメージできるようになれば,設計の視野を広げることができます。
また,プログラマ同士でクラスの実装について議論する場合,クラスファイル群に向き合うよりも,シーケンス図を見ながら議論すると効果的です。
皆さんもぜひ,シーケンス図を描いてみてください。

クラス図+シーケンス図=詳細設計
前回,クラスの静的な設計はクラス図で表せることを説明しました。
そして今回,オブジェクト同士の動的なインタラクションがシーケンス図で表せることを説明しました。
クラス図が静的な側面の設計であり,
シーケンス図が動的な側面の設計だという感覚がつかんでいただけたでしょうか?
クラス図とシーケンス図が揃えば,コードに極めて近い設計=詳細設計を表すことができます。
よって前回,「詳細設計ドキュメントを読み書きする際に最低限知っておくべき図は,クラス図とシーケンス図」と述べたわけです。


シーケンス図上でプリファクタリングしよう
皆さんは,「なんじゃこのクラス,使いずら。機能つめこみすぎ!」と思ったことはありませんか?
私は毎日のようにあります。言語によらず。
では,なぜそのような使いづらいクラスができてしまったのでしょうか?
それは,実装者が設計プロセスをすっとばして思いつきでコーディングしてしまったためです。
使いやすくて喜ばれるクラス設計を行うには,シーケンス図を事前に描いてインタラクション全体を見直していくことが大切です。
これをプリファクタリングと呼びます。
コーディングの前にプリファクタリングしておけば,設計がすっきりして使いやすいクラスが実装できます。
「実装後にリファクタリングすればいいのでは?」と思う人がいるかもしれませんが,実装後ではあまりに遅く,修正による副作用やテストのやり直しを考えると,極めてリスクが高いと言えます。
したがって,良いクラス設計を得るには,プリファクタリングが重要であり,かつ安全というわけです。
システムの中で重要な処理は,必ず実装前にシーケンス図を描きましょう。


ではどうやって0から,クラス図とシーケンス図を導くのか?
あるシステムを実装したいという「要求」から,どうやって,クラス図とシーケンス図を導けばよいのでしょうか?やはり,センスに頼るしかないのでしょうか?
いいえ,実はクラス図とシーケンス図を導く手法がすでにあります。心配はいりません。
具体的には,OOA(Object-Oriented Analysis: オブジェクト指向分析)と,OOD(Object-Oriented Design: オブジェクト指向設計)という2つの手法を用います。
まず,OOAで,要求を定義・分析して,分析レベルの設計図やドキュメントを作ります。次に,その分析レベルの成果物を元に,OODを用いてクラス図とシーケンス図を徐々に導出していきます。あとはそれをOOP言語で実装すれば完成です。
ちなみに,「OOA,OOD,OOP,そしてテスト手法」を統合したフルセットの開発手法のことを,OODP(Object-Oriented Development Process: オブジェクト指向開発プロセス)と呼びます。