階層分解
アプリケーションのクラスの作成をすぐに開始しないでください。まず設計する必要があります。設計は思慮深いアーキテクチャで終わる必要があります。そして、このアーキテクチャを実現するには、システムを一貫して分解する必要があります。
分解は階層的に実行する必要があります。まず、システムは、その動作を最も一般的な形式で記述する大きな機能モジュール/サブシステムに分割されます。次に、結果のモジュールがより詳細に分析され、サブモジュールまたはオブジェクトに分割されます。
オブジェクトを選択する前に、少なくとも精神的には、システムを基本的なセマンティック ブロックに分割します。小規模なアプリケーションでは、通常、これは非常に簡単に実行できます。システムは最初にサブシステム/パッケージに分割され、パッケージはクラスに分割されるため、いくつかのレベルの階層で十分です。
![階層分解](https://cdn.codegym.cc/images/article/d5e9aa2b-5783-430f-b5b5-1832bc33c540/800.jpeg)
このアイデアは、思っているほど簡単ではありません。たとえば、Model-View-Controller (MVC) などの一般的な「アーキテクチャ パターン」の本質は何でしょうか?
重要なのは、プレゼンテーションをビジネス ロジックから分離することです。まず、ユーザー アプリケーションは 2 つのモジュールに分割されます。1 つはビジネス ロジック自体 (モデル) の実装を担当し、もう 1 つはユーザーとの対話 (ユーザー インターフェイスまたはビュー) を担当します。
次に、モジュールは何らかの方法で対話する必要があることがわかり、そのためにモジュールの対話を管理するタスクであるコントローラーを追加します。また、MVC のモバイル (クラシック) バージョンでは、ビューがモデルからイベントを受信し、表示されるデータをリアルタイムで変更できるように、オブザーバー パターンが追加されています。
システムを最初に最大のコンポーネントに分割した結果として得られる典型的なトップレベル モジュールは、正確には次のとおりです。
- ビジネスの論理;
- ユーザーインターフェース;
- データベース;
- メッセージング システム。
- オブジェクトコンテナ。
通常、最初の分割ではアプリケーション全体が 2 ~ 7 (最大 10 部分) に分割されます。それをさらに多くの部分に分割すると、それらをグループ化する必要が生じ、再び 2 ~ 7 個のトップレベル モジュールが得られます。
機能分解
モジュール/サブシステムへの分割は、システムが解決するタスクに基づいて行うのが最適です。メインタスクは、その構成要素であるサブタスクに分割され、相互に独立して自律的に解決/実行できます。
各モジュールは、いくつかのサブタスクを解決し、対応する機能を実行する必要があります。機能的な目的に加えて、モジュールは、その機能を実行するために必要なデータのセットによっても特徴付けられます。
モジュール = 関数 +それを実行するために必要なデータ。
モジュールへの分解が正しく行われれば、他のモジュール (他の機能を担当する) との相互作用は最小限になります。存在する可能性がありますが、それが存在しないことはモジュールにとって重要ではありません。
モジュールは任意のコードではなく、特定のタスクに対するソリューションを提供する独立した機能的に意味のある完全なプログラム単位 (サブプログラム) であり、理想的には独立して動作することも、別の環境で動作して再利用することもできます。モジュールは、「行動と発達において相対的に独立できる完全性」の一種である必要があります。(クリストファー・アレクサンダー)
したがって、適切な分解は、まず第一に、システム機能とこれらの機能を実行するために必要なデータの分析に基づいています。この場合の関数はオブジェクトではないため、クラス関数やモジュールではありません。モジュール内にクラスが 2 つしかない場合は、やりすぎです。
強い接続と弱い接続
モジュール化をやりすぎないことが非常に重要です。初心者にモノリシック Spring アプリケーションを与え、それをモジュールに分割するように依頼すると、初心者は各 Spring Bean を別個のモジュールに取り出して、自分の作業が完了したと考えるでしょう。しかし、そうではありません。
分解の品質の主な基準は、モジュールがタスクの解決にどのように集中し、独立しているかです。
これは通常、次のように定式化されます。「分解の結果として得られるモジュールは、内部で最大限に共役し (高い内部凝集性)、相互接続は最小限にする必要があります (低い外部結合)。」
高い凝集性、モジュール内の高い凝集性または「凝集性」は、モジュールが 1 つの狭い問題を解決することに焦点を当てており、異種の機能や無関係な責任の実行には関与していないことを示します。
凝集度は、モジュールによって実行されるタスクが相互に関連している程度を特徴付けます。
高い凝集性の結果は、単一責任原則です。これは 5 つの SOLID 原則のうちの最初のものであり、これによれば、オブジェクト/モジュールは 1 つの責任のみを持つべきであり、それを変更する理由は複数あってはならないとされています。
低結合(疎結合) は、システムが分割されているモジュールが、可能であれば互いに独立しているか、互いに疎結合である必要があることを意味します。彼らは対話できる必要がありますが、同時にお互いのことをできるだけ知らない必要があります。
各モジュールは、他のモジュールがどのように機能するか、どの言語で記述されているか、どのように機能するかを知る必要はありません。多くの場合、このようなモジュールの相互作用を整理するには、これらのモジュールがロードされる特定のコンテナが使用されます。
適切な設計であれば、1 つのモジュールを変更した場合、他のモジュールを編集する必要がなくなるか、変更は最小限で済みます。結合が緩いほど、プログラムの作成/理解/拡張/修復が容易になります。
適切に設計されたモジュールには次の特性があると考えられています。
- 機能の完全性と完全性- 各モジュールは 1 つの機能を実装しますが、それを適切かつ完全に実装し、モジュールはその機能を実装するための一連の操作を独立して実行します。
- 1 つの入力と 1 つの出力- 入力時に、プログラム モジュールは特定の初期データ セットを受け取り、意味のある処理を実行し、1 セットの結果データを返します。つまり、標準の IPO 原則が実装されます - 入力 -\u003e プロセス -\u003e出力。
- 論理的独立性- プログラム モジュールの作業の結果は初期データのみに依存し、他のモジュールの作業には依存しません。
- 他のモジュールとの弱い情報リンク- モジュール間の情報交換は、可能であれば最小限に抑える必要があります。
初心者にとって、モジュールの接続をさらに減らす方法を理解するのは非常に困難です。この知識は、部分的には経験によって得られますが、部分的には賢明な本を読んだ後に得られます。ただし、既存のアプリケーションのアーキテクチャを分析することが最善です。
継承ではなく合成
有能な分解は一種の芸術であり、ほとんどのプログラマーにとって困難な作業です。ここでは単純さは欺瞞であり、間違いは高くつくものです。
![](https://cdn.codegym.cc/images/article/d7e785ed-e16d-4354-883c-0ad83327512a/512.jpeg)
専用モジュールは相互に強く結合しており、独立して開発できない場合があります。あるいは、それぞれがどのような機能を担っているのかが明確ではありません。同様の問題が発生した場合は、モジュールへの分割が正しく行われていない可能性があります。
各モジュールがどのような役割を果たしているかを常に明確にする必要があります。分解が正しく行われるかどうかの最も信頼できる基準は、モジュールがアプリケーションの残りの部分から分離して使用できる (したがって再利用できる) 独立した貴重なサブルーチンであるかどうかです。
システムを分解するときは、「それぞれのモジュールがどのような役割を果たしているのか?」、「モジュールのテストは簡単か?」、「モジュールを単体で使用することは可能か?」などを自問して品質をチェックすることが望ましい。それとも別の環境で?「他人に影響を与える?」
モジュールを可能な限り自律的に保つように努める必要があります。前述したように、これは適切な分解のための重要なパラメータです。したがって、モジュールが最初は相互に弱く依存するような方法で実行する必要があります。成功したなら、あなたは素晴らしいです。
そうでない場合でも、ここですべてが失われるわけではありません。サブシステム間のリンクをさらに最小限に抑え、弱めることができる特別なテクニックやパターンが多数あります。たとえば、MVC の場合、この目的のために Observer パターンが使用されましたが、他のソリューションも可能です。
デカップリングの技術は主要な「建築家のツールキット」を構成していると言えます。すべてのサブシステムについて話していることと、階層のすべてのレベルでの接続、つまりクラス間だけでなく、各階層レベルのモジュール間の接続も弱める必要があることを理解する必要があるだけです。