小さな例を使用して質問に答える方が簡単だと思います:
問題例: ある入力から何らかの計算を行うコンポーネントがあります。顧客に応じて、次のことは変化する傾向があります。
- 入力データのソース/形式
- 入力データの前処理
- 顧客のユースケースに応じて出力を計算する 3 つの異なる方法 [A..C]。
- 計算結果の出力形式。
したがって、ワークフローは次のようになります。
前処理 -> [A..C] フレーバーのいずれかで計算 -> 出力のフォーマット -> 完了。
構成スパゲッティの束なしでこれを処理することを検討する設計は次のようになります。
- 処理ステップごとに動作の種類を分類します。前処理ステップの例では、これは IInputShaper のようなもので、特定の種類の入力データを内部形式に変換し、前処理を行う役割を果たします。おそらく、これは再び分割される可能性があります: IInputFitter、IPreprocess。ワークフローの他のステップについては、それに応じて行います。これにより、多くの動作タイプのコントラクトが残ります (これは、問題のドメインを理解するのにも役立ちます。自由度が実際にどこから来ているのか、システムが持つさまざまな種類の動作の数を簡単に確認できるようになりました。また、コア コード ベースをテストしたり、各動作コントラクトの要件を文書化したり、「in vivo」ではなく単体テスト方式で動作実装をテストしたりできます。
- ステップ 1 で定義されたコントラクトに対して各動作を実装します。過度に設計して動作間の共通点を除外しようとするのではなく、動作の実装全体で重複するコードを受け入れます。複雑にしないでおく。そして、それらのそれぞれについてテストを行います。
結果: 構成オプションが 0 の動作実装のコレクションと、構成オプションが 0 のコア コード ベース。最後のステップは、顧客プロジェクトの動作の実装を選択することです。何か特別なものがある場合は、新しい実装を作成することもできます。
これを正しく行えば、契約や基幹システムを変更することなく、いつまでも幸せに暮らすことができます。完璧な設計を少しでも見逃した場合は、コア コード ベースが安定するように最適なコントラクト インターフェイス設計を見つけるまで、数回の反復が必要になります。
このアプローチを使用すると、作業を見積もったり、新しい顧客への見積もりを書いたりするのもずっと簡単になります。すべての動作の実装が既にある場合、または記述する必要がある新しい動作がある場合は、引用フェーズで簡単に確認できます。そして、そもそもそれらを書くのにどれくらいの時間がかかったのかを追跡すれば、それにかかる工数を推測することさえできます.