[デザインパターンとパラダイム:概要] 74 | 23 の古典的なデザインパターンの原理、背後にある考え方、適用シナリオなどを要約およびレビューします。

今日の時点で、23 の古典的なデザイン パターンすべてが説明されています。コラム全体も3/4を終え、いよいよ実戦段階に入ります。新しいモジュールの学習に入る前に、いつものように概要を復習してもらいます。23 の古典的なデザイン パターンは、創造的、構造的、行動的の 3 つのタイプに分類されます。今日は、これら 3 つのタイプを対応する 3 つの小さなモジュールに分割し、各設計パターンの原理、実装、設計意図、およびアプリケーション シナリオを 1 つずつ確認していきます。

前回のまとめ記事と同じく、今日の内容も1万語近くとかなりの量ですが、以前に習った内容なので、あまり力を入れなくても良さそうですが、しっかりと内容をマスターできているかどうかが試されます。

同じ文章でも、読んだ後に感動できれば、よく学べたということですし、頭の中で自分なりの知識構造を形成でき、目を閉じたときに思い出せるようになれば、学べたということになります。そうですね、自分なりの理解を持ち、プロジェクト開発においてコード品質の問題について考え始め、コードの問題を解決するために学んだデザインパターンを使い始めることができれば、これらの内容の本質をマスターしたことになります。

ここに画像の説明を挿入
早速、今日のレビューを正式に始めましょう!

1.創造的なデザインパターン

創造的なデザイン パターンには、シングルトン パターン、ファクトリー パターン、ビルダー パターン、プロトタイプ パターンが含まれます。これは主にオブジェクト作成の問題を解決し、複雑な作成プロセスをカプセル化し、オブジェクトの作成コードと使用コードを分離します。

1. シングルトンモード

シングルトン パターンは、グローバルに一意のオブジェクトを作成するために使用されます。クラスは 1 つのオブジェクト (またはインスタンス) のみを作成できます。その場合、このクラスはシングルトン クラスとなり、このデザイン パターンはシングルトン パターンと呼ばれます。シングルトンの古典的な実装方法はいくつかあります。それらは、ハングリーマンスタイル、レイジーマンスタイル、二重検出、静的内部クラス、列挙です。
シングルトンは非常に一般的なデザインパターンですが、実際の開発ではよく使用しますが、シングルトンはアンチパターン(アンチパターン)であると考えている人もおり、使用は推奨されません。主な理由は次のとおりです。以下に続きます:

  • シングルトンは OOP 機能のサポートに不向きです
  • シングルトンはクラス間の依存関係を隠します
  • シングルトンはコードのスケーラビリティに優しくない
  • シングルトンはコードのテスト容易性に不向きです
  • シングルトンはパラメーター化されたコンストラクターをサポートしていません

では、シングルトン ソリューションに代わるものは何でしょうか? これらの問題を完全に解決したい場合は、グローバルに一意なクラスをルートから実装する他の方法を見つける必要があるかもしれません。たとえば、グローバルな一意性は、ファクトリ パターンと IOC コンテナを通じて保証されます。

シングルトンをアンチパターンとみなし、プロジェクトで使用すべきではないと主張する人もいます。個人的には、これは少し極端だと思います。正しいパターンや間違ったパターンはありません。キーは使い方によって異なります。シングルトン クラスにその後の拡張の必要がなく、外部システムに依存しない場合は、シングルトン クラスとして設計しても大きな問題はありません。一部のグローバル クラスは、別の場所で新規作成するとクラス間で受け渡しが必要になるため、シングルトン クラスを直接作成した方が簡単で使いやすいです。

また、プロセスの唯一のケース、スレッドの唯一のケース、クラスタの唯一のケース、複数のケースなどの拡張知識ポイントについても説明しましたが、この部分は実際の開発では使用されません。しかし、それはあなたのアイデアを拡張し、論理的思考を訓練することができます。私はあなたをここに連れ戻しません、あなた自身で思い出してください。

2. 工場出荷時のパターン

ファクトリ パターンには、単純ファクトリ、ファクトリ メソッド、抽象ファクトリの 3 つの細分パターンが含まれます。その中でも、単純なファクトリとファクトリ メソッドがより一般的に使用され、抽象ファクトリのアプリケーション シナリオは比較的特殊であるため、めったに使用されず、私たちの研究の焦点では​​ありません。

ファクトリ パターンは、異なるが関連するタイプのオブジェクト (同じ親クラスまたはインターフェイスを継承するサブクラスのグループ) を作成するために使用され、指定されたパラメータによって作成するオブジェクトのタイプが決定されます。実際、オブジェクト作成のロジックが複雑でない場合は、ファクトリー パターンを使用せずに、 new を通じて直接オブジェクトを作成できます。作成ロジックがより複雑で「大きなプロジェクト」である場合は、ファクトリ パターンを使用してオブジェクト作成プロセスをカプセル化し、オブジェクトの作成と使用を分離することを検討します。

各オブジェクトの作成ロジックが比較的単純な場合は、単純なファクトリ パターンを使用して、複数のオブジェクトの作成ロジックをファクトリ クラスに入れることをお勧めします。各オブジェクトの作成ロジックが比較的複雑な場合、大きすぎるファクトリ クラスの設計を避けるために、ファクトリ メソッド パターンを使用して作成ロジックをより詳細に分割し、各オブジェクトの作成ロジックが独自のものから独立していることをお勧めします。授業中の工場。
具体的には、ファクトリーモデルには次の 4 つの機能があり、ファクトリーモデルを使用するかどうかを判断する最も重要な基準でもあります。

  • カプセル化の変更: 作成ロジックが変更される可能性があります。ファクトリ クラスにカプセル化された後、作成ロジックの変更は呼び出し元に対して透過的です。
  • コードの再利用: 独立したファクトリ クラスに抽出された後に再利用できるコードを作成します。
  • 複雑さを分離する: 複雑な作成ロジックをカプセル化すると、呼び出し元はオブジェクトの作成方法を理解する必要がありません。
  • 複雑さを制御する: 元の関数またはクラスの責任が 1 つになり、コードがより簡潔になるように、作成コードを抽出します。

さらに、ファクトリ パターンの非常に古典的なアプリケーション シナリオについても説明しました。依存関係注入フレームワーク (Spring IOC、Google Guice など) は、オブジェクトを一元的に作成、アセンブル、管理するために使用され、特定のビジネス コードから切り離され、プログラマー ビジネスコードの開発に注力します。DI フレームワークは日々の開発に必要なフレームワークとなっていますが、コラムでは簡単な DI フレームワークの実装も紹介しましたので、遡ってご覧ください。

3. ビルダーモード

ビルダー モードは、複雑なオブジェクトを作成するために使用されます。これは、さまざまなオプションのパラメーターを設定することでさまざまなオブジェクトを作成するように「カスタマイズ」できます。ビルダー モードの原理と実装は比較的単純で、アプリケーション シナリオを習得し、過度の使用を避けることに重点が置かれています。

クラス内に多くの属性がある場合、コンストラクターのパラメータリストが長すぎてコードの読みやすさと使いやすさに影響を与えることを避けるために、コンストラクターを set() メソッドと連携して使用して問題を解決できます。 。ただし、次のいずれかの状況が存在する場合は、ビルダー パターンの使用を検討する必要があります。

  • クラスの必要なプロパティをコンストラクターに配置し、オブジェクトの作成時にそれらのプロパティが強制的に設定されるようにします。必須属性が多数ある場合、これらの必須属性がコンストラクターで設定されている場合、コンストラクターには長いパラメーター リストが含まれます。set() メソッドを通じて必須属性を設定すると、これらの必須属性が入力されたかどうかを検証するロジックを配置する場所がなくなります。

  • クラスの属性間に特定の依存関係または制約がある場合、set() メソッドの設計思想でコンストラクターを使用し続けると、これらの依存関係または制約の検証ロジックを配置する場所がなくなります。

  • 不変オブジェクトを作成したい場合、つまりオブジェクトの作成後は内部属性値を変更できなくなりますが、この機能を実現するにはクラス内で set() メソッドを公開することはできません。コンストラクターが set() メソッドと連携してプロパティ値を設定する方法は適用されません。

4. 試作パターン

オブジェクトの作成コストが比較的高く、同じクラスの異なるオブジェクト間の違いがほとんどない場合 (ほとんどのフィールドが同じ)、この場合、既存のオブジェクト (プロトタイプ) のコピー (またはコピー) を使用できます。 ) ) 作成時間を節約するという目的を達成するために新しいオブジェクトを作成する方法。プロトタイプに基づいてオブジェクトを作成するこの方法は、プロトタイプ モードと呼ばれます。

プロトタイプ モードを実装するには、ディープ コピーとシャロー コピーという 2 つの方法があります。浅いコピーでは、オブジェクト内の基本データ型データと参照先オブジェクトのメモリ アドレスのみがコピーされ、参照先オブジェクトと参照先オブジェクトの参照先オブジェクトは再帰的にコピーされません。一方、深いコピーでは完全に独立したオブジェクトが取得されます。 。したがって、深いコピーは浅いコピーよりも時間とメモリを消費します。

コピー対象のオブジェクトが不変オブジェクトの場合は、不変オブジェクトを浅いコピーで共有しても問題ありませんが、可変オブジェクトの場合は、浅いコピーで取得したオブジェクトと元のオブジェクトで一部のデータを共有することになり、データが改ざんされる可能性があります。リスクはさらに複雑です。操作に非常に時間がかかる場合を除き、浅いコピーを使用することをお勧めします。そうでない場合は、わずかなパフォーマンス向上のために浅いコピーを使用しない理由はありません。

2. 構造設計パターン

構造パターンは主に、いくつかのクラスまたはオブジェクトが組み合わされた古典的な構造を要約しており、これらの古典的な構造は特定のアプリケーション シナリオでの問題を解決できます。構造パターンには、プロキシ パターン、ブリッジ パターン、デコレータ パターン、アダプタ パターン、ファサード パターン、組み合わせパターン、フライウェイト パターンが含まれます。

1.プロキシモード

プロキシモードは、元のクラスのインターフェースを変更せずに、元のクラスのプロキシクラスを定義するモードであり、機能拡張ではなくアクセス制御が主な目的であり、これがデコレータモードとの最大の違いです。一般に、プロキシ クラスと元のクラスに同じインターフェイスを実装させます。ただし、元のクラスがインターフェイスを定義していない場合、および元のクラス コードが当社によって開発および保守されていない場合は、この場合、プロキシ クラスに元のクラスのメソッドを継承させることで、プロキシ パターンを実装できます。

静的プロキシでは、クラスごとにプロキシ クラスを作成する必要があり、各プロキシ クラスのコードはテンプレート スタイルの「繰り返し」コードに似ており、メンテナンスと開発のコストが増加します。静的プロキシの問題については、動的プロキシを使用することで解決できます。元のクラスごとに事前にプロキシ クラスを記述するのではなく、実行時に元のクラスに対応するプロキシ クラスを動的に作成し、システム内の元のクラスをプロキシ クラスに置き換えます。

プロキシ モードは、監視、統計、認証、電流制限、トランザクション、冪等性、ログなど、ビジネス システムの非機能要件を開発するためによく使用されます。これらの追加機能をビジネス機能から分離し、統合処理のためにエージェント クラスに配置することで、プログラマーはビジネス開発のみに集中できます。さらに、プロキシ モードは、RPC やキャッシュなどのアプリケーション シナリオでも使用できます。

2. ブリッジモード

ブリッジ モードのコード実装は非常にシンプルですが、理解するのが少し難しく、適用シナリオが比較的限定されているため、比較的実際のプロジェクトではブリッジ モードはあまり一般的に使用されません。簡単に説明したものであり、見れば理解できますが、それは問題ありません。それは私たちの研究の焦点では​​ありません。

ブリッジ モードを理解するには 2 つの方法があります。1 つ目の理解方法は、「抽象化と実装を切り離し、独立して開発できるようにする」です。この理解方法は非常に特殊であり、応用シナリオはそれほど多くありません。もう 1 つの理解方法はより単純で、「継承よりも合成の方が優れている」という設計原則に相当し、この理解方法はより一般的であり、より多くの適用シナリオがあります。どのような捉え方をしてもコード構造は同じであり、クラス間の組み合わせ関係となります。

1つ目の理解方法は、定義にある「抽象」と「実装」という2つの概念を理解することが理解の鍵となります。定義における「抽象」は「抽象クラス」や「インターフェース」を指すのではなく、抽象化された「クラス ライブラリ」のセットを指します。これにはスケルトン コードのみが含まれており、実際のビジネス ロジックは「実装」に委任する必要があります。 " 定義の "行われること。定義における「実装」は「インターフェース実装クラス」ではなく、独立した「クラスライブラリ」の集合です。「抽象」と「実装」は独立して開発され、オブジェクト間の構成関係によって組み立てられます。

3. デコレータパターン

デコレータパターンは主に、継承関係が複雑すぎる問題を解決し、継承を組み合わせで置き換えたり、元のクラスに拡張機能を追加したりするパターンです。これは、デコレーター パターンを使用するかどうかを判断するための重要な基準でもあります。さらに、デコレータ パターンには、元のクラスに複数のデコレータをネストできるというもう 1 つの特徴があります。このような要件を満たすために、デコレータ クラスは設計時に元のクラスと同じ抽象クラスまたはインターフェイスを継承する必要があります。

4.アダプターモード

プロキシ モードとデコレータ モードは元のクラスと同じインターフェイスを提供しますが、アダプタは元のクラスとは異なるインターフェイスを提供します。アダプター モードは、互換性のないインターフェイスを互換性のあるインターフェイスに変換する適応に使用され、互換性のないインターフェイスが原因で連携できなかったクラスが連携できるようになります。アダプター パターンには、クラス アダプターとオブジェクト アダプターの 2 つの実装があります。このうち、クラスアダプタは継承関係を利用して実装され、オブジェクトアダプタは合成関係を利用して実装されます。

アダプター パターンは、設計上の欠陥を修正するために使用される後付けの修正戦略です。このモデルを適用することは「無力な行動」であると考えられます。設計の初期段階でインターフェースの非互換性の問題を回避できれば、このモードは役に立ちません。実際の開発では、どのような状況でインターフェースの不互換が発生するのでしょうか?以下の5つのシナリオをまとめました。

  • 欠陥のあるインターフェイス設計をカプセル化する
  • 複数のクラスのインターフェース設計を統一する
  • 依存する外部システムを置き換える
  • 旧バージョンのインターフェースと互換性あり
  • さまざまな形式のデータに適応する

5. ファサードパターン

ファサード モードの原理と実装は非常にシンプルで、アプリケーション シナリオは比較的明確です。これは、きめの細かいインターフェイスをカプセル化し、各きめの細かいインターフェイスを組み合わせた高レベルのインターフェイスを提供して、インターフェイスの使いやすさを向上させたり、パフォーマンスや分散トランザクションなどの問題を解決したりします。

6. コンビネーションモード

この合成モードは、先ほどお話ししたオブジェクト指向設計における「合成関係(合成により2つのクラスを組み立てる)」とは全く異なります。ここで言う「結合モード」は主に木構造のデータを処理する場合に使用されます。アプリケーション シナリオの特殊性により、データはツリー構造で表現する必要があるため、このモードは実際のプロジェクト開発ではあまり使用されません。ただし、データがツリー構造を満たすと、このパターンの適用が大きな役割を果たし、コードが非常に簡潔になります。

組み合わせモードの設計アイデアは、設計モードというよりも、ビジネス シナリオ向けのデータ構造とアルゴリズムを抽象化したものです。このうち、データはツリーなどのデータ構造として表現でき、ツリー上の再帰的走査アルゴリズムを通じてビジネス要件を実現できます。結合モードでは、オブジェクトのグループをツリー構造に編成し、個々のオブジェクトと複合オブジェクトの両方をツリー内のノードとして扱い、処理ロジックを統合します。また、ツリー構造の特性を使用して各サブツリーを再帰的に処理し、コードを簡素化します。 。

7. フライウェイトモード

いわゆる「フライング人民元」は、名前が示すように、共有ユニットです。フライウェイト パターンの目的は、フライウェイト オブジェクトが不変オブジェクトである場合に、オブジェクトを再利用してメモリを節約することです。

具体的には、システム内に重複オブジェクトが多数ある場合、フライウェイト パターンを使用してオブジェクトをフライウェイトとして設計し、複数のコード参照に対してメモリ内に 1 つのインスタンスのみを保持することで、メモリを削減できます。メモリを節約するオブジェクトの数。実は、同じオブジェクトをフライウェイトとして設計するだけでなく、類似したオブジェクトについては、それらのオブジェクトから同じ部分(フィールド)を抽出してフライウェイトとして設計し、これらの多数の類似したオブジェクトがこれらを参照できるようにすることもできます。フライウェイト。

3. 行動デザインパターン

創造的デザインパターンは主に「オブジェクトの作成」の問題を解決し、構造的デザインパターンは主に「クラスまたはオブジェクトの組み合わせ」の問題を解決し、動作的デザインパターンは主に「クラスまたはオブジェクト間の相互作用」の問題を解決することがわかっています。オブジェクト」。動作パターンは数多くあり、オブザーバーパターン、テンプレートパターン、戦略パターン、責任連鎖パターン、イテレータパターン、状態パターン、訪問者パターン、メモパターン、コマンドパターン、インタプリタパターン、中間モデルの11種類があります。

1. 観察者パターン

オブザーバー パターンは、オブザーバーと監視されるコードを切り離します。オブザーバー モードには、コード レベルでのデカップリングからアーキテクチャ レベルでのシステム デカップリング、または一部の製品設計アイデアに至るまで、幅広いアプリケーション シナリオがあり、そのすべてに電子メール サブスクリプション、RSS フィード、基本的にこのモードの影が含まれています。観察者のパターン。

さまざまなアプリケーション シナリオや要件に応じて、このモードにはまったく異なる実装方法もあります。同期ブロッキング実装方法と非同期非ブロッキング実装方法があり、プロセス内実装方法とプロセス間実装方法があります。同期ブロッキングは、主にコードの分離を目的とした最も古典的な実装方法です。非同期非ブロッキングは、コードの分離を達成できるだけでなく、コードの実行効率も向上します。オブザーバー モードのプロセス間の分離はより徹底されており、一般にメッセージ キューを使用して実現されます。異なるプロセス間の、観察される側と観察される側の間の相互作用。

フレームワークの役割は、実装の詳細を隠し、開発の困難さを軽減し、コードの再利用を実現し、ビジネス コードと非ビジネス コードを分離し、プログラマーがビジネス開発に集中できるようにすることです。非同期ノンブロッキング オブザーバー モードの場合、これを EventBus フレームワークに抽象化して、この効果を実現することもできます。EventBus は「イベント バス」と訳され、オブザーバー パターンを実装するためのスケルトン コードを提供します。このフレームワークに基づいて、最初から開発することなく、独自のビジネス シナリオに Observer パターンを簡単に実装できます。

2. テンプレートモード

テンプレート メソッド パターンは、メソッド内のアルゴリズム スケルトンを定義し、特定の手順をサブクラスの実装に延期します。テンプレート メソッド パターンを使用すると、アルゴリズム全体の構造を変更せずに、サブクラスでアルゴリズムの特定のステップを再定義できます。ここでいう「アルゴリズム」とは広義の「ビジネスロジック」として理解でき、特にデータ構造やアルゴリズムにおける「アルゴリズム」を指すものではありません。ここでのアルゴリズムの骨格が「テンプレート」であり、アルゴリズムの骨格を含むメソッドが「テンプレートメソッド」であり、テンプレートメソッドパターンの名前の由来でもあります。

テンプレート モードには、再利用と拡張という 2 つの機能があります。このうち再利用とは、親クラスで提供されているテンプレートメソッドのコードをすべてのサブクラスで再利用できることを意味します。拡張とは、フレームワークがテンプレート モードを通じて機能拡張ポイントを提供し、フレームワーク ユーザーがフレームワークのソース コードを変更せずに拡張ポイントに基づいてフレームワークの機能をカスタマイズできるようにすることを意味します。

それに加えて、コールバックについても話しました。これには、コードの再利用と拡張というテンプレート パターンと同じ役割があります。これは、一部のフレームワーク、クラス ライブラリ、コンポーネントなどの設計でよく使用されます。たとえば、JdbcTemplate はコールバックを使用します。

通常の関数呼び出しと比較して、コールバックは双方向の呼び出し関係です。クラスAは、ある関数FをクラスBにあらかじめ登録しておき、クラスAがクラスBのP関数を呼び出すと、クラスBは、クラスAが登録した関数Fを呼び出します。ここでのF関数は「コールバック関数」です。A が B を呼び出し、B が A を呼び出すこの呼び出しの仕組みを「コールバック」といいます。

コールバックは、同期コールバックと非同期コールバックに分類できます。アプリケーション シナリオの観点から見ると、同期コールバックはテンプレート モードに似ており、非同期コールバックはオブザーバー モードに似ています。コールバック モードとテンプレート モードの違いは、アプリケーション シナリオよりもコードの実装にあります。コールバックは合成関係に基づいて実装され、テンプレート モードは継承関係に基づいて実装されます。コールバックはテンプレート パターンよりも柔軟です。

3. 戦略パターン

戦略パターンは、アルゴリズム クラスのファミリーを定義し、各アルゴリズムを個別にカプセル化して、相互に置き換えることができるようにします。Strategy パターンは、アルゴリズムを使用するクライアントとは独立してアルゴリズムを変更できます (ここでのクライアントとは、アルゴリズムを使用するコードを指します)。戦略パターンは、戦略の定義、作成、使用を分離するために使用されます。実際、完全な戦略パターンはこれら 3 つの部分で構成されます。

戦略クラスの定義は比較的単純で、戦略インターフェイスとこのインターフェイスを実装する戦略クラスのグループが含まれます。ポリシーの作成は、ポリシー作成の詳細をカプセル化するファクトリ クラスによって行われます。ストラテジ モードには、オプションのストラテジのセットが含まれています。クライアント コードは、使用するストラテジを選択します。決定方法には、コンパイル時の静的決定と実行時の動的決定の 2 つの方法があります。その中でも、「実行時の動的決定」は、ストラテジーパターンの最も典型的な適用シナリオです。
実際のプロジェクト開発においても戦略パターンはよく使われます。最も一般的なアプリケーション シナリオは、長時間にわたる if-else または switch 分岐の判断を回避するために使用することです。ただし、それ以上の効果があります。テンプレート モードと同様に、フレームワーク拡張ポイントなどを提供することもできます。実際、戦略パターンの主な役割は、戦略の定義、作成、使用を分離し、各部分が複雑になりすぎたり、コードの量が多すぎたりしないように、コードの複雑さを制御することです。さらに、複雑なコードの場合、ストラテジ パターンはオープンクローズ原則を満たすことができ、新しいストラテジを追加する際、コードの変更を最小限に抑えて一元化し、バグが発生するリスクを軽減します。

4. 責任連鎖モデル

Chain of Responsibility パターンでは、複数のプロセッサが同じリクエストを順番に処理します。リクエストは最初にプロセッサ A によって処理され、次にプロセッサ B に渡され、プロセッサ B によって処理された後、プロセッサ C に渡され、というようにチェーンを形成します。チェーン内の各プロセッサは独自の処理責任を負うため、責任チェーン モードと呼ばれます。

GoF の定義では、プロセッサがリクエストを処理できるようになると、そのリクエストを後続のプロセッサに渡し続けることはありません。もちろん、実際の開発では、このモードの変形、つまりリクエストが途中で終了されず、すべてのプロセッサで処理されるモードもあります。

責任チェーン モードは、フレームワーク開発でフィルター機能とインターセプター機能を実装するためによく使用され、フレームワーク ユーザーがフレームワークのソース コードを変更せずに新しいフィルター機能とインターセプト機能を追加できるようにします。これは、前述した拡張にはオープンで、変更にはクローズであるという設計原則も反映しています。

5. イテレータパターン

イテレータ モードはカーソル モードとも呼ばれ、コレクション オブジェクトを走査するために使用されます。ここで言う「コレクションオブジェクト」は「コンテナ」や「集合オブジェクト」とも呼ばれ、実際には配列、リンクリスト、ツリー、グラフ、ジャンプリストなどのオブジェクトの集合体をまとめたオブジェクトのことを指します。イテレータ パターンの主な役割は、コンテナ コードとトラバーサル コードを分離することです。ほとんどのプログラミング言語には、使用できる既製のイテレーターが用意されているため、最初から開発する必要はありません。
一般に、コレクションを走査するには、for ループ、foreach ループ、イテレータ走査の 3 つの方法があります。後の 2 つは本質的に 1 種類であり、どちらも反復子トラバーサルとみなすことができます。for ループの走査と比較して、反復子を使用した走査には 3 つの利点があります。

  • イテレータ モードは、コレクション内の複雑なデータ構造をカプセル化します。開発者は、トラバース方法を知る必要はなく、コンテナによって提供されるイテレータを使用するだけです。
  • イテレータ モードでは、コレクション オブジェクトのトラバース操作がコレクション クラスから分離され、それがイテレータ クラスに入れられるため、この 2 つの役割がより単一になります。
  • イテレータ パターンを使用すると、オープンクローズの原則に沿って、新しいトラバーサル アルゴリズムを簡単に追加できます。さらに、イテレータはすべて同じインターフェイスから実装されるため、プログラムの実装ではなく、インターフェイスに基づいた開発中にイテレータを置き換えるのが簡単です。

イテレータを通じてコレクション要素を走査しているときに、コレクション内の要素を追加または削除すると、要素が繰り返し走査されたり、走査されなかったりすることがあります。この問題に対応して、このような予測不可能な運用結果を回避するための比較的簡単な解決策が 2 つあります。1 つは、トラバーサル中に要素の追加または削除が許可されていないこと、もう 1 つは要素の追加または削除後にトラバーサルがエラーを報告することです。最初の解決策は、イテレータの使用がいつ終了するかを判断することが難しいため、実装がより困難です。2 番目の解決策はより合理的で、Java 言語で採用されている解決策です。要素を追加および削除した後、フェールファスト ソリューションを選択して、トラバーサル操作が実行時例外を直接スローするようにします。

6. ステートモード

ステート パターンは通常、ステート マシンの実装に使用され、ステート マシンはゲームやワークフロー エンジンなどのシステム開発でよく使用されます。ステート マシンは有限ステート マシンとも呼ばれ、ステート、イベント、アクションの 3 つの部分で構成されます。このうち、イベントは遷移条件とも呼ばれる。イベントは状態の遷移とアクションの実行をトリガーします。ただし、このアクションは必須ではなく、何もせずに状態を転送するだけでも可能です。

ステートマシンについては、3 つの実装方法をまとめます。
最初の実装は分岐ロジック方式と呼ばれます。if-else または switch-case 分岐ロジックを使用し、状態遷移図を参照して、各状態遷移をそのままコードに文字通り変換します。単純なステート マシンの場合、この実装が最も単純かつ簡単であり、最初の選択肢となります。

2 番目の実装はルックアップ テーブル方式と呼ばれます。多くの状態と複雑な状態遷移を持つステート マシンの場合は、ルックアップ テーブル方式の方が適しています。状態遷移図を 2 次元配列で表現すると、コードの可読性と保守性が大幅に向上します。

これを実現する 3 番目の方法は、状態パターンを使用することです。状態が少なく、状態遷移が比較的単純なステート マシンの場合、イベントによってトリガーされるアクションに含まれるビジネス ロジックはより複雑になる可能性があるため、この実装方法を推奨します。

7. 訪問者のパターン

ビジター パターンでは、1 つ以上の操作をオブジェクトのセットに適用できます。設計の意図は、操作とオブジェクト自体を分離し、クラスを単一の責任で維持し、オープンとクローズの原則を満たし、コードの複雑さ。
ビジター モードの場合、学習の主な困難はコードの実装にあります。コードの実装がより複雑になる主な理由は、ほとんどのオブジェクト指向プログラミング言語では関数のオーバーロードが静的にバインドされているためです。つまり、クラスのどのオーバーロード関数を呼び出すかは、実行時のパラメーターの実際の型ではなく、コンパイル時に宣言されたパラメーターの型によって決まります。さらに、ダブルディスパッチについても話しました。言語が Double Dispatch をサポートしている場合、Visitor パターンは必要ありません。
コードの実装が理解しにくいからこそ、このパターンをプロジェクトに適用するとコードの可読性が低下します。同僚がこの設計パターンを理解していない場合、あなたが作成したコードを読んだり保守したりできない可能性があります。したがって、必要な場合を除き、このモードを使用しないでください。

8.メモモード

Memento モードはスナップショット モードとも呼ばれ、具体的には、カプセル化の原則に違反することなくオブジェクトの内部状態をキャプチャし、この状態をオブジェクトの外部に保存して、後でオブジェクトを以前の状態に復元できるようにします。このパターンの定義は 2 つの部分を表します: 1 つの部分は、後でリカバリするためにコピーを保存することであり、もう 1 つの部分は、カプセル化の原則に違反することなくオブジェクトのバックアップとリカバリを実行することです。
メモ モードの適用シナリオも比較的明確かつ限定されており、主に損失防止、失効、回復などに使用されます。これは、私たちが通常「バックアップ」と呼んでいるものと非常によく似ています。2 つの主な違いは、メモ パターンはコードの設計と実装に重点を置いているのに対し、バックアップはアーキテクチャ設計または製品設計に重点を置いているということです。
大きなオブジェクトのバックアップの場合、バックアップが占有するストレージ容量は比較的大きくなり、バックアップと復元にかか​​る時間は比較的長くなります。この問題に対応して、ビジネス シナリオごとに処理方法が異なります。たとえば、必要なリカバリ情報のみをバックアップし、最新のデータと組み合わせてリカバリしたり、フル バックアップと増分バックアップ、低頻度のフル バックアップ、高頻度の増分バックアップを組み合わせて、その 2 つを組み合わせてリカバリしたりすることができます。

9. コマンドモード

コマンド モードは日常業務ではあまり使用されないため、少し理解するだけで十分です。
コーディング実装に関しては、コマンド モードで使用される中心的な実装方法は、関数をオブジェクトにカプセル化することです。ほとんどのプログラミング言語では、関数をパラメータとして他の関数に渡すことも、変数に割り当てることもできないことがわかっています。コマンド パターンを使用すると、関数をオブジェクトにカプセル化して、関数をオブジェクトのように使用できるようにします。
コマンド モードの主な機能とアプリケーション シナリオは、非同期、遅延、キュー実行コマンド、元に戻すおよびやり直しコマンド、ストア コマンド、コマンドのログの記録などのコマンドの実行を制御することです。これは、コマンド モードの固有の役割です。コマンドモードの場所。

10. 通訳モード

インタプリタ パターンは、言語の文法 (または文法) 表現と、その文法を処理するためのインタプリタを定義します。実は、ここでいう「言語」とは、私たちが普段話している中国語、英語、日本語、フランス語などの言語だけを指すわけではありません。広義には、情報を運ぶことができる媒体である限り、それを「言語」と呼ぶことができます。たとえば、古代の結び目メモ、点字、ダム言語、モールス信号などです。

「言語」によって表現される情報を理解するには、対応する文法規則を定義する必要があります。このように、書き手は文法ルールに従って「文章」を書くことができ(専門名は「表現」となります)、読み手は文法ルールに従って「文」を読むことができ、情報を正しく伝えることができます。これから説明するインタープリター モードは、実際には、文法規則に従って「文」を解釈するために使用されるインタープリターです。

インタプリタ モードのコード実装はより柔軟であり、固定されたテンプレートはありません。前に述べたように、アプリケーション設計パターンは主にコードの複雑さに対処するものであり、インタープリター パターンも例外ではありません。そのコード実装の中心的な考え方は、大規模で包括的な分析クラスを避けるために、文法分析の作業をさまざまな小さなクラスに分割することです。一般的なアプローチは、文法規則をいくつかの小さな独立した単位に分割し、次に各単位を解析し、最後にそれらを文法規則全体の解析にマージすることです。

11. 中間パターン

中間パターンの設計思想は中間層の設計思想と非常に似ており、中間層の中間層を導入することにより、オブジェクト群間の相互作用関係(または依存関係)が多対多(多対多)に変換されます。ネットワーク関係) から 1 対多 (スター関係) への関係)。本来、オブジェクトは n 個のオブジェクトと対話する必要がありますが、現在は 1 つの中間オブジェクトと対話するだけで済みます。これにより、オブジェクト間の対話が最小限に抑えられ、コードの複雑さが軽減され、コードの可読性と保守性が向上します。

観察者パターンと仲介パターンはいずれも、参加者間の分離を実現し、対話関係を単純化することを目的としています。2 つの違いはアプリケーション シナリオにあります。オブザーバー パターンの適用シナリオでは、参加者間の対話は比較的組織化されており、通常は一方向であり、参加者はオブザーバーまたはオブザーバーのいずれかの ID を 1 つだけ持ちます。中間モデルの適用シナリオでは、参加者間の対話関係は複雑であり、参加者は同時にメッセージの送信者であると同時にメッセージの受信者にもなり得ます。

クラスディスカッション

これら 23 のデザイン パターンを最終的に学習した後、これらのデザイン パターンについてどのような質問がありますか? メッセージエリアでそれについて話すことができます。

おすすめ

転載: blog.csdn.net/qq_32907491/article/details/131275536