作業:
|
目的
|
|
役割: 設計者 | |
頻度: 反復ごとに 1 回 | |
ステップ | |
入力とする成果物: | 結果となる成果物: |
ツール・メンター: | |
More Information: |
ワークフローの詳細: |
クラスは、設計作業をこなす作業担当として、システムの実際の作業を遂行します。その他の設計要素であるサブシステム、パッケージ、およびコラボレーションは、クラスのグループ化の方法、あるいはクラスの相互運用方法を記述します。
アクティブ・クラスは、パッシブ・クラスの振る舞いを調整および駆動する設計クラスです。アクティブ・クラスのインスタンスはアクティブ・オブジェクトであり、このオブジェクトはそれ自体の制御スレッドを備えています。
設計パターンおよびメカニズムを、設計するクラスまたは機能に適合するように、かつプロジェクト設計ガイドラインに従って使用します。
パターンおよびメカニズムの組み込みはこの作業の後続の手順 (新規のクラス、操作、属性、および関係の追加) の多くを効果的に実行しますが、パターンまたはメカニズムにより定義されるルールに従います。
パターンとメカニズムは通常、設計の進展と共に組み込まれるものであり、この作業の最初の手順としてだけでないことに注意してください。パターンとメカニズムは単一のクラスだけでなく、クラスのセットにも頻繁に適用されます。
この作業への入力として与えられている分析クラスに対して 1 つまたは複数の初期設計クラスを作成し、追跡依存関係を割り当てます。このステップで作成した設計クラスは、分析クラスの設計方法を説明するさまざまな設計プロパティーを割り当てる後続のステップで、改良、調整、分割あるいは統合されます。この設計プロパティーには、操作、メソッド、および状態マシンなどがあります。
設計対象の分析クラスのタイプ (境界型、エンティティー型、コントロール型) に応じて、初期設計クラスの作成に使える特定の戦略が決まります。
バウンダリー・クラスはユーザーに対するインターフェースまたは他のシステムに対するインターフェースのいずれかを表します。
他のシステムへのインターフェースを表すバウンダリー・クラスは、内部の振る舞いが複雑なことが多いため、サブシステムとしてモデル化することが一般的です。インターフェースの振る舞いが単純な (外部システムに対する既存の API へのパススルーとしてのみ働く) 場合は、1 つまたは複数の設計クラスを使ってインターフェースを表すことができます。この方法を選択した場合は、プロトコル、インターフェース、または API ごとに単一の設計クラスを使い、クラスの特殊な要求の中で、使用する標準などについての特殊な要求に着目します。
ユーザーに対するインターフェースを表すバウンダリー・クラスは、通常、ユーザー・インターフェース中の各ウィンドウについて 1 つのバウンダリー・クラス、または各フォームについて 1 つのバウンダリー・クラスというルールに従います。 この結果として、バウンダリー・クラスの責任はかなりハイレベルなものになり、このステップで改良し、詳細を詰める必要があります。ユーザー・インターフェースの追加モデルまたはプロトタイプは、この手順で考慮すべきもう 1 つの入力ソースになることが考えられます。
バウンダリー・クラスの設計は、プロジェクトで使用できるユーザー・インターフェース (UI) 開発ツールによって異なります。現在の技術を使用し、開発ツール内で UI を直接ビジュアルに構築するのが一般的です。これにより、コントロールおよびエンティティー・クラスの設計に関連付ける必要のある UI クラスが自動的に作成されます。UI 開発環境が UI を実装するために必要なサポートするクラスを自動的に作成する場合、それらを設計の中で考慮する必要はありません。 開発環境で自動的に作成されないものを設計するだけです。
分析時に、エンティティー・クラスは情報の操作される単位を表します。 エンティティー・クラスは、パッシブかつ永続的であることが多く、永続性の分析メカニズムとして識別され、関連付けられることがあります。 データベースをベースとした永続性メカニズム設計の詳細は、『作業: データベース設計』で説明します。性能を考慮した場合、永続的なクラスの再編成が必要になり、役割: データベース設計者と役割: 設計者との間で共同で議論される設計モデルへの変更を伴う場合があります。
永続的クラスの設計問題に関する幅広い議論が、後述の『永続的クラスの識別』に示されています。
コントロール・オブジェクトには、ユース・ケースの流れを管理する責任があり、ユース・ケースのアクションのほとんどを調整します。コントロール・オブジェクトは、ユーザー・インターフェースの問題 (境界オブジェクト) またはデータ工学の問題 (エンティティー・オブジェクト) には特に関係していないロジックをカプセル化します。このロジックは、アプリケーション・ロジックまたはビジネス・ロジックと呼ばれることがあります。
コントロール・クラスを設計する際に以下の課題を考慮してください。
- ユース・ケースを調整する振る舞いが UI の中に埋め込まれるようになり、システム変更がますます難しくなります
- 異なるユース・ケースの実現中では、同じ UI を簡単には使えません
- UI に余分な機能が付いて重荷となり、性能を劣化させます
- このエンティティー・オブジェクトにはユース・ケース特有の振る舞いが付いて回るようになり、汎用性が減少します
このような問題を避けるために、コントロール・クラスを導入して、イベント・フローの調整に関する振る舞いを提供しています。
後の 2 つの場合で、コントロール・クラスが独立した制御スレッドであるときは、アクティブ・クラスを使って制御スレッドをモデル化することがさらに適している場合があります。
永久媒体上に状態を格納する必要のあるクラスを、「永続的」であると言います。状態の格納は、クラス情報の永久記録やシステム障害時のためのバックアップ、あるいは情報交換のために必要となります。永続的クラスは、永続的インスタンスと一時的インスタンスの両方を持っていることがあります。あるクラスを永続的と言うのは、単にクラスのいくつかのインスタンスが永続的である必要があるという意味です。
また、分析中に発見した永続性メカニズムに対応する設計メカニズムも組み込みます。例えば、クラスによって必要となるものによって、永続性の分析メカニズムは以下の設計メカニズムのいずれかにより実現される場合があります。
永続的オブジェクトはエンティティー・クラスからのみ派生するわけではありません。永続的オブジェクトは一般に機能外要求を処理するためにも必要となる場合があります。 例としては、プロセス制御に関する情報を保持したり、トランザクション間の状態情報を保持したりするために必要な永続的オブジェクトがあります。
永続的クラスを識別することは、役割: データベース設計者に、このクラスが物理的格納特性に特別な注意が必要であることを知らせるのに役立ちます。これはまた、役割: ソフトウェア・アーキテクトに対しクラスが永続的である必要があること、および役割: 設計者に対しクラスのインスタンスを永続的にする必要があることも知らせます。
調和のとれた永続的な方針が必要なため、役割: データベース設計者は、永続的なフレームワークを使って永続的クラスをデータベースにマッピングする責務を負います。プロジェクトで永続的フレームワークを開発している場合、フレームワーク開発者は設計クラスの永続的な要求も把握しておく責任があります。このような人々に必要な情報を提供するには、この時点では、クラスが永続的であること、またはより正確にいえばクラスのインスタンスが永続的であることを示すだけで十分です。
各クラスに対して、そのクラスが存在しているパッケージでのクラスの可視性を決定します。public クラスは、それが含まれているパッケージの外側から参照可能です。private クラス (または可視性がimplementation のもの) は、同一パッケージ内のクラスからのみ参照可能です。
設計クラスで操作を識別するには
操作は、シーケンス図に提示されるメッセージをサポートする必要があります。これは、スクリプト、つまり操作にまだ割り当てられていないメッセージ (一時的なメッセージ仕様) で、クラスが遂行すると思われる振る舞いを記述するからです。 図 1 はシーケンス図の例を示しています。
図 1: 操作識別の基礎を形成するメッセージ
ユース・ケースの実現では、すべての操作を明確にするための十分な情報を提供できません。明確になっていない操作を探すには、次のことを検討してください。
public 属性の値を単に取得および設定する操作を定義しないでください (『属性の定義』と『関連の定義』を参照)。通常、これらはコード生成機能により生成され、明示的に定義する必要はありません。
操作、戻り型、パラメーターとそれらの型を命名する場合は、実装言語用の命名規則を利用します。
各々の操作に対して、次を定義してください。
操作の定義が完了すると、各メッセージに対してどの操作が呼び出されるかという情報を付けてシーケンス図を完成させます。
詳しくは、『ガイドライン: 設計クラス』の『クラス操作 』を参照してください。
操作ごとに、操作のエクスポートの可視性を以下から選択して定めます。
操作の目的を達成できて、しかも最も可視性を制限できるものを選びます。こうするには、シーケンス図を見て、各メッセージに対して、それが受け手のパッケージの外部のクラスから (public レベルの可視性が必要)、パッケージの内部から (implementation レベルの可視性が必要)、サブクラスから (protected レベルの可視性が必要)、クラス自体または <<friend>> から (private レベルの可視性が必要) 送られるのかを決定します。
ほとんどの場合、操作はインスタンス操作で、クラスのインスタンスに対して実行します。ただし、場合によっては、操作はクラスの全インスタンスに適用され、クラス範囲の操作となります。クラス操作の受け手は、実際には、クラスの特定のインスタンスというよりは、クラス自体の説明であるメタクラスのインスタンスです。クラス操作の例としては、新インスタンスを生成する (インスタンス化する) メッセージがあり、クラス allInstances などを戻します。
クラス範囲の操作を表すには、操作の文字列に下線を施します。
メソッドは操作の実装を指定します。多くの場合、操作で必要となる振る舞いは操作名、説明およびパラメーターで十分に定義でき、メソッドはプログラミング言語で直接実装します。操作の実装に特別なアルゴリズムが必要な場合、あるいは操作の記述に示されている以上の情報が必要な場合は、別個にメソッドの記述が必要です。このメソッドでは、操作が単に何をするかだけでなく、どのようにして動作するかを記述します。
記述がある場合、メソッドは以下を説明しなければなりません。
要求はケースバイケースで異なりますが、クラスのメソッド仕様は常に以下を定めます。
より具体的な要求としては、次のようなものがあります。
このような情報に対して、シーケンス図は重要な情報源となります。これらのダイアグラムから操作を実行する場合に、他のオブジェクトで何の操作が使用されるのかが明らかになります。操作を完全に実装するには、他のオブジェクトでどの操作が必要になるのかという仕様が必要となります。完全なメソッド仕様の作成には、関連するオブジェクト向けの操作の識別および対応するシーケンス図の検査が必要です。
ある種の操作に対しては、操作の振る舞いは受信者のオブジェクトの状態に依存しています。状態マシンはオブジェクトが取り得る状態を説明するツールであり、オブジェクトをある状態から別の状態へ遷移させるイベントを説明するツールです。これについては、『ガイドライン: ステートチャート図』を参照してください。状態マシンは、アクティブ・クラスの説明に最も役立ちます。
単純な状態マシンの例を図 2 に示します。
図 2: 給油機に関する単純なステートチャート図
各遷移イベントは操作と関連付けることができます。オブジェクトの状態に応じて、操作は異なる振る舞いをします。遷移イベントでは、それがどのように起きるのかを記述します。
関連操作に対するメソッドの記述は状態特有の情報で更新すべきで、各々の適切な状態に対して、操作で何をすべきかを示します。状態を表現するために属性がよく使われます。ステートチャート図は属性の識別ステップへの入力として役立ちます。
詳細は、『ガイドライン: ステートチャート図』を参照してください。
メソッドの定義および状態の識別時に、操作の実行のためにクラスが必要とする属性を明確にします。属性はクラス・インスタンス用の情報記憶であり、クラス・インスタンスの状態を表すのによく使用されます。クラス自体が保持する情報は、属性によって保持されます。 各属性について以下を定義します。
全属性が必要かどうかを明確にします。属性はその正当性を確かめておくべきです。属性をプロセスの初期段階で追加したものの、近視眼的であったために不必要になってもその属性が依然として存在し続けることが簡単に発生します。余分な属性がインスタンスの数千倍または数百万倍に増えると、システムの性能と記憶領域要求に有害な影響を与えます。
詳しくは『ガイドライン: 設計クラス』の『属性 』の項を参照してください。
オブジェクト間で通信が必要なケースでは、次の質問をしてください。
この方法でモデル化したリンクは一時的なリンクで、コラボレーションという特定のコンテキストの中で限られた時間しか存在しません。その意味では、それらはコラボレーションの関連ロールのインスタンスです。一方、クラス・モデル内の関係 (つまり、コンテキストから独立した関係) は、前述のとおり、依存関係です。『RUM98』で述べられている一時的なリンクの定義は、次のとおりです。「そのようなリンクすべてを関連としてモデル化することはできますが、関連に対する条件を幅広く記述することが必要です。そうしないと、オブジェクトの組み合わせを制限する際にリンクの精度が大部分失われます。」 このような状況では、依存関係のモデル化は、コラボレーション内の関係のモデル化ほど重要ではありません。それは、依存関係では関係を完全に記述できず、関係が存在することしか記述できないからです。
関連は、オブジェクトが相互にやりとりする仕組みを実現します。 メッセージが流れる通路をオブジェクトに提供します。また、クラス間の依存関係を文書化し、ある 1 つのクラスの変更を他の多数のクラスで認識できることを強調します。
各操作に対するメソッドの記述を調べ、クラスのインスタンスが他のオブジェクトとどのようにやりとりし、連携動作するのかを理解します。他のオブジェクトにメッセージを送るためには、オブジェクトにメッセージの受け手への参照が含まれている必要があります。コミュニケーション図 (シーケンス図の代替) では、図 3 に示すように、リンク形式でオブジェクトのやりとりを示します。
図 3: コミュニケーション図の例
残りのメッセージでは、関連または集約を使い、やりとりする 2 つのクラスのインスタンス間の関係を指定します。 適切な表現手段を選択するためには『ガイドライン: 関連』および『ガイドライン: 集約』を参照してください。 この両方の関係について、コミュニケーション図のリンクの可視性をフィールドに設定します。その他の作業としては、次のものがあります。
関連と集約は、関連するクラスを表すクラス図で定義することが最適です。クラス図は関連するクラスを含むパッケージで所有すべきです。図 4 は関連と集約を明記したクラス図の例を示しています。
図 4: クラス間の関連、集約、汎化を表すクラス図の例
分析クラス間の通知予約関係は、クラス間のイベント依存関係を明確にするために使います。設計モデルでは、既存のイベント・ハンドラー・フレームワークを使うか、独自のイベント・ハンドラー・フレームワークを設計および作成し、このようなイベント依存関係を明示的に処理する必要があります。Visual Basic のようなプログラミング言語では、対応するイベントを素直に宣言、発生、処理できます。その他の言語では、再利用可能な関数のライブラリーを追加して、通知予約とイベントを処理する必要があります。この機能を購入できない場合は、設計して作成する必要が生じます。『ガイドライン: 通知予約関係』も参照してください。
クラスの中には、複雑な抽象化/抽象概念を表し、複雑な構造を持つものもあります。 クラスをモデリングする際に、設計者は、内部に含まれる要素とそれらの関係を表現し、 それに従って実装担当者が該当クラス内で生じるコラボレーションを確実に実装できるようにしたい場合があります。
UML 2.0 では、クラスは内部構造や複数のポートを持つことができる構造化 されたクラスとして定義されています。そして、 クラスは、接続されたパートの集合や、さらに細かい単位に分解できる場合もあります。クラスをカプセル化し、外部からのコミュニケーションが宣言済みのインターフェースに従うポートを必ず経由するようにすることもできます。
複雑な構造を持つ複雑なクラスがある場合、該当クラス用の複合構造図を作成します。該当クラスの振る舞いに対応する役割を実行するパートをモデリングしてください。コネクターによってどのようにパートを「結び付ける」かを定義します。 該当クラスの別のクライアントが、そのクラスが提供する振る舞いの特定の一部にアクセスできるようにする場合は、ポートと宣言済みのインターフェースを使用してください。また、該当クラスの内部のパートを環境から完全に隔離する目的でも、ポートを利用します。
このトピックの詳細と複合構造図の例については、『概念: 構造化されたクラス』を参照してください。
クラスは、共通の振る舞いと構造を反映した汎化階層に編成される場合があります。共通のスーパークラスを定義し、サブクラスにスーパークラスの振る舞いと構造の両方を継承させることができます。 汎化は表記法上の便宜を図るもので、ある 1 箇所で共通の構造と振る舞いを定義すると、定義したものをその振る舞いと構造が繰り返される場所で再利用できます。汎化関係の詳細については、『ガイドライン: 汎化』を参照してください。
汎化できるものが見つかったら、共通の属性、関連、集約および操作を含む共通のスーパークラスを作成します。共通のスーパークラスのサブクラスになるクラスから、共通の振る舞いを取り除きます。サブクラスからスーパークラスへの汎化関係を定義します。
この手順の目的は、複数のユース・ケースが設計クラスのインスタンス群に潜在的に、一貫しない方法で同時にアクセスする場合に、並行アクセスの衝突を防ぐことです。
ユース・ケース単位で設計プロセスを進めていく上で問題となることの 1 つに、2 つ以上のユース・ケースが、潜在的に衝突する方法で設計オブジェクト上の操作を、同時に呼び出そうとすることが挙げられます。このような場合には、並行性の衝突を明確にし、明白に解決する必要があります。
同期メッセージ処理を利用すると、操作を実行してもその操作が完了するまで、オブジェクトに対する次の呼び出しは実行されません。同期メッセージ処理とは、メッセージを先着順で処理することを意味します。これにより、特に全メッセージが同一の優先度を持っていたり、同一の実行スレッドで動作したりする場合に、並行性の衝突を解決できます。1 つのオブジェクトが異なる実行スレッド (アクティブ・クラス) からアクセスされる場合は、明白なメカニズムを使って、並行性の衝突を防ぐ、または解決する必要があります。
同一オブジェクトに対する異なる操作を、並行性の衝突を引き起こさずに異なる実行スレッドにより同時に呼び出せる場合があります。つまり、顧客の名前も住所も、衝突を引き起さずに並行に変更できます。このようなことが可能なのは、衝突が発生するオブジェクトの同一プロパティーを 2 つの異なる実行スレッドが変更しようとする場合のみです。
異なる実行スレッドから並行にアクセスされる可能性がある各オブジェクトについて、同時アクセスから保護する必要があるコード部分を明確にします。推敲フェーズの初期には、特定のコード部分を明確にするのは不可能です。保護すべき操作だけで十分です。次に、衝突を起こす同時アクセスを防止する適切なアクセス制御メカニズムを選択あるいは設計します。このようなメカニズムの例としては、アクセスを順次行うためのメッセージのキューイング、セマフォーあるいはトークンを使った 1 度に 1 スレッドのアクセス制限、その他のロック・メカニズムなどがあります。このメカニズムの選択は、実装に大きく依存し、通常、プログラミング言語と操作環境によって異なります。
設計クラスは、 このステップへの重要な入力は分析クラスに対する機能外要求であり、これは、特殊な要求と責任の中で既に述べられています。このような要求は、クラスの実現にどのようなアーキテクチャー (分析) メカニズムが必要かという観点で指定される場合がよくあります。このステップではクラスを改善し、このような分析メカニズムに対応する設計メカニズムを組み込みます。
利用可能な設計メカニズムは、 必要とする各設計メカニズムに対して、できるだけ多数の特徴を付加し、適切な範囲を定義します。設計メカニズムの詳細については、『作業: 設計メカニズムの識別』、『概念: 分析メカニズム』、および『概念: 設計メカニズムと実装メカニズム』を参照してください。
クラスを設計する際に考慮しておくべき、いくつかの一般的な設計ガイドラインと設計メカニズムを示します。
この段階で設計モデルをチェックして、作業が正しい方向に進んでいるか確認します。詳細にレビューする必要はありませんが、以下のチェックポイントを考慮しなければなりません。
Rational Unified Process
|