近年、人々はデザイン パターンを積極的に提唱し、使用しています。その基本的な理由は、コードの再利用性を実現し、コードの保守性を高めるためです。デザイン パターンの実装は、コードの再利用性と保守性の向上という目的を達成するために、いくつかの原則に従っています。デザイン パターンは、オブジェクト指向の 3 つの主要な特徴を理解するのに非常に役立ちます。デザイン パターンを詳しく理解することは困難です。オブジェクト指向開発のメリットを享受できます。学習の初期段階では、これらのモードを統合するのが難しいため、コーディングする前にさらに考え、十分に考えた後にコーディングの練習を開始する必要があります。以下は、デザイン パターンが従うべき 7 つの原則です
1. オープン クローズの原則
定義: クラス、モジュール、関数などのソフトウェア エンティティは、拡張に対してオープンであり、変更に対してクローズされる必要があります。
オープンクローズの原則は、設計するときに、このクラスが十分に優れたものになるように常に考慮し、新しい要件が発生した場合は変更しないようにする必要があることを意味します。 . 元のコードは移動できても移動しません。この原則には 2 つの特徴があります。1 つは「拡張に対してオープン」、もう 1 つは「変化に対してクローズ」です。需要に応じて、プログラムへの変更は、既存のコードを変更するのではなく、新しいコードを追加することによって行われます。これは「オープンクローズ原則」の精神です
例えば、最初はクライアントクラスで追加プログラムを書くだけですぐに完成しましたが、この時点では変更は発生しませんでした。要件は減算関数を追加することでした。この時点で、機能を追加するには元のクラスを変更する必要があり、これはオープンクローズ原則に違反するため、プログラムを再構築し、抽象演算クラスを追加し、特定の加算を分離することを検討する必要があります。減算はクライアントと結合されているため、引き続きニーズを満たし、変更を処理できます。現時点では、乗算と除算の関数を追加する必要がある場合、クライアント クラスと加算と減算のクラスを変更する必要はなく、乗算と除算のサブクラスを追加するだけです。
絶対的な変更のクローズは不可能です。モジュールがどれほど「クローズ」されているとしても、クローズできない変更がいくつか存在します。完全にクローズすることは不可能であるため、設計者は、設計したモジュールのどの変更をクローズするかを決定する必要があります。選択の余地がありません。まず、発生する可能性が最も高い変更の種類を推測し、次にそれらの変更を分離するための抽象化を構築する必要があります。最初にコードを記述するときは、変更は起こらないと想定し、変更が起こった場合には、将来同じ種類の変更を分離するために抽象化を作成します。
私たちが望んでいるのは、開発作業が開始された直後に、起こり得る変更について知ることです。どのような変更が発生したかを知るまでに時間がかかるほど、正しい抽象化を作成することが難しくなります。オープン/クローズの原則はオブジェクト指向設計の中核であり、この原則に従うことで、オブジェクト指向テクノロジによって主張される、保守性、拡張性、再利用性、および柔軟性という大きな利点がもたらされます。開発者は、頻繁に変更されるプログラムの部分のみを抽象化する必要があります。ただし、未熟な抽象化を意図的に拒否することも、抽象化自体と同じくらい重要です。オープンクローズ原則により、以前のコードが変更されていないため、開発者は新しく拡張されたコードに設計を配置することに集中できます。
古典的なことわざとして簡単に言うと、時間は巻き戻せないので、過去は歴史となり変更することはできませんが、今や明日何をするかを自分で決めることができます(つまり、拡張することができます)。
2. リスコフ置換原則
定義 1: T1 型のオブジェクト o1 ごとに、T2 型のオブジェクト o2 が存在する場合、T1 で定義されたすべてのプログラム P がすべてに含まれる場合、オブジェクト o1 が o2 に置き換えられると、プログラム P の動作が変わらない場合、型 T2 は型 T1 のサブタイプになります。
定義 2: サブタイプは親タイプを置き換えることができなければなりません。
説明: ソフトウェア エンティティが親クラスを使用する場合、そのクラスはそのサブクラスに適用可能である必要があり、親クラス オブジェクトとサブクラス オブジェクトの違いを検出できません。つまり、ソフトウェアでは、すべての親クラスがそのクラスに置き換えられます。サブクラスを作成しても、プログラムの動作は変わりません
例: 生物学的な分類では、ペンギンは鳥の一種ですが、プログラミングの世界では、ペンギンは鳥を継承できません。オブジェクト指向設計では、サブクラスは親クラスの非プライベートな動作と属性をすべて持ちます。鳥は飛ぶことができますが、ペンギンは飛ぶことができないため、ペンギンは鳥を継承できません。
サブクラスが親クラスを置き換えることができ、ソフトウェアユニットの機能に影響がない場合にのみ、親クラスを真に再利用でき、サブクラスも親クラスに基づいて新しい動作を追加できます。それはまさにリヒターです。交換原理により継承と再利用が可能になります。親クラス型を使用するモジュールを変更せずに拡張できるのは、まさにサブタイプの置換可能性があるためです。そうでない場合、拡張のためにオープンし、変更のためにクローズすることについてどのように説明できるでしょうか。平たく言えば、リスコフ置換原則は次のとおりです。親クラスの機能を拡張できますが、親クラスの元の機能を変更することはできません。これには次の 4 つのレベルの意味が含まれます:
1. サブクラスは親クラスの抽象メソッドを実装できますが、親クラスの非抽象メソッドをオーバーライドすることはできません。
2. サブクラスは独自のメソッドを追加できます。
3. サブクラスのメソッドが親クラスのメソッドをオーバーライドする場合、メソッドの前提条件 (つまり、メソッドの仮パラメータ) は、親クラスのメソッドの入力パラメータよりも緩くなります。
4. サブクラスのメソッドが親クラスの抽象メソッドを実装する場合、メソッドの事後条件 (メソッドの戻り値) は親クラスの事後条件よりも厳しくなります。
信じられないことのように思えます。なぜなら、私たち自身のプログラミングではリスコフ置換原則に違反することがよくあることがわかりますが、それでもプログラムは正常に実行されます。それで、誰もがこの疑問を抱くでしょう、もし私がリスコフ置換原則に従わないと主張したら、結果はどうなるでしょうか?
その結果、作成したコードで問題が発生する可能性が大幅に高まります。
3. 依存性逆転の原則
定義: 高レベルのモジュールは低レベルのモジュールに依存すべきではなく、両方ともその抽象化に依存すべきであり、抽象化は詳細に依存すべきではありません。つまり、実装のためのプログラムではなく、インターフェイスのためのプログラムです
依存関係の逆転とは、実際には、合意されたインターフェイスを除いて、誰もが依存すべきではないことを意味します。依存関係の逆転は、プログラムを書くときに、詳細、つまりすべての依存関係をプログラミングするのではなく、抽象化を考慮してプログラミングする場合には、オブジェクト指向設計の兆候であると言えます。プログラムの場合は抽象化で終わります。クラスまたはインターフェイスはオブジェクト指向設計であり、それ以外の場合は手続き型設計です。設計のさまざまなコンポーネントやクラスが相互に依存している場合、結合度が高くなり、保守や拡張が困難になります。これはオブジェクト指向の利点を反映しません。
依存関係逆転の原則は、デマンド グループ、開発グループ、テスト グループからなるチームのようなもので、開発グループとテスト グループはすべて同じニーズに直面し、テストの代わりに独自の対応する作業を実行します。開発グループを理解するグループが要件に従ってテスト ケースを作成すること、つまり、開発チームとテスト チームが要件グループに向かって直接作業すること、つまり製品を予定通りにリリースすることを保証することです。そして要件は開発とテストに依存しません。
依存関係逆転の原理は、抽象的なものは詳細の変動性よりもはるかに安定しているという事実に基づいています。抽象化に基づいて構築されたアーキテクチャは、詳細に基づいて構築されたアーキテクチャよりもはるかに安定しています。 Java では、抽象化はインターフェイスまたは抽象クラスを指し、詳細は特定の実装クラスを指します。インターフェイスまたは抽象クラスを使用する目的は、特定の操作を行わずに仕様と契約を作成することであり、詳細を実装クラスに示すタスクは残ります。完了。
依存関係反転の原理の中心となる考え方は、インターフェイス指向のプログラミングです。 依存関係を転送するには、コンストラクター メソッドの転送とセッター メソッドの転送の 2 つの方法があると思います。 Spring フレームワークを使用したことがあります。はい、依存関係の配信方法についてはよくご存知でしょう。
実際のプログラミングでは、通常、次の 3 つの点を行う必要があります:
低レベルのモジュールには、抽象クラスまたはインターフェイス、あるいはその両方が必要です。
変数の宣言型は、可能な限り抽象クラスまたはインターフェースにする必要があります。
継承を使用する場合は、リスコフ置換原則に従ってください。
つまり、依存関係の逆転の原理では、インターフェース向けのプログラミングが必要になります。インターフェース指向のプログラミングを理解すれば、依存関係の逆転も理解できるようになります。
4. インターフェース分離原則
インターフェース分離原則の意味は、単一のインターフェースを確立し、巨大で肥大化したインターフェースを構築せず、可能な限りインターフェースを改良し、インターフェース内のメソッドを最小限にすることです。できるだけ。言い換えれば、呼び出しに依存するすべてのクラスに対して巨大なインターフェイスを構築しようとするのではなく、クラスごとに専用のインターフェイスを確立する必要があります。プログラミングでは、1 つの包括的なインターフェイスに依存するよりも、複数の特殊なインターフェイスに依存する方が柔軟性が高くなります。インターフェースは設計時に外部から設定される「契約」であり、複数のインターフェースを分散定義することで外部からの変更の波及を防ぎ、システムの柔軟性と保守性を向上させることができます。
そういえば、多くの人はインターフェース分離原則が単一責任原則によく似ていると思うでしょうが、そうではありません。まず、単一責任原則はもともと責任に焦点を当てていましたが、インターフェイス分離原則はインターフェイスの依存関係の分離に焦点を当てていました。第二に、単一責任原則は主にクラスを制約し、次にインターフェイスとメソッドを制約し、プログラムの実装と詳細を対象としますが、インターフェイス分離原則は主にインターフェイスを制約し、主に抽象化と全体的なフレームワークの構築を目的とします。プログラム。
インターフェース分離の原則を使用してインターフェースを制限する場合は、次の点に注意してください:
1. インターフェースを可能な限り小さくしますが、制限内に保ちます。インターフェイスを改良するとプログラミングの柔軟性が向上することは事実ですが、小さすぎるとインターフェイスが多くなり、設計が複雑になります。したがって、それは適度に行う必要があります。
2. インターフェイスに依存するクラスのサービスをカスタマイズし、必要なメソッドのみを呼び出しクラスに公開し、不要なメソッドを非表示にします。モジュールにカスタマイズされたサービスを提供することに重点を置くことによってのみ、確立される依存関係を最小限に抑えることができます。
3. 結束力を高め、外部とのやり取りを減らします。ほとんどのことを達成するためにインターフェイスで使用するメソッドを最小限にします。
インターフェイス分離の原則を使用し、インターフェイスの設計が大きすぎても小さすぎてもよくありません。インターフェイスを設計するときは、より多くの時間を考えて計画することによってのみ、この原則を正確に実装することができます。
4. 結合/集約再利用の原則
は、再利用の目的を達成するために、継承関係の代わりに可能な限り合成と集約を使用することを意味します
この原則は、新しいオブジェクトで既存のオブジェクトを使用することです。新しいオブジェクトの一部: 新しいオブジェクトは、これらのオブジェクトに委任することで既存の機能を再利用するという目的を達成します。
実際、ここでの最後のことは、「has-a」と「is-a」の違いを区別することです。合成や集約と比較した場合、継承の欠点は、親クラスのすべてのメソッドが子クラスに公開されることです。親クラスが変更されると、サブクラスも変更する必要があります。集約を再利用すると、他のクラスへの依存が少なくなります。 。
合成/集約の再利用
① 利点:
新しいオブジェクトがコンポーネント オブジェクトにアクセスする唯一の方法は、コンポーネント オブジェクトのインターフェイスを経由することです
コンポーネント オブジェクトの内部の詳細が目に見えないため、この種の再利用はブラック ボックスの再利用です。新しいオブジェクトに;
この種の再利用では必要な依存関係が少なくなります;
各新しいクラスは 1 つのタスクに集中できます;
この種の再利用は実行時に動的に行うことができ、新しいオブジェクトは合成を使用できます/aggregation 関係を使用して、新しい責任を適切なオブジェクトに委任します。
② デメリット:
このように再利用してシステムを構築すると、管理すべきオブジェクトが増えます。
① 利点:
基本クラスのほとんどの関数は継承関係を通じて自動的に派生クラスに入ることができるため、新しい実装が簡単です。
継承された実装の変更または拡張が簡単です。
② 欠点:
継承により基本クラスの実装の詳細が派生クラスに公開されるため、継承の再利用はパッケージ化を破壊します。
基本クラスの実装が変更されると、実装が変更されます。派生クラスの変更も必要です
基本クラスから継承された実装は静的であり、実行時に変更できず、十分な柔軟性がありません。
6. デメテルの法則
一言でまとめると、オブジェクトは他のオブジェクトに関する最小限の知識を保持する必要があります。
7. 単一責任の原則
定義: クラス変更の理由は複数あってはならない。平たく言えば、クラスは 1 つの責任に対してのみ責任を負い、その変更の理由は 1 つだけであるはずです
単一責任の原則に関しては、多くの人がそれを却下するでしょう。シンプルすぎるからです。たとえ少し経験のあるプログラマがデザイン パターンを読んだことも、単一責任の原則について聞いたこともなかったとしても、ソフトウェアを設計する際にはこの重要な原則を意識的に遵守します。これは常識だからです。ソフトウェアプログラミングでは、1 つの関数を変更することによって他の関数が誤動作することを誰も望んでいません。この問題を回避する方法は、単一責任の原則に従うことです。単一責任の原則は非常にシンプルで常識と考えられていますが、経験豊富なプログラマが作成したプログラムであっても、この原則に違反するコードが含まれる可能性があります。なぜこのような現象が起こるのでしょうか?責任が増大するからです。いわゆる責任分散とは、何らかの理由で責任 P がより詳細な責任 P1 と責任 P2 に分割されることを意味します。
単一責任の原則に従う利点は次のとおりです:
1. クラスは 1 つの責任のみを担当し、そのロジックは複数の責任を担当するよりも明らかに単純です。クラスの信頼性を向上させ、システムの保守性を向上させます。
3. 単一責任の原則に従えば、他の機能への影響は避けられません。機能を大幅に削減できます。
説明する必要があることの 1 つは、単一責任の原則はオブジェクト指向プログラミングに固有のものではないということです。モジュール型プログラミングである限り、この重要な原則に従う必要があります。