[UMLとオブジェクト指向分析・設計が開発リスクを軽減する]
設計
設計では,構造を中心としてとらえていた分析モデルを実装を考慮した制御的なモデルに洗練していきます.設計作業では,クラスではなく実際に生成されたオブジェクトどうしの活動に焦点を当てるため,モデル検討の際にも,静的な構造を表わすクラス図ではなく,オブジェクト単位の表現が可能なコラボレーション図やシーケンス図を多用します.
設計作業はさらに,システム全体に対するトップダウン的視点からのシステム設計と,クラスやサブシステムを洗練していくオブジェクト設計のふたつのフェーズに分けられます.本稿では紙面の都合からシステム設計に絞って話を進めていきたいと思います.
システム設計における具体的な作業および順序は以下のようになります.
・イベントを整理する
・イベントに対するコラボレーションを設計する
・全体のスレッド構成を決める
・アクティブオブジェクトを追加する
・共有されるオブジェクトについて検討する
・ハードウェアクラスの構造を検討する
・デザインパターンを適用する
(1)イベントを整理する
組み込みシステムでは外部環境から非常に多くのイベントを受け取ります.ここでは,コンテキスト図やユースケース図の作成時に見つけ出されたイベントを整理し,クラス構造として表わします.このようにすることで,イベントの構造をわかりやすくするとともに,すべてのイベントをポリモフィックに扱える汎用的なイベント処理が可能になります.
まず,すべてのイベントのルートとなる「イベント」クラスを定義し,そこから新たに「物理イベント」と「論理イベント」を派生させます.「イベント」クラスは属性としてすべてのイベントに必要な発生時刻や発生元の情報などを持ちます.「物理イベント」はハードウェアなどの割り込み処理やOSからの呼び出しの際に使用される外部環境からのイベントを表わします.「論理イベント」はサブシステム間での呼び出しなどのようなシステム内部で使用されるイベントを表わします.
次に,外部イベントと内部イベントを上記にしたがって「物理イベント」と「論理イベント」に分類し,さらにそれぞれから派生した独立のクラスとして定義します.
エレベータシステムの場合は,さらにイベントを目的に応じてパッケージ単位に分けます.エレベータパッケージでは,パッケージ外に公開するイベントを別のパッケージにまとめています.このような構成をとると,ハードウェアからの物理イベントのようなパッケージ内でのみ使用されるイベントは他のパッケージからは参照されないため,ハードウェア変更に伴うイベントの変更などがあった場合でも変更の影響をエレベータパッケージ内に局所化することができます.エレベータシステムのイベントのクラス図を図11,図12に示します.
図11:エレベータ内部イベント
図12:エレベータ外部イベント
(2)イベントに対するコラボレーションを設計する
イベントを受け取った後,サブシステム内部のオブジェクトがどのように相互作用しあって処理を行うのかをコラボレーション図で表わします.複数のサブシステムにまたがって処理が行われる場合は,先に分類した論理イベントを経由して他のサブシステムと繋がることになります.この時点では,どのオブジェクトがどのような順番で相互作用するのかに重点を置き,具体的なイベントの送受信方法までは検討しません.
(3)全体のスレッド構成を決める
ここでは,スレッドの構成とそれに合わせたオブジェクトどうしのコラボレーションの洗練を行っていきます.
●ひとつのイベントをひとつのスレッドで
処理した場合
この方法をとる場合,外部イベントに対する一連の処理ごとにスレッドを割り当てることになります.検討にはこの前の作業で作成したコラボレーション図を使用します.
まず,外部とのインターフェイスとなるオブジェクトに注目します.GUI,ネットワーク,ハードウェアラッパー,他のシステムとのインターフェイスなどがこれに該当します.外部イベント処理はこれらのオブジェクトを起点として処理されますので,最低でもこのインスタンス数分のスレッドを用意しなくてはなりません.
次に,各イベントごとのコラボレーション図を使って,さらに細かなレベルでの検証を行います.その前に,今まで論理イベントとして表現していたサブシステム間の呼び出しについては,すべてオブジェクトのメソッド呼び出しに変更しておきます.コラボレーション図の中に,以下のようなオブジェクトが含まれているかどうかを調べます.
・同時並行に動作させたいもの
・実行時間の長い処理を行うもの
もしこれらに該当するオブジェクトがあれば,それらは別スレッドを割り当てる候補になります.別スレッドにする場合は,それらのオブジェクトへの呼び出し部分をイベントを使用した非同期通信に変更し,そのイベントに対しては新しい論理イベントを割り当てます.
●ひとつのイベントを複数のスレッドで
処理した場合
この方法の場合は,ある程度機能ごとに分割されているサブシステムを使用することができます.
まず,各サブシステムのインスタンスに対してスレッドをひとつずつ割り当て,これをシステム全体のスレッド構成のベースとします.
次に,これらのスレッドの必要性について検討します.各インスタンスが同時並行に動作するもの,実行時間の長いものなどは別々のスレッドでかまいませんが,順番に処理が行われても問題のないもの,実行時間の短いものなどは他のインスタンスとスレッドを共有できるかどうか検討を行います.スレッドの数が多いとリソースや効率面からマイナスとなるため,共有が可能であればそれらに割り当てたスレッドをマージしていきます.
反対に,前の方法と同じくサブシステム内で別スレッドを作る必要性についても検討を行います.アーキテクチャ設計の項で述べたようなスレッド構成のパターンを適用する場合などは,さらに詳細なスレッド構成を検討する必要があります.
今回のエレベータシステムでは,エレベータ,フロア,システム制御の各サブシステムはすべて同時並行に動作する必要があるためスレッド数はそれらのインスタンスの総計となります.さらに,エレベータサブシステムの場合はリクエストの受付とリフトの操作を別々に行う必要があるので,リフト部分には別スレッドを割り当てることになります.
(4)アクティブオブジェクトを追加する
導出した各スレッドごとに,スレッドクラスのオブジェクトをひとつとメッセージキュークラスのオブジェクトを必要な分だけ,それぞれ割り当てます.
スレッドクラスは自ら起動を行うアクティブクラスであり,OSごとに異なるスレッドの生成や削除に関する処理を行います.各スレッドはそれを所有する特定のメソッドを呼び出します.メッセージキュークラスは,スレッドと同様にOSに固有の仕組みを使ってメッセージキュー機能を実現します.
(5)共有されるオブジェクトについて検討する
(3)の作業で,いくつかのスレッドにまたがるオブジェクトやリソースが見つかることがあります.これらの共有されるオブジェクトに対しては,どのスレッドに割り当てるか,アクセス方法や排他制御はどのように行うか,などを検討しなければなりません.
共有されるオブジェクトへの呼び出しが同期通信の場合には,呼び出されるメソッドの前後で排他制御を行う必要があります.また,特に同じインスタンスを共有しなくても良いのであれば,別々のオブジェクトにしてそれぞれのスレッドに持たせてしまうことも可能です.ただし,その際はリソースとのトレードオフとなります.
ここで,エレベータシステムに対し(2)から(5)までの作業を行った結果のクラス図およびコラボレーション図の一部を図13,図14に示します.
図13:エレベータパッケージ内クラス図
図14:フロア押下時のコラボレーション図
(6)ハードウェアラッパーの構造を検討する
ここでは,ハードウェアラッパーの実装方法について考えてみます.ハードウェアラッパーは外部に対し,ハードウェアの操作プロトコルや物理的な違いなどを隠蔽した抽象的なインターフェイスを提供する一方,内部ではそれを具体的なハードウェア操作に変換し実際の制御を行います.たとえばエレベータリフトクラスは,外部に対し目的のフロアを引数とし「移動する」という簡易な操作を提供しますが,内部ではフロアセンサーを使って現在のフロア位置を調べながら目的のフロアに到着するまでのモーター制御を行います.
このようにハードウェアのインターフェイスと実装を分離するとハードウェアの内部仕様が固まらないうちからソフトウェアの設計を進めることが可能になります.またスイッチやボタン,モーターなど汎用的に使用されるハードウェア部品についてはあらかじめそれらの制御操作を提供するハードウェアラッパークラスとして作成しておくことも可能です.
ハードウェアラッパーを実装する際には,物理的なハードウェアとの接点に注意する必要があります.実際のハードウェアの操作部分については効率や拡張性の面からもハードウェアラッパー内で実装するよりCやアセンブラで記述されたドライバなどを利用する方が望ましいでしょうし,イベントを発生するハードウェアラッパーの場合にはアセンブラなどで記述された割り込みハンドラからハードウェアラッパーオブジェクトを呼び出すための仕組みを考えておかなければなりません.
なお,分析では実際のハードウェア構成にあわせてクラスを導出しましたが,将来のハードウェアの変更や物理的な制約などに影響されないためには,ある程度抽象的なハードウェアとしてクラスを作成することも重要になります.
(7)デザインパターンを適用する
オブジェクト指向では特定の問題に対して有効なクラスの組み合わせを,パターンとして分類しています.パターンを使うことで,より確実で有効な設計を行うことが可能になります.また,設計の意図もより伝わりやすくなります.パターンは非常に多くのものが紹介されています.ここでは,特に組み込みシステムで使われることの多いパターンについて簡単に紹介したいと思います.
最初の7つは有名なGammaらによる『オブジェクト指向における再利用のためのデザインパターン』(★注1)から,最後の3つは『REAL-TIME
UML』(★注2)から紹介します.なお,これらのパターンの具体例やクラス図などについては今後弊社webなどで公開していく予定です.
●Abstract Factory
システムのハードウェア構成がシリーズやバージョンごとに少しずつ異なるような場合,Abstract
Factoryを使用することでハードウェアクラスのインスタンス生成に関わる制御を隠蔽します.
●Composite
おもに,ハードウェアクラス同士の構造を表わすのに使用されます.
●Command
スレッド間で受け渡されるイベントなどに使用されます.
●Facade
複数のハードウェアクラスをまとめて,ひとつのハードウェアのように見せる場合などに使用されます.
●Strategy
ハードウェアクラスの提供するサービスを複数の方法で実現したい場合などに使用されます.たとえば,センサーのもつ補正アルゴリズムを複数種類サポートしたい場合などは,アルゴリズムに該当する部分だけをStrategyクラスとして独立させます.
●State
多くの状態遷移を持つクラスを,複数の状態クラスの集合で置き換えます.ただし,状態が極端に多い場合などは,その分クラス数が増えてしまうので管理が大変になります.
●Mediator
ハードウェアクラス同士が直接イベントを送り合うと,ハードウェアの変更などによる仕様変更に対応できなかったり双方に多くの依存関係が発生することになります.これを解決するために,Mediatorとして振る舞うコントローラクラスを導入します.
●Transaction
このパターンは,プロトコル処理から送達確認を行う部分だけTransactionクラスとして独立させることで,プロトコルを階層的に設計する事を可能にします.
●Rendezvous
スレッド間で何らかの同期を行いたい場合に使用します.このパターンを使用すると,各OSごとに異なる同期の判別処理を局所化する事ができます.競合するリソースの管理などにも,このパターンを使用することができるでしょう.
●State
Table
これは,先に紹介したStateパターンに,遷移処理を行うためのTransitionクラスをイベント単位で追加することにより,より細かなクラスの協調で状態遷移の設計を行えるようにしたパターンです.状態遷移の設計でよく使用される「状態」と「イベント」からなる状態遷移表の各ます目の処理を行う部分がTransactionオブジェクトになります.
© 1999 OGIS-RI Co., Ltd. |
|