<h3 class="blogstory"> ソフトウェアを非常に柔軟で保守しやすいものにするのは非常に困難です。柔軟なソフトウェアは構造が複雑で、保守が困難です。利益と損失があり、損失が損失を上回るように、この二つにどう対処するかが鍵となります。ソフトウェアの設計と開発は、次の 6 つの原則に従う必要があります: <br><span style="color: #993300">1. OCP <br>正式名称: 「オープン-クローズド原則」オープン-クローズド原則 </span><br>説明: 拡張にはオープン、変更にはクローズ。 <br>利点: OCP 原則に従って設計されたシステムは、プログラムのさまざまな部分間の結合を軽減し、適応性、柔軟性、安定性が比較的優れています。既存のソフトウェアシステムに新しい機能を追加する必要がある場合、システムの基盤である抽象化層を変更する必要はなく、追加する必要がある機能を実現するために元の基盤に新しいモジュールを追加するだけで済みます。 。追加された新しいモジュールは元のモジュールにまったく影響を与えないか、ほとんど影響を与えないため、元のモジュールを再テストする必要はありません。 <br>「オープン-クローズド」原則を実装する方法<br> オブジェクト指向設計では、変更できないのはシステムの抽象化層ですが、拡張できるのはシステムの実装層です。言い換えれば、実装層で可能な限り多くの動作を実装できるようにする、一度きりの抽象化設計層を定義します。 <br>問題を解決する鍵は、オブジェクト指向設計の最初の中核となる本質である抽象化にあります。 <br>何かを抽象化するとは、本質的にはその本質を要約することです。抽象化すると、最も重要なことを把握し、より高いレベルから考えることができます。これにより、思考の複雑さが軽減され、同時に多くのことを考える必要がなくなりました。言い換えれば、私たちは物事の本質をカプセル化していて、細部を見ることはできません。 <br> オブジェクト指向プログラミングでは、抽象クラスとインターフェイスを通じて、具体的なクラスの特性が抽象化層として規定され、比較的安定しており、変更する必要がないため、「変更が禁止されている」という要件が満たされます。抽象クラスから派生した具象クラスはシステムの動作を変更できるため、「拡張へのオープン」を満たすことができます。 <br>エンティティを拡張する場合、ソフトウェアのソースコードやバイナリコードを変更する必要はありません。鍵となるのは抽象化です。 <br><br><span style="color: #008000"><strong>2. LSP </strong><br><strong>完全名: "Liskov Substitution Principle" リスコフ置換原理 </strong></span><br> 説明: サブタイプは、その基本タイプを置き換えることができなければなりません。ソフトウェア エンティティが基本クラスを使用している場合、その基本クラスがその基本クラスを継承するサブクラスに置き換えられても、プログラムの動作は変わりません。ソフトウェア エンティティは、基本クラス オブジェクトとサブクラス オブジェクトの違いを認識しません。 <br>利点: クライアントが意識することなく、同じ親クラスの下でサブクラスの交換を実現するのは簡単です。 <br><br><span style="color: #800080">3. DIP <br>正式名称:「依存性逆転原理」 依存性逆転原理 </span><br>説明: 抽象化に依存し、具象に依存しない。クライアントは抽象結合に依存します。 <br>抽象化は詳細に依存すべきではなく、詳細は抽象化に依存すべきです。 <br>プログラミングは実装のためではなくインターフェースのためにあるべきです。 <br> 利点: 従来の手続き型プログラミングによって作成された依存関係を使用すると、戦略は詳細に依存しますが、詳細の変更によって戦略が影響を受けるため、これは問題です。依存関係逆転の原理により、詳細と戦略が抽象化に依存し、抽象化の安定性がシステムの安定性を決定します。 <br>依存関係の逆転を実現するにはどうすればよいですか? <br>抽象的な方法で結合することが、依存関係逆転の原理の鍵となります。抽象結合関係には常に、抽象クラスから継承する具象クラスが含まれており、基本クラスへの参照をそのサブクラスに変更できることを保証する必要があるため、リスコフ置換原則が依存関係逆転原則の基礎となります。 <br>抽象レベルでの結合は柔軟ですが、具体的なクラスが変更される可能性が非常に低い場合、抽象的な結合の利点は非常に限定されます。より良い。 <br>階層: 適切に構造化されたすべてのオブジェクト指向アーキテクチャには明確なレイヤー定義があり、各レイヤーは、明確に定義され制御されたインターフェイスを通じて、一連の一貫したサービスを外部に提供します。 <br>抽象化に依存する: 具象クラスに依存しないことをお勧めします。つまり、プログラム内のすべての依存関係は抽象クラスまたはインターフェイスで終了する必要があります。次のことを実行してみてください: <br>1. 変数は特定のクラスへのポインターまたは参照を保持しないでください。 <br>2. クラスは具象クラスから派生しないでください。 <br>3. どのメソッドも、その基本クラスで実装されたメソッドをオーバーライドしてはなりません。 <br><br><span style="color: #339966">4. ISP <br>完全名: "インターフェイス分離原則" インターフェイス分離原則 </span><br> 説明: 1 つの合計インターフェイスを使用するよりも、複数の専用機能インターフェイスを使用する方が常に優れています。クライアント クラスの観点から見ると、あるクラスの別のクラスに対する依存関係は、最小限のインターフェイスに基づく必要があります。過度に肥大化したインターフェイスはインターフェイスの汚染であり、クライアントが使用しないメソッドに依存することを強制すべきではありません。 <br>利点: ソフトウェアシステムの機能が拡張されるとき、変更の圧力が他のオブジェクトに伝わりません。 <br>インターフェース分離原則の実装方法<br>ユーザーは、使用していないメソッドに依存することを強制されるべきではありません。 <br>1. 委任を使用してインターフェイスを分離します。 <br>2. 多重継承を使用してインターフェースを分離します。 <br><span style="color: #3366ff"><br></span><span style="color: #333399"><strong>5. CARP または CRP <br>完全名: "複合再利用原則" または "複合再利用原則"</strong><strong></strong> 複合再利用原則</span><strong> 説明: 新しいオブジェクトの一部の機能には、作成された他のオブジェクトにすでに実装されているため、自分で再作成するのではなく、他のオブジェクトによって提供される関数を使用して、それらを新しいオブジェクトの一部にするようにしてください。新しいオブジェクトは、これらのオブジェクトに委任することで、既存の機能の再利用を実現します。 </strong> つまり、合成/集計を使用し、継承は使用しないようにしてください。 <br>利点: <br>1) 新しいオブジェクトがコンポーネント オブジェクトにアクセスする唯一の方法は、コンポーネント オブジェクトのインターフェイスを経由することです。 <br>2) コンポーネント オブジェクトの内部詳細は新しいオブジェクトには見えないため、この種の再利用はブラック ボックスの再利用です。 <br>3) この種の再利用はパッケージ化をサポートします。 <br>4) この種の再利用では必要な依存関係が少なくなります。 <br>5) 新しいクラスはそれぞれ、タスクに集中できます。 <br>6) この種の再利用は実行時に動的に実行でき、新しいオブジェクトはコンポーネント オブジェクトと同じタイプのオブジェクトを動的に参照できます。 <br>7) 再利用方法として、ほぼすべての環境に適用できます。 <br>欠点: <br>つまり、システム内に管理する必要があるオブジェクトが増えることになります。 <br><br><br><br>6. LOD または LKP <span style="color: #ff00ff">正式名称:「デメテルの法則」デメテル原則または「最小知識原則」最小知識原則<strong> <br></strong>説明:オブジェクトは、できるだけ少ない方法で相互に関連している必要があります。関係。 </span>ディミットの法則の実装方法<br> ディミットの法則の主な目的は、情報の過負荷を制御することです。システム設計に適用する場合は、次の点に注意する必要があります。 <br>1) クラス分割の観点からは、弱い結合が必要です。作成された種類。クラス間の結合が弱いほど、クラスを再利用しやすくなります。 <br>2) クラス構造設計の観点から、各クラスはメンバーのアクセス権を最小限に抑える必要があります。クラスはそのプロパティを公開すべきではありませんが、外部の世界がそのプロパティに間接的にアクセスできるように、値を取得して割り当てるためのメソッドを提供する必要があります。 <br>3) クラス設計では、可能な限り、クラスは不変クラスになるように設計する必要があります。 <br>4) 他のオブジェクトへの参照に関しては、クラスの他のオブジェクトへの参照は最小限に抑える必要があります。 <br><br><br><br><strong>単一責任の原則もあります: <span style="color: #000080"><br> SRP の紹介 (SRP--単一責任の原則): クラスに関する限り、クラスは 1 つのことを行うことにのみ焦点を当て、その理由は 1 つだけを持つ必要があります。その変化<br> <br>。いわゆる責任は機能として理解できます。これは、設計された機能には 2 つ以上の機能ではなく 1 つだけの機能が必要であることを意味します。このクラスを変更する必要がある変更が 2 つあることがわかった場合は、このクラスを取り消すことを検討する必要があります。責任は変化の軸であるため、要件が変化すると、その変化はクラスの責任の変化を反映します。 SRP を使用する際の注意点: 1. 合理的なクラスには、変更の理由が 1 つだけある必要があります。つまり、変更の兆候がない場合に SRP またはその他の原則を適用するのは賢明ではありません。実際に変更する場合は、コードをリファクタリングするために SRP などの原則を適用する必要があります。 </span>4. テスト駆動の開発を使用すると、設計が悪化する前に、不合理なコードを分離する必要があります。脆弱性の匂いが非常に強くなるため、コードをリファクタリングするにはファサード モードまたはプロキシ モードを使用する必要があります。SRP の利点: 結合を排除し、要件の変更によって生じるコードの硬直性を軽減します。 </strong><br><br><br>これらの原則に厳密に従う必要はなく、違反しても宗教上の罰則はありません。しかし、これらの原則は、いずれかが違反された場合に警鐘が鳴ると考えるべきです。 <br><br>-----Arthur J.Riel <br>(1) すべてのデータは、それが配置されているクラス内に隠される必要があります。 <br>(2) クラスのユーザーはクラスの共有インターフェースに依存する必要がありますが、クラスはそのユーザーに依存することはできません。 p15 <strong>(3) クラスプロトコル内のメッセージを最小限に抑える。 </strong>(4) すべてのクラスが理解できる最も基本的なパブリック インターフェイス [たとえば、コピー操作 (深いコピーと浅いコピー)、等価性の判断、正しい出力内容、ASCII 記述からの解析など] を実装します。 p16 <br>(5) 実装の詳細(共有コードを配置するプライベート関数など)をクラスのパブリックインターフェイスに入れないでください。 <br>クラスの 2 つのメソッドに共通のコードがある場合、これらの共通コードを防ぐプライベート関数を作成できます。 <br>(6) ユーザーが使用できないものや興味のないものでクラスのパブリック インターフェイスを妨害しないでください。 p17 <br>(7) クラス間の結合はゼロであるか、派生結合関係のみが存在する必要があります。つまり、あるクラスは別のクラスとまったく関係がないか、別のクラスのパブリック インターフェイスでの操作のみを使用するかのどちらかです。 p18 <br>(8) クラスは 1 つの主要な抽象概念のみを表す必要があります。 <br>同じタイプのプロパティの変更に対しては、パッケージ内のすべてのクラスを共同で閉じる必要があります。変更がパッケージに影響を与える場合、そのパッケージ内のすべてのクラスに影響しますが、他のパッケージには影響しません。 <br>(9) 関連するデータと動作を一元化します。 <br>デザイナーは、get などの操作を通じて他のオブジェクトからデータを取得するオブジェクトに注意を払う必要があります。このタイプの動作は、この経験原則に違反していることを意味します。 <br>(10) 関係のない情報を別のクラスに入れる(つまり、お互いにコミュニケーションをとらない行為)。 p19 <br>安定した依存関係に向けて進む <br>(11) モデル化する抽象概念が、オブジェクトによって果たされる役割だけではなく、クラスであることを確認してください。 p23 <br>(12) システム機能を水平方向にできるだけ均一に分散します。つまり、設計に従って、最上位クラスは作業を均一に共有する必要があります。 <br>(13) システム内に全能のクラス/オブジェクトを作成しないでください。 Driver、Manager、System、および Susystem を名前に含むクラスには特に注意してください。 <br>インターフェースを実装するのではなく、インターフェースを計画します。 <br>(14) パブリックインターフェースで多数のアクセスメソッドを定義するクラスには注意してください。アクセス方法が多数あるということは、関連するデータと動作が集中的に保存されていないことを意味します。 <br>(15) 相互に通信しない動作が多すぎるクラスには注意してください。 <br>この問題のもう 1 つの兆候は、アプリケーション内のクラスのパブリック インターフェイスに多数の get 関数と set 関数を作成することです。 <br>(16) ユーザーインターフェースと対話するオブジェクト指向モデルで構成されるアプリケーションでは、モデルはインターフェースに依存すべきではありませんが、インターフェースはモデルに依存する必要があります。 <br>(17) 可能な限り現実世界に従ってモデル化します (システム機能分散の原則を遵守し、汎用クラスの原則を回避し、関連するデータと動作を一元的に配置するために、この原則に違反することがよくあります)。 <br>(18) デザインから不要なクラスを削除します。 <br>一般的には、このクラスをプロパティにダウングレードします。 <br>(19) システム外のクラスを削除します。 <br> システム外部のクラスの特徴は、抽象的に言えば、システム ドメインにメッセージを送信するだけで、システム ドメイン内の他のクラスからのメッセージは受け付けないことです。 <br>(20) オペレーションをクラスに変えないでください。名前が動詞であるか動詞から派生したクラス、特に意味のあるアクションが 1 つだけあるクラスには質問してください。その意味のある動作を、既存のクラスまたはまだ発見されていないクラスに移動する必要があるかどうかを検討してください。 <br>(21) アプリケーションの分析モデルを作成するときに、プロキシ クラスを導入することがよくあります。設計段階では、多くのエージェントが役に立たず、削除する必要があることがわかります。 <br>(22) クラスの協力者の数を最小限に抑えます。 <br>クラスで使用される他のクラスの数はできるだけ少なくする必要があります。 <br>(23) クラスとコラボレーター間で受け渡されるメッセージの数を最小限に抑えます。 <br>(24) クラスとコラボレーター間のコラボレーションの量を最小限に抑えます。つまり、クラスとコラボレーターの間で受け渡されるさまざまなメッセージの数を減らします。 <br>(25) クラスのファンアウトを最小限に抑えます。つまり、クラスによって定義されたメッセージの数と送信されるメッセージの数の積を減らします。<br>(26) クラスに別のクラスのオブジェクトが含まれる場合、含まれるクラスは、含まれるオブジェクト情報にメッセージを送信する必要があります。つまり、包含関係は常に使用関係を意味します。 <br>(27) クラスで定義されたほとんどのメソッドは、ほとんどの場合、ほとんどのデータ メンバーを使用する必要があります。 <br><br><br>(28) クラスに含まれるオブジェクトの数は、開発者の短期記憶の容量を超えてはなりません。この数は多くの場合 6 です。 <br>クラスに 6 つを超えるデータ メンバーが含まれる場合、論理的に関連するデータ メンバーを 1 つのグループに分割し、新しい包含クラスを使用してこのメンバーのグループを含めることができます。 <br>(29) システム機能を狭く深い継承体系で垂直に分散させます。 <br>(30) セマンティック制約を実装するときは、クラス定義に基づいて実装するのが最善です。これは多くの場合、クラスのオーバーフローにつながります。この場合、制約はクラスの動作に実装する必要があります。通常はコンストラクターに実装する必要がありますが、必ずしもそうである必要はありません。 <br>(31) クラスのコンストラクターにセマンティック制約を実装する場合、コンストラクター ドメインで許可される最も深い包含レベルに制約テストを配置します。 <br>(32) 制約が依存するセマンティック情報が頻繁に変更される場合は、それを一元化されたサードパーティ オブジェクトに配置するのが最善です。 <br>(33) 制約が依存するセマンティック情報がめったに変更されない場合は、次のことが最善です。関連する各カテゴリの制約に分散します。 <br>(34) クラスはそれに何が含まれているかを知る必要がありますが、誰がそれを含んでいるかを知ることはできません。 <br>(35) リテラル スコープを共有する (つまり、同じクラスに含まれる) オブジェクトは、相互に使用関係を持つべきではありません。 <br>(36) 継承は、特殊化階層をモデル化するためにのみ使用されるべきです。 <br>(37) 派生クラスは基底クラスを知っている必要があり、基底クラスは派生クラスに関する情報を知っていてはなりません。 <br>(38) 基本クラス内のすべてのデータはプライベートである必要があり、保護されたデータを使用しないでください。 <br><br>クラスの設計者は、クラスのユーザーが必要としないものをパブリックインターフェイスに決して配置すべきではありません。 <br>(39) 理論的には、継承階層は深くあるべきであり、深ければ深いほど良いです。 <br>(40) 実際には、継承階層の深さは平均的な人の短期記憶容量を超えてはなりません。広く受け入れられている深さの値は 6 です <br>(41) すべての抽象クラスは基底クラスである必要があります <br>(42) すべての基底クラスは抽象クラスである必要があります <br>(43) データ、動作、インターフェースの共通点を継承階層のできるだけ上位に配置します。 <br>(44) 2 つ以上のクラスが共通のデータを共有する (ただし、共通の動作がない) 場合、共通のデータは 1 つのクラスに配置され、このデータを共有する各クラスにこのクラスが含まれる必要があります。 <br>(45) 2 つ以上のクラスが共通のデータと動作 (つまり、メソッド) を持つ場合、これらの各クラスは、これらのデータとメソッドを表す共通の基本クラスを継承する必要があります。 (46) 2 つ以上のクラスが共通のインターフェイス (メソッドではなくメッセージを参照) を共有する場合、多態的に使用する必要がある場合にのみ、共通の基本クラスから継承する必要があります。 (47) オブジェクトタイプの表示に関するケースバイケースの分析は、一般的に間違っています。このような場合、ほとんどの場合、設計者はポリモーフィズムを使用する必要があります。 <br>(48) 属性値の表示の状況分析は間違っていることがよくあります。クラスは継承階層に分離され、各属性値が派生クラスに変換される必要があります。 <br><br><br>(49) 継承関係を通じてクラスの動的セマンティクスをモデル化しないでください。静的セマンティクス関係を使用して動的セマンティクスをモデル化しようとすると、実行時に型が切り替わります。 <br>(50)クラスオブジェクトを派生クラスに変えないでください。インスタンスが 1 つしかない派生クラスには注意してください。 <br>(51) 実行時に新しいクラスを作成する必要があると考えられる場合は、一歩下がって、オブジェクトを作成していることを認識してください。次に、これらのオブジェクトをクラスに一般化します。 <br>(52) 派生クラスで空のメソッド (つまり、何も実行しないメソッド) を使用して、基本クラスのメソッドをオーバーライドすることは違法である必要があります。 <br>(53) オプションの組み込みと継承の必要性を混同しないでください。オプションの包含を継承としてモデル化すると、クラスの急増につながります。 <br>(54) 継承階層を作成するときは、再利用可能なコンポーネントではなく、再利用可能なフレームワークを作成するようにしてください。 <br>(55) 設計で多重継承を使用する場合は、間違いがあったと想定してください。間違いを犯していない場合は、それを証明する必要があります。 <br>(56) オブジェクト指向設計で継承が使用されている限り、次の 2 つの質問を自問してください: (1) 派生クラスは、それが継承するものの特別な型ですか? (2) 基本クラスは派生クラスの一部ですか? <br>(57) オブジェクト指向設計で多重継承が見つかった場合は、基本クラスが実際に別の基本クラスの派生クラスになっていないことを確認してください。 <br>(58) オブジェクト指向設計において、包含と関連付けのどちらかを選択する必要がある場合は、包含を選択してください。 <br>(59) クラスのオブジェクトの簿記にグローバル データやグローバル関数を使用しないでください。クラス変数またはクラスメソッドを使用する必要があります。 <br>(60) オブジェクト指向の設計者は、物理的な設計原則が論理的な設計を損なうことを許すべきではありません。ただし、論理設計に関する決定を行う際には、物理設計基準を使用することがよくあります。 <br>(61) オブジェクトの状態を変更するためにパブリック インターフェイスをバイパスしないでください。 </h3>