この記事の目次
はじめに
前回の記事では、アーキテクチャ根底技法のうち、最初の5つの技法について詳しく解説しました。今回は、残りの5つの技法(充足性、完全性、プリミティブ性、ポリシーと実装の分離、インターフェースと実装の分離、参照の一点性、分割統治)について解説します。これらの技法を理解し、実践することで、より良いソフトウェアアーキテクチャを構築するための強力な基盤を築くことができます。
充足性、完全性、プリミティブ性 (Sufficiency, Completeness, Primitiveness)
表現が十分かつ完璧かつ純粋
カプセル化により、関連のある要素がモジュールに集まりますが、そのモジュールが担っている抽象の表現は「充足」し「完全」で「プリミティブ」であるべきです。
- 充足性:モジュールが表現しようとしている抽象が、それを伝えるために十分であるか。
- 例:モジュールがコレクションを表現している場合、「remove」が提供されていても「add」が提供されていなければ不十分。
- 完全性:モジュールが表現しようとしている抽象が、すべての特徴を備えているか。
- 例:モジュールがコレクションを表現している時、要素数を取得する「size」が提供されていなければ完全ではない。
- プリミティブ性:モジュールが表現しようとしている抽象が、すべて純粋であるかどうか。
- 例:モジュールがコレクションを表現している時、「add」が提供されていれば「add10」は不要。
表現している抽象を正確に伝える
モジュールが表現している抽象は、その意図が使う人に伝わり、使う人に有用でなければなりません。
- 充足性がないと、情報が不足してモジュールの本質を見失いがちになります。
- 完全性がないと、クライアントが安心して利用できず、機能不足に陥る可能性があります。
- プリミティブ性がないと、インターフェースが複雑になり、使用が困難になります。
モジュールの抽象を隙なく表現
モジュールがどのような抽象を表現しているのかを明確にし、情報が多すぎず少なすぎず適切に伝わるように設計します。提供する関数は「充足性」「完全性」「純粋性」を持つラインナップにし、余計なものは削除するか別のモジュールに移動します。
ポリシーと実装の分離 (Separation of Policy and Implementation)
「ポリシー」と「実装」は混ぜない
モジュールは「ポリシー」あるいは「実装」を扱うべきであり、1つのモジュールで両方を扱ってはいけません。
- ポリシーモジュール:ソフトウェアの前提に依存するビジネスロジックや引数の選択を行う。
- 実装モジュール:ソフトウェアの前提に依存しない独立したロジック。
「実装」は安定だが「ポリシー」は不安定
実装モジュールは特定のソフトウェアに依存しないため再利用可能ですが、ポリシーモジュールはソフトウェアに特化しており、変更が発生しやすいです。ポリシーと実装が混ざると、ポリシーの変更に実装が引きずられ、再利用性が損なわれます。
「ポリシー」と「実装」は別モジュール
ポリシーと実装を意識して設計し、それぞれ別のモジュールに分けます。分離が不可能な場合でも、モジュール内でポリシー部分と実装部分が明確に分かるように表現し、コードの保守時には混ざらないように注意します。
インターフェースと実装の分離 (Separation of Interface and Implementation)
構成は「インターフェース」と「実装」から
モジュールは「インターフェース」と「実装」の2つの部分から構成されます。
- インターフェースパート:モジュールが持つ機能を定義し、使用方法を定める部分。
- クライアントによってアクセス可能な関数のシグネチャから構成されます。
- 実装パート:モジュールが持つ機能を実現するコード部分。
- 内部ロジックとデータが含まれ、クライアントからはアクセスできません。
使用者はインターフェースだけ知ればよい
インターフェースと実装が分離されることで、クライアントは実装の詳細を知る必要がなくなり、シンプルでわかりやすくなります。また、実装の修正がインターフェースに影響を与えないため、クライアントへの影響を考えることなく修正が可能です。
インターフェースを用いて設計する
インターフェースを用いてコードを設計し、モジュール同士の呼び出しはインターフェースのみが使用されるようにします。実装はインターフェースの背後に隠し、直接呼び出すことを許可しないようにします。
参照の一点性 (Single Point of Reference)
定義は一度きり
モジュールの要素について、宣言や定義は一回限りにします。例えば、変数の値を初期化したら、その後は変更しないということです。これにより、コードの見通しが良くなります。
副作用のないプログラミング
副作用のないプログラミングが可能になります。
- 副作用:ある機能がモジュールの状態を変化させ、それ以降の結果に影響を与えること。
- 副作用がないと、常に同じ結果が得られ、状況依存による障害の発生が抑えられます。
「単一代入」とする
変数に対して値の再代入を行わない「単一代入」を実践します。プログラミング言語によっては再代入が可能ですが、以下の工夫を行います。
- 極力定数を利用する
- コード規約で再代入を制限する
分割統治 (Divide and Conquer)
大きな問題を小さく割る
大きな問題は解決が難しいため、いくつかの小さな問題に分割して個別に解決します。小さくした問題は元の問題に比べて容易になります。
大きなままでは制御不能
大きな問題のままでは解決が困難で遅くなり、最悪の場合解決できなくなります。制御が容易になるような規模まで問題を分割する方が効率的です。
小さくして各個撃破
問題を分割してから解決します。具体例としては、以下のような方法があります。
- ソフトウェア全体を設計する際、独立して設計できる部分に分割して取り組む。
- モジュールを設計する際、「責任・責務」の観点から分割する。
- アルゴリズムを設計する際、マージソートのようにボトムアップで分割してから解決する。
- 大量データ処理を設計する際、MapReduceのように計算を小さい単位に分割して分散環境で並行して実行する。
まとめ
これらの技法を理解し実践することで、ソフトウェア設計における複雑な問題を効率的に解決し、保守性と拡張性の高いシステムを構築することができます。アーキテクチャ根底技法は、ソフトウェア開発の中で普遍的に適用できる原則であり、これらを活用することで、堅牢なソフトウェアを作り上げることができるでしょう。
カテゴリー: