ガイドライン: 設計パッケージ
トピック
設計モデルは、理解しやすくするために小さな単位に分割することができます。
設計モデルの要素をパッケージやサブシステムにグループ化し、これらのグループ間の関係を示すことにより、モデルの全体的構造が理解しやすくなります。 設計サブシステムは、1 つ以上のインターフェースを実現するコンポーネントとしてモデリングされます。詳しくは、『成果物: 設計サブシステム』および『ガイドライン: 設計サブシステム』を参照してください。これに対し、設計パッケージはグループ化のためだけのものです。
パッケージに含まれるクラスは、パブリックまたはプライベートにできます。パブリック (public) クラスは、他のすべてのクラスから関連づけを行うことができます。プライベート (private) クラスは、パッケージに含まれるクラスからのみ関連づけを行うことができます。
パッケージ・インターフェースは、パッケージのパブリック・クラスで構成されます。パッケージ・インターフェース (パブリック・クラス) は、他のパッケージへの依存関係を分離して実装します。これにより、開発の早期にインターフェースを確立することができ、開発者は他のパッケージのインターフェースにおける変更のみを知ればよくなるので、並行開発が容易になります。
設計モデルを分割するのには次のような理由があります。
- システムが完成したときに、パッケージやサブシステムを注文、構成、配布の単位として使用できます。
- 異なる開発チームの資源と能力を割り当てるには、異なる場所にいる異なるグループ間でプロジェクトを分割する必要が生じることがあります。適切に定義されたインターフェースを持つサブシステムでは、制御や調整を保ちながら作業を複数のチームに分割し、設計や実装を並行して進めることができます。
- サブシステムは、ユーザー・タイプを反映するような方法で、設計モデルを構成するのに使用できます。多くの変更依頼はユーザーから提出されます。サブシステムでは、特定のユーザー・タイプからの変更が、そのユーザー・タイプに対応するシステムの部分のみに影響することを確認できます。
- アプリケーションによっては、少数の人だけが特定の情報にアクセスできるようにする必要があります。サブシステムを使用すれば、必要な部分にセキュリティーを提供できます。
- サポート・システムを作成する場合は、サブシステムやパッケージを使用して、サポート対象のシステムと同様の構造を作成できます。このようにすれば、2 つのシステムの保守を同期化することが可能です。
- サブシステムは、システムが使用する既存の製品やサービス (COTS 製品やライブラリーなど) を表すのに使用されます。詳細は後で説明します。
バウンダリー・クラスをパッケージに割り当てる場合、2 つの方策を取ることができます。どちらの方策を使用するかは、システム・インターフェースが将来大幅に変更される可能性があるかどうかによります。
- システム・インターフェースが置き換えられる可能性のある場合、または大幅に変更される場合は、インターフェースを設計モデルの他の部分から分離します。こうすれば、ユーザー・インターフェースが変更されたときに影響を受けるのは、この部分だけで済みます。このような大幅な変更の例としては、行形式のインターフェースからウィンドウ形式のインターフェースへの切り替えなどが挙げられます。

インターフェースの大幅な変更を簡単にするのが主な目的である場合は、バウンダリー・クラスを 1 つ (または複数) の独立したパッケージに配置します。
- 大幅なインターフェースの変更が予定されていない場合は、インターフェースの変更ではなく、システム・サービスへの変更を基に計画を行います。この場合、バウンダリー・クラスは、機能的に関連するエンティティー・クラスやコントロール・クラスと共に配置する必要があります。これにより、エンティティー・クラスやコントロール・クラスが変更された場合、どのバウンダリー・クラスが影響を受けるかを簡単に判断できます。

システムのサービスへの変更を簡単にするために、バウンダリー・クラスを機能的に関連するクラスとパッケージにします。
エンティティー・クラスやコントロール・クラスと機能的に関連しない必須のバウンダリー・クラスは、同じインターフェースに属するバウンダリー・クラスと共に、独立したパッケージに配置します。
バウンダリー・クラスがオプションのサービスと関連する場合は、サービスを提供するために協力するクラスとグループ化し、独立したサブシステムに配置します。サブシステムを、オプションの機能が命令された場合に提供されるオプションのコンポーネントにマップします。
パッケージは、機能的に関連するクラスのグループごとに識別する必要があります。2 つのクラスが機能的に関連しているかどうかを判断するには、いくつかの実際的な条件を適用できます。次に、これらの条件を重要度の高い順番に挙げます。
- クラスの振る舞いや構造の変更により、他のクラスを変更する必要が生じる場合、2 つのクラスは機能的に関連しています。
例
エンティティー・クラス「注文」に新しい属性を追加した場合、コントロール・クラス「注文管理者」も更新しなければならない可能性が高くなりました。このため、この 2 つのクラスは同じパッケージ「注文処理」に所属します。
- あるクラスが別のクラスと機能的に関連するかどうかを見極めるために、片方のクラス (例えば、エンティティー・クラス) から始め、そのクラスを削除した場合に、もう一方のクラスにどのような影響が出るかを検討することができます。他方のクラスを削除したためにクラスが不要になる場合、そのクラスはなんらかのかたちで削除したクラスと関連づけられています。ここでいう「不要」とは、クラスが削除したクラスのみで使用されているか、削除したクラスに依存していることを指します。
例
「倉庫処理システム」に、2 つのコントロール・クラス (「注文管理者」と「注文登録者」) を含むパッケージ「注文処理」があります。どちらのコントロール・クラスも、倉庫での注文処理のサービスをモデル化しています。注文のすべての属性と関係は、注文処理のためのみに存在するエンティティー・クラス「注文」に格納されています。このエンティティー・クラスを削除すると、「注文管理者」や「注文登録者」は 「注文」が存在する場合にのみ機能するため、必要がなくなります。このため、エンティティー・クラス「注文」は 2 つのコントロール・クラスと同じパッケージに含める必要があります。

「注文管理者」と「注文登録者」は、「注文」がシステムから削除されると不要になるため、「注文」と同じパッケージに所属します。
- 2 つのオブジェクトが多数のメッセージをやり取りする場合、またはそうでないと相互のコミュニケーションが複雑になる場合、これらのオブジェクトは機能的に関連する可能性があります。
例
コントロール・クラス「作業実施者」は、「運送者インターフェース」と多くのメッセージをやり取りします。これは、これらのクラスが同じパッケージ「作業処理」に含める必要があることを示唆するものです。
- バウンダリー・クラスの機能がエンティティー・クラスを示す場合、バウンダリー・クラスはそのエンティティー・クラスと機能的に関連する可能性があります。
例
「倉庫処理システム」のバウンダリー・クラス「パレット型」は、エンティティー・クラス「パレット」をユーザーに提示します。各「パレット」は、画面上に ID 番号で表示されます。「パレット」に関する情報が変更された場合 (例えば、「パレット」に名前が与えられた場合)、バウンダリー・クラスを変更しなければならない可能性があります。このため、「パレット型」は「パレット」と同じパッケージに含める必要があります。
- 2 つのクラスが互いに相互作用する場合、または同じアクターの変更によって影響を受ける場合、これらの 2 つのクラスは機能的に関連する可能性があります。2 つのクラスに同じアクターがない場合、これらのクラスは同じパッケージに含めるべきではありません。ただし、このルールはより重要な理由がある場合、無視することもできます。
例
「倉庫処理システム」に「作業処理」というパッケージがあり、コントロール・クラス 「作業実施者」が含まれています。このパッケージはアクター「運送者」(倉庫内のパレットを移動する実際の運送者) にかかわる唯一のパッケージです。このアクターは、バウンダリー・クラス「運送者インターフェース」を介してコントロール・クラス「作業実施者」と相互作用します。このため、このバウンダリー・クラスはパッケージ「作業処理」に含める必要があります。

「運送者インターフェース」と「作業実施者」は、どちらも「運送者」アクターの変更によって影響を受けるため、同じパッケージに所属します。
- 2 つのクラス間に関係 (関連や集約など) がある場合、この 2 つのクラスは機能的に関連する可能性があります。もちろん、この条件はいつでも当てはまるものではありませんが、他の条件を適用できない場合に使用できます。
- クラスは、そのクラスのインスタンスを作成するクラスに、機能的に関連する可能性があります。
次の 2 つの条件は、2 つのクラスが同じパッケージに配置されるべきではない場合を定義します。
- 異なるアクターに関連する 2 つのクラスは、同じパッケージに配置すべきではありません。
- オプションのクラスと必須のクラスは、同じパッケージに配置すべきではありません。
まず、同じパッケージ内のすべての要素は、すべてオプションまたは必須でなければなりません。オプションのモデル要素を、必須のパッケージに配置することはできません。
例
必須のエンティティー・クラス「品目」には、「在庫補充しきい値」と呼ばれる属性が含まれています。しかし、在庫補充機能はシステムではオプションになっています。このため、「品物」を 2 つのエンティティー・クラスに分割して、オプションのクラスを必須のクラスに関連づける必要があります。
必須とみなされているパッケージは、オプションとみなされているパッケージに依存しない場合もあります。
一般的に、1 つのパッケージを 2 つの異なるアクターで使用することはできません。これは、1 つのアクターの振る舞いにおける変更は、他のアクターに影響を与えるべきではないためです。ただし、この規則には例外があります。オプションのサービスを設定するパッケージなどです。このタイプのパッケージは、アクターによってどのように使用されるかにかかわらず、分割すべきではありません。つまり、複数のアクターで使用されるパッケージやクラスは、パッケージがオプションの場合以外は分割します。
同じパッケージ内のすべてのクラスは、機能的に関連しなければなりません。「機能的に関連するクラスからパッケージを探す」の条件に従えば、1 つのパッケージ内のクラスは互いに機能的に関連するようになります。ただし、特定のクラスで、「あまりにも多くの」振る舞いや、クラスに属さない関係が含まれる場合があります。このような場合は、クラスの一部を削除して新規のクラスとするか、他のパッケージに属す別のクラスに移動します。
例
1 つのパッケージ内にあるコントロール・クラス A の振る舞いは、別のパッケージに含まれるクラス B に過度に依存するべきではありません。B に固有の振る舞いを分離するために、コントロール・クラス A を 2 つのコントロール・クラス (A' と A") に分割します。
B に固有の振る舞いは、B と同じパッケージに配置する新しいコントロール・クラス A" に入れます。また、新規のクラス A" には、元のオブジェクト A' への関係 (汎化など) も与えられます。

B に固有の振る舞いを分離するために、均一性を欠くコントロール・クラス A を 2 つのコントロール・クラス (A' と A'') に分割します。
あるパッケージ内のクラスが他のパッケージのクラスと関連する場合、これらのパッケージは互いに依存しています。パッケージの依存関係は、パッケージ間の依存関係を使ってモデル化されます。依存関係は、変更の影響を評価する助けとなります。多くのパッケージが依存するパッケージは、1 つのパッケージが依存するパッケージよりも、変更が困難になります。
このような複数の依存関係がパッケージの仕様決定において見つけられるため、これらの関係は作業中に変更されていきます。依存関係の記述には、どのクラス関係によって依存関係が生じたかが含まれることもあります。ただし、これにより維持の困難な情報が導入されるため、情報が適切で価値のあるものである場合のみ、この記述を行うようにします。
例
「倉庫処理システム」において、パッケージ「注文処理」からパッケージ「品目処理」 への依存関係が存在します。これは、「注文処理」のエンティティー・クラス「注文」が、他のパッケージのエンティティー・クラス「品目種別」と関連を持つために生じています。

パッケージに含まれる 2 つのクラスに関連があるため、パッケージ「注文処理」は「品目処理」に依存します。
パッケージのカップリングには利点と欠点があります。利点は、カップリングはすなわち再利用可能であるということ、欠点は、カップリングによりシステムの変更や展開を困難にする依存関係が発生することです。次のような一般的原則に従います。
- パッケージのクロス・カップリング (相互依存) は避けます。つまり、2 つのパッケージが相互に依存しないようにします。

これらの場合、パッケージを再編成し相互依存を取り除く必要があります。
- 下位レイヤーにあるパッケージは、上位レイヤーにあるパッケージに依存すべきではありません。パッケージの依存は、同じレイヤーにあるパッケージか、すぐ下位のレイヤーにあるパッケージに限ります。

このような場合、機能性を再パーティション分割する必要があります。1 つの解決策は、インターフェースの依存関係を示し、インターフェースを下位レイヤー内で構成することです。
- 一般的に、依存する振る舞いがすべてのレイヤーで共通でない限り、依存関係ではレイヤーを飛び越えるべきではなく、処理の呼び出しがレイヤーを通して渡されるようにします。
- パッケージはサブシステムに依存すべきではありません。依存は他のパッケージ、またはインターフェースに限られます。
|