このチュートリアルでは、Java の例を通じてデザイン パターンの概念を段階的に説明します。
目的: コードの複雑さを軽減し、システムを分離し、可読性を向上させる
意味: クラスの場合、クラスの変更の理由は 1 つだけです。そのクラスの責任は固有であり、この責任が他のクラスの変更の唯一の理由です。
解決策: さまざまな責任をさまざまなクラスまたはモジュールにカプセル化します。既存の責任をより粒度の小さな責任に分割する新しい要件がある場合は、既存のコードをタイムリーにリファクタリングする必要があります。システム ロジックが十分に単純である場合、メソッドが十分に少ない場合、サブクラスが十分に少ない場合、または後続の関連付けが十分に少ない場合は、過度の設計や過剰な粒度を避けるために SRP 原則に厳密に従う必要はありません。
例:電線タイプ 電線は電圧 220v で居住者に電力を供給しますが、新たな需要が増加し、電線は電圧 200kv の高圧電力も輸送します。 Expansion メソッドを追加することで実装できますが、これは単一責任の原則に違反します。基本クラスを提供し、住宅用電源線と高電圧送電線という 2 つの派生クラスを作成できます。
目的: システム継承システムの破壊を防ぐため
意味:すべての場所基本クラスを参照するクラスは、そのサブクラスのオブジェクトを透過的に使用できなければなりません。
解決策: サブクラスは親クラスの抽象メソッドを実装できますが、親クラスの非抽象メソッドをオーバーライドすることはできません。サブクラスが When をオーバーライドまたは実装する場合、サブクラスは独自のメソッドを追加できます。親クラスのメソッドがメソッドである場合、サブクラスのメソッドが親クラスの抽象メソッドを実装する場合、メソッドの前提条件 (つまり、メソッドの仮パラメータ) は親クラスのメソッドの入力パラメータよりも緩くなります。 、メソッドの事後条件 (つまり、メソッドの戻り値) は親クラスよりも厳密です。サブクラスが親クラスのメソッドを完全に実装できない場合、または親クラスの一部のメソッドがサブクラス内で歪められている場合は、継承関係を切断し、継承の代わりに依存関係、集約、組み合わせなどの関係を使用することをお勧めします。
例: 鳥が 2 つの羽で飛ぶためのメソッドが定義されていますが、親クラスのメソッドがオーバーライドされた場合、飛ぶためのメソッドは何ですか。そうしないと、リヒター置換原則に違反し、すべての鳥が飛べなくなります。飛行する鳥と飛べない鳥の 2 つの基本的な鳥クラスを並べて作成する必要があります。たとえば、親クラスは Map を返し、サブクラスは HashMap を返します。親クラスは HashMap 仮パラメータを受け入れ、サブクラスは Map を受け入れます。
目的: 需要の変化による過剰なメンテナンス作業を回避する
意味: 概要モジュールは低レベルのモジュールに依存すべきではなく、両方ともその抽象化に依存すべきであり、抽象化は詳細に依存すべきではなく、詳細は抽象化に依存すべきです。
解決策: インターフェイス指向プログラミングでは、インターフェイスまたは抽象クラスを使用して、特定の操作を行わずに仕様と契約を作成し、詳細を表示するタスクを実装クラスに任せます。
例: マザー クラス Mother には、ストーリーテリング メソッド TellStory があり、Book クラスの入力に依存し、Book クラスの getContent メソッドを使用してストーリーを伝えます。次に、母親に新聞や携帯電話から物語を語ってもらう必要がある場合、元のインターフェイスは無力になります。このとき、getContentメソッドを含むIReader基本クラスが抽象化され、Book、Newspaper、Cellphoneが個別に実装されます。母親の TellStory メソッドは IReader インスタンスを受け入れ、getContent メソッドを呼び出します。
目的: 肥大化したインターフェイスを避ける
意味: クライアントあるクラスの別のクラスへの依存は、最小のインターフェイスに基づく必要はありません。
解決策: インターフェイスを適切に調整し、肥大化したインターフェイスをいくつかの独立したインターフェイスに分割します。
例:言語、数学、物理学、化学、生物学、政治、歴史、地理などをテストする方法を含む試験インターフェイス。学生クラスは試験インターフェイスを実装し、試験を受けます。文系学生クラスと理系学生クラスは、学生クラスから派生したものであり、試験インターフェースを実装する際には、必要のないメソッドを実装する必要があります(文系学生は物理・化学、理系の試験を受けないため)。学生は政治、歴史、地理の試験を受けません)。現時点では、試験インターフェースを改良し、基礎科目試験インターフェース、教養試験インターフェース、理科試験インターフェースに分割する必要があります。学生クラスは基礎科目試験インターフェースを実装し、理系学生は理科試験インターフェースを実装します。検査インターフェイス。
目的: クラス間の結合を減らす
意味: 各ソフトウェア ユニットは他のユニットに関する最小限の知識を持ち、そのユニットと密接に関連するソフトウェア ユニットに限定されます。
解決策: 依存関係、関連性、結合、集約などの結合関係を持たない馴染みのないクラスは、ローカル変数としてクラス内に出現すべきではありません。
例: 校長は教師を管理し、教師は生徒を管理します。校長が全員に電話する必要がある場合は、まず教師に電話する必要があります。ただし、教師を通じて生徒の情報を取得して点呼を行う必要はなく、教師が生徒の点呼を管理できるようにする必要があります。そうしないと、校長と生徒の間に不必要な結合が発生し、生徒のクラスが変更されると、教師のクラスと校長のクラスの両方を変更する必要があります。
目的: クラスシステムの巨大化を防ぐ
意味: 機能を拡張する場合クラスの場合、継承よりも合成/集約を優先します。
解決策: クラス間の関係が「Is-A」の場合は継承を使用し、クラス間の関係が「Has-A」の場合は組み合わせを使用します。
例:たとえば、ブリッジ モードでは、抽象化と実装を独立して変更できます。たとえば、デコレーション モードでは、実装クラスを追加するだけです。クラスを形成して新しい機能を拡張します。グラフィックスの表示要件については、グラフィックスの Shape クラスと表示の Paint クラスを使用して実装します。各 Shape クラスには、グラフィックスの描画と表示を担当する Paint クラス ポインターがあります。 Paint クラスは RedPaint クラスと BluePaint クラスを派生し、Shape クラスに渡すことで異なる色のグラフィックスの描画を実現するため、グラフィックス描画ロジックとグラフィックス描画の実装を独立して変更できます。ある日、需要が増加し、すべての描画に境界線を追加する必要があります。これは、Paint 基本クラスから派生した PaintDecorator クラスを追加し、Paint 描画メソッドを書き換えます。そして、addedPaint メソッドへの呼び出しを追加します。 PaintDecorator クラスから派生した BorderPaintDecorator クラスを追加し、AddedPaint メソッドをオーバーライドし、境界線を描画するコードを追加します。このように、新しいクラスを追加すると、元のすべてのブラシ クラスの機能を拡張できます。
目的: スケーラビリティを向上させ、メンテナンスを容易にする
意味: 拡張のためにオープン、クローズのために修正。つまり、システムの拡張は推奨されますが、既存のシステム コードの変更はサポートされません。言い換えれば、ソフトウェアに新しい要件や変更がある場合、フレームワーク内のコードを変更するのではなく、ソフトウェア フレームワークを拡張して新しい要件に適応するだけで済みます。
解決策: デザイン パターンの最初の 6 つの原則と 23 のデザイン パターンはよく守られており、開閉の原則も当然よく守られています。要件の変化について将来を見据えて予測できるようにすることで、抽象化をより広範囲に適用できるようになり、設計されたソフトウェア アーキテクチャを比較的安定させることができます。ソフトウェア要件の変数の詳細は、抽象化から実装クラスを派生することによって拡張されます。
#include <iostream> //绘制类 class Paint { public: virtual void Draw() = 0; }; //红色绘制类 class RedPaint : public Paint { public: void Draw() { std::cout << "Color Red!" << std::endl; } }; //蓝色绘制类 class BluePaint : public Paint { public: void Draw() { std::cout << "Color Blue!" << std::endl; } }; //图形类,使用桥接模式,将图形绘制逻辑与绘制实现解耦 class Shape { public: Shape(Paint* pt) : m_pPt(pt) { } virtual void Show() { std::cout << "Shape Style:" << std::endl; m_pPt->Draw(); } protected: Paint* m_pPt; }; //长方形类 class Rectangle : public Shape { public: Rectangle(Paint* pt) : Shape(pt) { } }; //圆形类 class Circle : public Shape { public: Circle(Paint* pt) : Shape(pt) { } }; //附加绘制类,使用装饰模式,对原有绘制类进行功能扩展 class PaintDecorator : public Paint { public: PaintDecorator(Paint* pt) : m_pPt(pt) { } void Draw() { m_pPt->Draw(); AddedPaint(); } virtual void AddedPaint() = 0; protected: Paint* m_pPt; }; //附加边框类,对绘制类添加边框绘制功能 class BoarderDecorator : public PaintDecorator { public: BoarderDecorator(Paint* pt) : PaintDecorator(pt) { } void AddedPaint() { std::cout << "With Boarder!" << std::endl; } }; void main() { Shape* pShape = new Circle(new BoarderDecorator(new RedPaint())); pShape->Show(); return; }
関連推奨事項:
JavaScript デザイン パターン ファクトリ パターンと構造 デバイスpattern_javascript スキル
JavaScript 設計の初期調査pattern_javascript スキル
以上がデザインパターンの7つの原則のまとめの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。