ソフトウェア設計
ソフトウェアの要件が確定した後、ソフトウェア設計段階に入ります。ソフトウェア設計はソフトウェア工学の重要な段階です。ソフトウェア設計の基本的な目的は、「システムをどのように実装すべきか」という問いに答えることです。ソフトウェア設計のタスクは、ソフトウェア要件仕様書に規定された機能要素を、実際の条件を考慮して、ソフトウェアシステムの要件を満たす技術的ソリューションに変換し、次の段階のソフトウェア実装作業の基礎を築くことです。
ソフトウェア設計の概要
ソフトウェア設計は、ソフトウェア開発プロセスにおいてソフトウェア製品の品質を決定する重要な段階です。ソフトウェア設計段階で行われる決定は、最終的にソフトウェア開発の成否を左右し、さらに重要なことに、これらの設計決定はソフトウェア保守の容易さを決定します。
ソフトウェア設計活動は、高品質で低コスト、保守が容易なソフトウェアを獲得するための最も重要な要素の一つです。その主な目的は、ソフトウェアの青写真を作成し、様々な技術や実装方法の利弊を比較検討し、各種リソースを適切に配分し、ソフトウェアシステムの詳細な計画と関連モデルを構築し、ソフトウェア実装作業を円滑に進めることです。
ソフトウェア設計は開発期間の開始段階であり、ソフトウェア開発期間全体の品質に関わります。ソフトウェア開発期間の情報フローは、ソフトウェア設計がソフトウェア要求からソフトウェアコーディングに至るまでの橋渡し役を果たすことを示しています。図5-1を参照してください。

図5-1 ソフトウェア開発期間の情報フロー
ソフトウェア設計のタスク
ソフトウェア設計のタスクは、ソフトウェア要求仕様書を出発点として、要件分析段階で決定された機能に基づき、ソフトウェアシステムの全体構造を設計し、機能モジュールを分割し、各モジュールの実装アルゴリズムを決定し、ソフトウェアの具体的な設計計画を形成することです。ソフトウェア設計は、設計者の計画の中で、ソフトウェアがどのように顧客のニーズを満たすか、どのように容易に実装できるか、どのように新しい要件に適応するために機能を拡張できるかなど、異なる角度から検討される創造的な活動です。
ソフトウェア工学の観点から、ソフトウェア設計は一般に、概要設計と詳細設計の2つの段階に分けられます。図5-2を参照してください。ソフトウェアプロジェクトの規模と複雑さに応じて、概要設計と詳細設計はソフトウェア設計段階として統合することも、ソフトウェア要件の内容を完全に実現するまで繰り返し反復することもできます。

図5-2 ソフトウェア設計段階の区分とタスク
概要設計
概要設計は全体設計とも呼ばれ、要件分析段階の作業結果から出発し、可能な技術オプションを明確にし、ソフトウェア構造の分割の事前作業を行い、システムを構成する物理要素を分割し、ソフトウェアアーキテクチャ設計、データ設計、およびユーザーインターフェース設計を実施します。
概要設計の主な参加者には、ソフトウェア分析者、ユーザー、ソフトウェアプロジェクト管理者、および関連する技術専門家が含まれます。ソフトウェア分析者は、対象システムの物理的ソリューションと最終的なソフトウェア構造の設計を完了します。ユーザーは、システムの物理的ソリューションと最終的なソフトウェア構造の評価と最終承認に参加します。ソフトウェアプロジェクト管理者は、ソフトウェア分析者が設計したシステムの物理的ソリューションとソフトウェア構造の評価に参加し、ソフトウェア分析者の設計作業を指導します。関連する技術専門家は主に、ソフトウェア分析者が設計したシステムの物理的ソリューションとソフトウェア構造の評価に参加します。
概要設計の主なタスクは、アーキテクチャ設計、データ設計、およびユーザーインターフェース設計を完了することです。
- アーキテクチャ設計。各サブシステムモジュール間のデータ受け渡しと呼び出し関係を決定します。構造化設計の主題分割では、主にクラスとクラス間の関係を決定します。
- データ設計。データ設計には、データベース、データファイル、およびグローバルデータ構造の定義が含まれます。構造化設計では、要件段階の実体関連図、データディクショナリを通じてデータモデルを構築します。オブジェクト指向設計では、クラスの抽象化とインスタンス化、およびクラスの永続ストレージ設計を通じて、データ設計プロセスを完了します。
- ユーザーインターフェース設計。システムと対話するヒューマンマシンインターフェースの設計、およびモジュール間、システムと外部システム間のインターフェース関係を含みます。構造化設計では、データフロー項目に基づいて、モジュールインターフェース、グローバルデータ構造を定義します。オブジェクト指向設計では、関連クラス、インターフェースクラス、境界クラスなどを定義し、ヒューマンマシンインターフェースデータの統一を満たすと同時に、クラス間のデータ受け渡しを完了します。
詳細設計
詳細設計のタスクは、概要設計に基づいて、各部分の詳細を具体的に実装し、システムのすべての内容が十分に詳細なプロセス記述を持つようにし、コーディングのタスクが詳細設計の内容をプログラミング言語に「翻訳」することだけになるようにすることです。正確に言えば、詳細設計のタスクはプロセス設計を完了することです。
プロセス設計には、ソフトウェア各モジュール内部の具体的な実装プロセスとローカルデータ構造の決定が含まれます。構造化設計では、モジュール独立性がデータ構造とアルゴリズムの分離を制約し、両者が設計時に局所性を持つことを必須とし、外部からの両者への影響を低減します。オブジェクト指向設計では、クラスのカプセル化がアルゴリズムとデータ構造の内部性をよく示しています。クラスの継承性は、複数のクラス(クラスファミリ)が共同でプロセス設計を実装するメカニズムを提供します。
ソフトウェア設計の原則
ソフトウェア開発技術の絶え間ない進歩に伴い、いくつかの優れた設計原則が提唱され、ソフトウェア設計プロセスを指導し、ソフトウェア品質を確保しています。
(1) 分割統治(Divide and Conquer):分割統治は、大規模で複雑な問題を解決する際に採用される戦略です。大きな問題を複数の小さな問題に分割し、大きな問題の解決を小さな問題の解決に変換することで、問題の複雑さを大幅に低減します。モジュール化は、ソフトウェア設計において分割統治の考え方を実現する技術的手段です。構造化設計では、モジュールは関数、プロシージャ、さらにはコード断片である場合があります。オブジェクト指向設計では、クラスがモジュールの主要な形態です。
(2) 設計パターンの再利用:再利用とは、同一のものを修正せず、またはわずかに修正して複数回使用できるメカニズムを指します。概要設計はシステムソフトウェア構造を完成させるため、再利用の内容はソフトウェア設計パターンです。ソフトウェア設計パターンは、特定のソフトウェア設計のプロセスとモデルを対象としており、特定のソフトウェア設計を対象とするものではありません。設計パターンを再利用することで、ソフトウェア設計品質が保証されるだけでなく、リソースを設計内の新しいプロセスや新しい方法に集中させ、さらに設計時に新しいプロセスや新しい方法の将来の再利用を考慮することができます。
(3) トレーサビリティ(追跡可能性):ソフトウェア設計のタスクの一つは、ソフトウェア各部分間の関係を決定することです。システム構造を設計するということは、システム各部分、各モジュール間の相互呼び出しまたは制御関係を決定し、モジュールの修正が必要な場合に、修正対象モジュールに関連する他の部分を把握し、問題の根本を正しく追跡できるようにすることを意味します。
(4) 柔軟性:設計の柔軟性とは、主に設計が修正しやすいことを指します。修正には、既存設計に対する追加、削除、変更などの活動が含まれます。修正が発生する主な理由は、ユーザー要件の変更、設計の欠陥、設計の最適化の必要性、設計の再利用です。
ソフトウェア設計の柔軟性は、主にシステムが問題を記述する抽象化によって発揮されます。抽象化は、物事の同じ属性や操作を統一して記述するものであり、広範性を持ちます。したがって、システム設計や設計パターンの抽象度が高いほど、カバー範囲は広くなります。例えば、「鳥」による「雀」の抽象化は、雀が飛べる特性を示すだけでなく、他の鳥類の説明もカバーします。しかし、抽象化は「諸刃の剣」であり、過度の抽象化は理解や設計の困難を引き起こします。例えば、「生物」によって「雀」という実体を抽象化すると、鳥の多くの特徴を「生物」の中で定義することが難しくなります。
(5) 一貫性:一貫性はソフトウェア設計の方法とプロセスの両方に現れます。ソフトウェア設計において、インターフェースビューの一貫性はユーザーエクスペリエンスとシステムへの忠誠度を保証します。例えば、Windows オペレーティングシステムのインターフェースは、複数のバージョン変更を経ても、ユーザーの操作方法は基本的に変わっていません。統一されたルールと制約でモジュールインターフェース定義を標準化し、コーディング段階でのインターフェースとデータ構造の統一操作を確保し、データ理解における曖昧さを減らし、ソフトウェア品質を保証します。
(6) 信頼性:設計は機能設計の各段階にわたるべきであり、基本機能を満たすと同時に、信頼性に影響を与える様々な要素を全面的に考慮する必要があります。設計実装時には、ソフトウェアアーキテクチャ設計が合理的であるべきで、各モジュール間の結合度をできるだけ低くし、システムのフォールトトレランスを高め、単一モジュールの障害が他のモジュールやシステム全体に影響を与えないようにします。また、ソフトウェア開発プロセスでは、開発とテストを同期して進める原則に従い、リアルタイムでテストと問題発見を行い、繰り返し検証することでリスクを低減し、ソフトウェアの信頼性を高める必要があります。
(7) 拡張性:ソフトウェアの設計と実装において、モジュールの役割境界を明確にし、統一された標準入出力インターフェースを使用することで、ソフトウェア関連業務の水平方向の拡張を容易にします。モジュールに含まれるデータ型、サブ機能、操作インターフェースはすべて独立してコンパイルされた動的ライブラリとし、ソフトウェア関連業務の垂直方向の拡張を容易にします。
(8) 保守性:設計実装時には、ソフトウェアの各構成項目は完全なログ記録(プロセスログや異常ログなど)を提供し、同時にソフトウェアエラー時には明確な異常情報の提示が必要です。すべての障害状態と情報は自動的に記録・保存され、事後の障害対策分析を容易にします。
ソフトウェア設計を改善し、ソフトウェア品質を高めるには、以下の原則に従う必要があります。
- モジュールの高い独立性
ソフトウェアの初期構造を設計した後は、さらにモジュールを分解または統合し、結合度を低減し凝集度を高めるよう努めるべきです。例えば、複数のモジュールが共有するサブ機能は、独立したモジュールとして定義し、これらのモジュールから呼び出されるようにすることができます。また、モジュールの分解や統合により、制御情報の受け渡しやグローバルデータへの参照を減らし、インターフェースの複雑さを低減できる場合もあります。
- モジュール規模の適切性
大きなモジュールは往々にして分解が不十分であることが原因ですが、さらなる分解は問題構造に従わなければならず、一般に分解後もモジュール独立性を低下させてはいけません。小さすぎるモジュールは、オーバーヘッドが有効な操作よりも大きく、さらにモジュール数が多すぎるとシステムインターフェースが複雑になります。そのため、小さすぎるモジュールは単独で存在する価値がない場合があり、特にそれを呼び出すモジュールが1つだけの場合は、通常、上位モジュールに統合して単独で存在させないことができます。
- 深さ、幅、ファンアウト、ファンインの適切さ
深さは、ソフトウェア構造内の制御の層数を示し、システムのサイズと複雑さを大まかに示すことができます。図5-3を参照してください。これはプログラムの長さとおおまかな対応関係があるべきですが、もちろんこの対応関係は一定の範囲内で変化します。もし層数が多すぎる場合は、多くの管理モジュールが単純すぎて適切に統合する必要があるかどうかを検討すべきです。幅は、ソフトウェア構造内の同じ階層におけるモジュール総数の最大値です。一般に幅が大きいほどシステムは複雑になります。幅に最も大きな影響を与える要素はモジュールのファンアウトです。

図5-3 ソフトウェア構造関連用語