paint-brush
JavaScript デザインパターンをマスターしたいですか?知っておくべきことはすべてここにあります!@alexmerced
4,501 測定値
4,501 測定値

JavaScript デザインパターンをマスターしたいですか?知っておくべきことはすべてここにあります!

Alex Merced41m2024/03/14
Read on Terminal Reader

長すぎる; 読むには

この包括的なガイドで JavaScript デザイン パターンの詳細を学びましょう。初心者でも経験豊富な開発者でも、JavaScript コードの構造と保守性を強化するテクニックをマスターしてください。
featured image - JavaScript デザインパターンをマスターしたいですか?知っておくべきことはすべてここにあります!
Alex Merced HackerNoon profile picture
0-item


クリーンで保守しやすく効率的なコードを作成する場合、ソフトウェア開発の世界ではデザイン パターンが重要な役割を果たします。デザイン パターンは、開発者がソフトウェア システムの設計および構築中に直面する一般的な問題に対する再利用可能な解決策です。これらは、特定の課題を解決するための構造化されたアプローチを提供し、堅牢なだけでなく理解と保守も容易なコードの作成を容易にします。


オブジェクト指向プログラミング (OOP ) では、デザイン パターンは、柔軟性、再利用性、および拡張性を促進する方法でコードを構造化するためのガイドラインとして機能します。これらは、ベスト プラクティスと設計原則をカプセル化し、進化し、実証済みのソリューションに集約されています。


コンテンツの概要

  • デザインパターンのカテゴリー
  • 一般的なデザインパターン
  • JavaScript のシングルトン パターン
  • JavaScript のファクトリ パターンと抽象ファクトリ パターン
  • JavaScript のビルダー パターン
  • JavaScript のプロトタイプ パターン
  • JavaScript のオブジェクト プール パターン
  • JavaScript のアダプター パターン
  • JavaScript のデコレータ パターン
  • JavaScript のプロキシ パターン
  • JavaScript の複合パターン
  • JavaScript のブリッジ パターン
  • JavaScript のフライウェイト パターン
  • JavaScript のオブザーバー パターン
  • JavaScript の戦略パターン
  • JavaScript のコマンド パターン
  • JavaScript の状態パターン
  • JavaScript における責任連鎖パターン
  • JavaScript の訪問者パターン
  • 結論


デザインパターンのカテゴリー

デザイン パターンは、次の 3 つの主要なグループに分類できます。

  1. 作成パターン:これらのパターンはオブジェクト作成メカニズムに焦点を当てており、状況に適した方法でオブジェクトを作成しようとします。これらはインスタンス化プロセスを抽象化し、プロセスをより柔軟にし、システムから独立させます。


  2. 構造パターン:構造パターンはオブジェクトの構成を扱い、オブジェクト間の関係を形成して、より大きく、より複雑な構造を作成します。これらは、オブジェクトとクラスを組み合わせて新しい構造を形成し、新しい機能を提供する方法を定義するのに役立ちます。


  3. 行動パターン:行動パターンはオブジェクト間の通信に関係し、オブジェクトがどのように相互作用し、責任を分散するかを定義します。これらのパターンは、オブジェクトがより柔軟かつ効率的な方法で連携するシステムを設計するのに役立ちます。


一般的なデザインパターン

各カテゴリの一般的なデザイン パターンのリストを次に示します。


創作パターン

  1. シングルトン パターン:クラスにインスタンスが 1 つだけあることを保証し、そのインスタンスへのグローバル アクセス ポイントを提供します。
  2. ファクトリ メソッド パターン:オブジェクトを作成するためのインターフェイスを定義しますが、サブクラスは作成されるオブジェクトのタイプを変更できます。
  3. Abstract Factory Pattern:具体的なクラスを指定せずに、関連オブジェクトまたは依存オブジェクトのファミリーを作成するためのインターフェイスを提供します。
  4. ビルダー パターン:複雑なオブジェクトの構築をその表現から分離し、同じ構築プロセスで異なる表現を作成できるようにします。
  5. プロトタイプ パターン:プロトタイプと呼ばれる、既存のオブジェクトをコピーして新しいオブジェクトを作成します。
  6. オブジェクト プール パターン:再利用可能なオブジェクトのプールを管理して、オブジェクトの作成と破棄のオーバーヘッドを最小限に抑えます。


構造パターン

  1. アダプター パターン:既存のクラスのインターフェイスを別のインターフェイスとして使用できるようにします。
  2. デコレータ パターン:追加の責任をオブジェクトに動的に付加し、サブクラス化に代わる柔軟な手段を提供します。
  3. プロキシ パターン:別のオブジェクトへのアクセスを制御するためのサロゲートまたはプレースホルダーを提供します。
  4. 複合パターン:オブジェクトをツリー構造に合成して、部分全体の階層を表します。
  5. ブリッジ パターン:オブジェクトの抽象化をその実装から分離し、両方を独立して変更できるようにします。
  6. Flyweight パターン:関連オブジェクトと可能な限り共有することで、メモリ使用量や計算コストを最小限に抑えます。


行動パターン

  1. オブザーバー パターン:オブジェクト間の 1 対多の依存関係を定義します。そのため、1 つのオブジェクトの状態が変化すると、そのすべての依存オブジェクトが通知され、自動的に更新されます。

  2. 戦略パターン:アルゴリズムのファミリーを定義し、それぞれをカプセル化して交換可能にします。

  3. コマンド パターン:リクエストをオブジェクトとしてカプセル化することで、キュー、リクエスト、操作によるクライアントのパラメータ化が可能になります。

  4. 状態パターン:オブジェクトの内部状態が変化したときに、その動作を別のクラスでラップして、オブジェクトの動作を変更できるようにします。

  5. 責任の連鎖パターン:ハンドラーのチェーンに沿ってリクエストを渡し、各ハンドラーがリクエストを処理するかチェーン内の次のハンドラーに渡すかを決定できるようにします。

  6. ビジター パターン:これは、オブジェクト構造の要素に対して実行される操作を表し、要素のクラスを変更せずに新しい操作を定義できるようにします。


このブログでは、これらの各設計パターンを詳しく説明し、説明、実際の使用例、およびJavaScriptコードの例を提供して、それらを理解してプロジェクトに効果的に実装するのに役立ちます。


JavaScript のシングルトン パターン

シングルトン パターンは、クラスにインスタンスが 1 つだけ存在することを保証し、そのインスタンスへのグローバル アクセス ポイントを提供する作成設計パターンです。このパターンは、アプリケーション内のクラスのインスタンスの数を制限し、単一の共有インスタンスへのアクセスを制御する場合に特に便利です。


JavaScript では、言語の柔軟性のおかげで、シングルトン パターンの実装は比較的簡単です。 JavaScript でシングルトンを作成する方法の簡単な例を見てみましょう。

実装例

// Singleton instance let instance = null;
 class Singleton { constructor() { if (!instance) { instance = this; // Your initialization code here } else { return instance; } } // Your methods and properties here }// Usage const singletonA = new Singleton(); const singletonB = new Singleton(); console.log(singletonA === singletonB); // Output: true (both variables reference the same instance)

この例では、インスタンスが既に存在するかどうかを確認するコンストラクターを含む Singleton クラスを作成します。インスタンスが存在しない場合は、インスタンスを作成してインスタンス変数に割り当てます。コンストラクターへの後続の呼び出しでは既存のインスタンスが返され、Singleton クラスのインスタンスが 1 つだけ存在することが保証されます。


実際の使用例

シングルトン パターンは、次のようなさまざまなシナリオで役立ちます。


  • 構成設定の管理:シングルトンを使用してアプリケーションの構成設定を管理し、構成値の信頼できる唯一の情報源を確保できます。
  • ロガーとエラー処理:シングルトンを使用して、集中ログまたはエラー処理メカニズムを維持し、ログ エントリやエラー メッセージを統合できます。
  • データベース接続:データベースを扱うときは、シングルトンを使用して、アプリケーション全体で共有されるデータベース接続が 1 つだけになるようにして、リソースの消費を最小限に抑えることができます。
  • キャッシュ:頻繁に使用されるデータをキャッシュするためにシングルトンを実装すると、単一のキャッシュ インスタンスを維持することでパフォーマンスの最適化に役立ちます。


考慮事項

シングルトン パターンは有益な場合もありますが、慎重に使用することが重要です。シングルトン パターンを過度に使用すると、コードとグローバル状態が緊密に結合される可能性があり、アプリケーションの保守とテストが困難になる可能性があります。したがって、メリットとデメリットを比較検討し、コードベースに真の価値をもたらすパターンを適用することが重要です。


JavaScript のファクトリ パターンと抽象ファクトリ パターン

ファクトリ パターンと抽象ファクトリ パターンは、オブジェクトの作成を扱う創造的なデザイン パターンですが、その方法は異なり、異なる目的を果たします。これらの各パターンを調べて、JavaScript でどのように実装できるかを見てみましょう。


ファクトリーパターン

ファクトリ パターンは、オブジェクトを作成するためのインターフェイスを提供する作成パターンですが、サブクラスが作成されるオブジェクトのタイプを変更できるようにします。オブジェクト作成プロセスがカプセル化され、より柔軟になり、クライアント コードから切り離されます。

実装例

// Product class class Product { constructor(name) { this.name = name; } }
 // Factory for creating products class ProductFactory { createProduct(name) { return new Product(name); } }// Usage const factory = new ProductFactory(); const productA = factory.createProduct('Product A'); const productB = factory.createProduct('Product B');console.log(productA.name); // Output: 'Product A' console.log(productB.name); // Output: 'Product B'

この例では、ProductFactory は Product クラスのインスタンスの作成を担当します。作成プロセスを抽象化し、ファクトリーを拡張することでさまざまなタイプの製品を作成できるようにします。


抽象的な工場パターン

Abstract Factory パターンは、具体的なクラスを指定せずに関連オブジェクトまたは依存オブジェクトのファミリーを作成するためのインターフェイスを提供する別の作成パターンです。これにより、調和して連携するオブジェクトのセットを作成できます。


実装例

// Abstract Product classes class Button { render() {} }
 class Checkbox { render() {} }// Concrete Product classes class MacButton extends Button { render() { return 'Render Mac button'; } }class MacCheckbox extends Checkbox { render() { return 'Render Mac checkbox'; } }class WindowsButton extends Button { render() { return 'Render Windows button'; } }class WindowsCheckbox extends Checkbox { render() { return 'Render Windows checkbox'; } }// Abstract Factory interface class GUIFactory { createButton() {} createCheckbox() {} }// Concrete Factories class MacFactory extends GUIFactory { createButton() { return new MacButton(); } createCheckbox() { return new MacCheckbox(); } }class WindowsFactory extends GUIFactory { createButton() { return new WindowsButton(); } createCheckbox() { return new WindowsCheckbox(); } }// Usage function createUI(factory) { const button = factory.createButton(); const checkbox = factory.createCheckbox(); return { button, checkbox }; }const macUI = createUI(new MacFactory()); console.log(macUI.button.render()); // Output: 'Render Mac button' console.log(macUI.checkbox.render()); // Output: 'Render Mac checkbox'const windowsUI = createUI(new WindowsFactory()); console.log(windowsUI.button.render()); // Output: 'Render Windows button' console.log(windowsUI.checkbox.render()); // Output: 'Render Windows checkbox'


この例では、MacFactory と WindowsFactory という 2 つの具体的なファクトリがあり、それぞれのプラットフォームに関連する UI コンポーネント (ボタンとチェックボックス) のセットを作成できます。 createUI 関数を使用すると、適切なファクトリを使用して特定のプラットフォーム用の一貫した UI を作成できます。


いつどのパターンを使用するか:

  • オブジェクト作成プロセスをカプセル化し、さまざまな実装でオブジェクトを作成するための単純なインターフェイスを提供する場合は、ファクトリ パターンを使用します
  • 連携して動作する必要がある関連オブジェクトまたは依存オブジェクトのファミリーを作成する必要がある場合は、 Abstract Factory パターンを使用します。これは、作成されたオブジェクトに互換性と一貫性があることを確認するのに役立ちます。


JavaScript のビルダー パターン

ビルダー パターンは、複雑なオブジェクトの構築をその表現から分離する創造的なデザイン パターンであり、同じ構築プロセスで異なる表現を作成できるようにします。このパターンは、オブジェクトに多数のプロパティがあり、柔軟性を維持しながらインスタンスの作成を簡素化したい場合に特に便利です。

JavaScript では、ビルダー パターンは、複雑なオブジェクトの段階的な構築をガイドするビルダー クラスまたはオブジェクトを使用して実装されることがよくあります。それがどのように機能するかを理解するために、例を見てみましょう。


実装例

// Product class with multiple properties class Product { constructor() { this.name = ''; this.price = 0; this.color = 'white'; // ... other properties }
 // Additional methods can be defined here }// Builder for creating Product instances class ProductBuilder { constructor() { this.product = new Product(); } setName(name) { this.product.name = name; return this; // Return the builder for method chaining } setPrice(price) { this.product.price = price; return this; } setColor(color) { this.product.color = color; return this; } // Other methods to set additional properties build() { return this.product; // Return the fully constructed product } }// Usage const builder = new ProductBuilder();const productA = builder .setName('Product A') .setPrice(99.99) .setColor('blue') .build();const productB = builder .setName('Product B') .setPrice(49.99) .build();console.log(productA); console.log(productB);


この例では、複数のプロパティを持つ Product クラスがあります。 ProductBuilder クラスは、各プロパティを段階的に設定するメソッドを提供することで、Product のインスタンスの作成を支援します。メソッドチェーンを使用すると、複数のプロパティを滑らかで読みやすい方法で設定できます。最後に、build メソッドは完全に構築された Product インスタンスを返します。


実際の使用例

ビルダー パターンは、次のようなさまざまなシナリオで役立ちます。

  • 複雑なオブジェクトの作成:多くのオプションまたは構成可能なプロパティを持つオブジェクトを作成する必要がある場合、ビルダー パターンを使用すると構築プロセスが簡素化されます。
  • 不変オブジェクト:ビルダーを使用すると、構築中にプロパティを設定でき、その後の変更を防ぐことができるため、不変オブジェクトを作成できます。
  • パラメーター化されたコンストラクター:ビルダー パターンは、コンストラクターで長いパラメーター リストを使用する代わりに、オブジェクトを構築するためのよりクリーンでより組織化されたアプローチを提供します。
  • 構成オブジェクト:ライブラリまたはコンポーネントを構成する場合、ビルダーは構成オブジェクトの作成とカスタマイズを支援できます。


考慮事項

ビルダー パターンには多くの利点がありますが、特に構築されるオブジェクトが比較的単純な場合、コードベースが複雑になることに注意することが重要です。したがって、Builder によってもたらされる複雑さが特定の使用例にとって正当であるかどうかを評価することが重要です。


JavaScript のプロトタイプ パターン

プロトタイプ パターンは、プロトタイプと呼ばれる既存のオブジェクトをコピーすることで新しいオブジェクトを作成できる創造的なデザイン パターンです。作成するオブジェクトの正確なクラスを指定せずに、オブジェクトの作成を促進します。このパターンは、複雑なオブジェクトのインスタンスを効率的に作成する場合に特に便利です。


JavaScript では、プロトタイプ パターンは、組み込みのprototypeプロパティおよびObject.create()メソッドと密接に関連しています。 JavaScript でプロトタイプ パターンを実装して使用する方法を見てみましょう。


実装例

// Prototype object const vehiclePrototype = { init(make, model) { this.make = make; this.model = model; }, getDetails() { return `${this.make} ${this.model}`; }, };
 // Create new instances using the prototype const car1 = Object.create(vehiclePrototype); car1.init('Toyota', 'Camry');const car2 = Object.create(vehiclePrototype); car2.init('Honda', 'Civic');console.log(car1.getDetails()); // Output: 'Toyota Camry' console.log(car2.getDetails()); // Output: 'Honda Civic'


この例では、すべての車両に共通のメソッドとプロパティを使用してvehiclePrototypeオブジェクトを定義します。 Object.create()を使用して、このプロトタイプに基づいて新しいインスタンス (car1 および car2) を作成します。これらのインスタンスはプロトタイプからプロパティとメソッドを継承するため、動作を共有する新しいオブジェクトを効率的に作成できます。


実際の使用例

プロトタイプ パターンは、次のようなさまざまなシナリオで役立ちます。

  • オブジェクトの初期化オーバーヘッドの削減:同様の構造を持つオブジェクトの複数のインスタンスを作成する必要がある場合、プロトタイプ パターンは、オブジェクトのプロパティとメソッドを繰り返し設定するオーバーヘッドを削減します。
  • 複雑なオブジェクトのクローン作成:ネストされた構造を持つ複雑なオブジェクトがある場合、プロトタイプ パターンを使用すると、プロトタイプをコピーすることで、同様のオブジェクトの作成が簡素化されます。
  • 構成可能なオブジェクトの作成:さまざまな構成でオブジェクトを作成する場合は、プロトタイプを使用して、さまざまな設定でオブジェクトを初期化できます。


考慮事項

プロトタイプ パターンは便利ですが、いくつかの考慮事項があります。

  • 浅いコピー:デフォルトでは、JavaScript の Object.create() メソッドはプロパティの浅いコピーを実行します。プロトタイプにネストされたオブジェクトまたは関数が含まれている場合、それらはインスタンス間で共有されます。必要に応じて、ディープ コピーを実装する必要がある場合があります。
  • プロトタイプの変更:プロトタイプのプロパティまたはメソッドを変更する場合は、プロトタイプから作成されたすべてのインスタンスに影響を与える可能性があるため、注意してください。
  • 初期化:プロトタイプ パターンでは、インスタンス固有のプロパティを設定するために別の初期化手順が必要になることが多く、複雑さが増す可能性があります。


JavaScript のオブジェクト プール パターン

オブジェクト プール パターンは、再利用可能なオブジェクトのプールを管理して、オブジェクトの作成と破棄のオーバーヘッドを最小限に抑える創造的なデザイン パターンです。これは、オブジェクトの作成と破棄に費用がかかる場合やリソースを大量に消費する場合に特に便利です。オブジェクト プール パターンは、新しいオブジェクトを最初から作成するのではなく、オブジェクトをリサイクルして再利用することで、パフォーマンスとリソースの使用率を向上させるのに役立ちます。


JavaScript では、配列またはカスタム プール管理クラスを使用してオブジェクト プール パターンを実装できます。簡単な例を使用して、このパターンがどのように機能するかを見てみましょう。

実装例

class ObjectPool { constructor(maxSize) { this.maxSize = maxSize; this.pool = []; }
 create() { if (this.pool.length < this.maxSize) { // Create a new object and add it to the pool const obj = { /* Your object initialization code here */ }; this.pool.push(obj); return obj; } else { // Pool is full, cannot create more objects console.log('Pool is full. Cannot create more objects.'); return null; } } reuse() { if (this.pool.length > 0) { // Reuse an object from the pool return this.pool.pop(); } else { // Pool is empty, no objects available for reuse console.log('Pool is empty. No objects available for reuse.'); return null; } } release(obj) { // Release an object back to the pool for reuse this.pool.push(obj); } }// Usage const pool = new ObjectPool(5); // Create a pool with a maximum size of 5 objectsconst obj1 = pool.create(); const obj2 = pool.create(); const obj3 = pool.create();pool.release(obj2); // Release obj2 back to the pool for reuseconst obj4 = pool.reuse(); // Reuse an object from the pool (obj2)


この例では、オブジェクトのプールを管理する ObjectPool クラスを作成します。 create メソッドはプールがいっぱいでないときに新しいオブジェクトを作成し、reuse メソッドは再利用するためにプールからオブジェクトを取得し、release メソッドは将来の使用のためにオブジェクトをプールに返します。


実際の使用例

オブジェクト プール パターンは、次のようなさまざまなシナリオで役立ちます。

  • データベース接続:データベース接続の管理はリソースを大量に消費する可能性があります。オブジェクト プールを使用すると、接続を再利用できるため、パフォーマンスが向上し、オーバーヘッドが削減されます。
  • スレッド管理:マルチスレッド環境では、特にスレッドの作成にコストがかかる場合に、オブジェクト プールを使用してスレッドを管理できます。
  • リソースを大量に消費するオブジェクト:大量のメモリを消費するオブジェクトや初期化に時間がかかるオブジェクトの場合、オブジェクト プール パターンを使用すると、インスタンスの作成と破棄のオーバーヘッドを削減できます。


考慮事項

オブジェクト プール パターンはパフォーマンス上の利点をもたらしますが、次の点を考慮することが重要です。

  • リソース管理:オブジェクトが使用後にプールに適切に返されるようにするには、リソースを慎重に管理する必要があります。
  • プール サイズ:リソースの使用率とメモリ消費のバランスをとるには、適切なプール サイズを選択することが重要です。
  • スレッド セーフ:マルチスレッド環境では、スレッド セーフを確保するために適切な同期メカニズムを実装する必要があります。


JavaScript のアダプター パターン

アダプター パターンは、互換性のないインターフェイスを持つオブジェクトが連携できるようにする構造設計パターンです。これは、互換性のない 2 つのインターフェイス間のブリッジとして機能し、ソース コードを変更せずに互換性を持たせます。このパターンは、アプリケーションの要件に適合しない既存のコードを統合または使用する必要がある場合に特に便利です。


JavaScript では、互換性のないインターフェイスをラップまたは適応させるクラスまたは関数を使用してアダプター パターンを実装できます。実際の例を使用して、JavaScript でアダプター パターンを実装して使用する方法を見てみましょう。


実装例

OldSystemという既存のクラスがあり、 legacyRequestというメソッドがあるとします。

 class OldSystem { legacyRequest() { return 'Data from the legacy system'; } }


ここで、このレガシー システムを、別のインターフェイスを必要とする最新のアプリケーションで使用したいと考えています。次のようにアダプター クラスまたは関数を作成できます。

 class Adapter { constructor(oldSystem) { this.oldSystem = oldSystem; }
 newRequest() { const legacyData = this.oldSystem.legacyRequest(); // Adapt the data or perform any necessary transformations return `Adapted: ${legacyData}`; } }


これで、Adapter クラスを使用して、レガシー システムを最新のアプリケーションと互換性のあるものにすることができます。

 const oldSystem = new OldSystem(); const adapter = new Adapter(oldSystem);
 const result = adapter.newRequest(); console.log(result); // Output: 'Adapted: Data from the legacy system'


この例では、Adapter クラスが OldSystem をラップし、最新のアプリケーションと互換性のある新しいインターフェイス newRequest を提供します。


実際の使用例

アダプター パターンは、次のようなさまざまなシナリオで役立ちます。

  • レガシー コードの統合:レガシー システムまたはライブラリを最新のコードベースと統合する必要がある場合、アダプターは 2 つの間のギャップを埋めるのに役立ちます。
  • サードパーティのライブラリ:互換性のないインターフェイスを持つサードパーティのライブラリまたは API を使用する場合、アダプタはそれらをアプリケーションの要件に適合させることができます。
  • テスト:アダプターは、依存関係を分離するためにテスト中にモック オブジェクトを作成したりインターフェイスをシミュレートしたりするのに役立ちます。
  • バージョンの互換性:アダプターを使用すると、さまざまなバージョンの API またはライブラリとの互換性を維持できます。


考慮事項

アダプター パターンは柔軟性と互換性を提供しますが、次のいくつかの点を考慮することが重要です。

  • パフォーマンス:アダプターでは、追加のメソッド呼び出しやデータ変換により、オーバーヘッドが発生する可能性があります。必要に応じて測定して最適化します。
  • 保守性:将来の開発者がアダプターの目的と使用法を確実に理解できるように、アダプター コードをクリーンかつ十分に文書化した状態に保ちます。
  • インターフェイスの複雑さ:あまりにも多くのことを実行しようとする過度に複雑なアダプターを作成しないように注意してください。特定の適応タスクに集中させてください。


JavaScript のデコレータ パターン

デコレーター パターンは、既存のコードを変更せずに、オブジェクトに新しい動作や責任を動的に追加できる構造設計パターンです。これは、オブジェクトをデコレータ オブジェクトでラップすることにより、オブジェクトの機能を拡張する強力な方法です。このパターンは、「拡張にはオープンだが、変更にはクローズ」という原則を促進し、コアの実装を変更せずにオブジェクトに新しい機能を簡単に追加できるようにします。


JavaScript では、クラスとオブジェクトの合成を使用してデコレータ パターンを実装できます。実際の例を使用して、JavaScript でデコレータ パターンを実装して使用する方法を見てみましょう。


実装例

基本クラスCoffeeあるとします。

 class Coffee { cost() { return 5; // Base cost of a regular coffee } } Now, you want to add decorators to your coffee to customize it with additional options, such as milk and sugar:
 javascript Copy code class MilkDecorator { constructor(coffee) { this.coffee = coffee; } cost() { return this.coffee.cost() + 2; // Adding the cost of milk } }class SugarDecorator { constructor(coffee) { this.coffee = coffee; } cost() { return this.coffee.cost() + 1; // Adding the cost of sugar } }


次に、次のように装飾されたコーヒー インスタンスを作成できます。

 const regularCoffee = new Coffee(); const coffeeWithMilk = new MilkDecorator(regularCoffee); const coffeeWithMilkAndSugar = new SugarDecorator(coffeeWithMilk);
 console.log(regularCoffee.cost()); // Output: 5 console.log(coffeeWithMilk.cost()); // Output: 7 console.log(coffeeWithMilkAndSugar.cost()); // Output: 8


この例では、ベースのコーヒーを表す Coffee クラスがあります。 MilkDecorator クラスと SugarDecorator クラスは、コーヒー オブジェクトをラップし、それぞれミルクと砂糖のコストを基本コストに追加するデコレーターです。


実際の使用例

デコレータ パターンは、次のようなさまざまなシナリオで役立ちます。

  • クラスの拡張:デコレータを使用すると、ソース コードを変更せずにクラスに機能を追加できるため、新しい機能を簡単に導入できます。
  • 動的構成:デコレータを使用すると、実行時にオブジェクトを動的に構成できるため、単純なコンポーネントから複雑なオブジェクトを構築できます。
  • カスタマイズ:デコレータのさまざまな組み合わせを適用することでオブジェクトをカスタマイズでき、柔軟性と構成可能性が得られます。
  • ロギングとプロファイリング:デコレータを使用すると、元のクラスを変更せずにメソッド呼び出しのログやプロファイリングを行うことができます。


考慮事項

デコレータ パターンは多用途ですが、いくつかの考慮事項に留意することが重要です。

  • 装飾の順序:デコレーターを適用する順序は最終的な動作に影響を与える可能性があるため、オブジェクトをラップする順序に注意してください。
  • 複雑さ:デコレータを使いすぎると、コードが複雑で複雑になる可能性があるため、デコレータがユースケースにとって最適なソリューションであるかどうかを慎重に検討してください。
  • インターフェイスの互換性:デコレータが共通のインターフェイスに準拠するか、デコレータが装飾するオブジェクトとの互換性を維持するために契約するようにします。


JavaScript のプロキシ パターン

プロキシ パターンは、別のオブジェクトへのアクセスを制御するためのサロゲートまたはプレースホルダを提供する構造設計パターンです。これはターゲット オブジェクトの仲介またはラッパーとして機能し、追加の動作の追加、アクセスの制御、またはオブジェクトの作成の遅延を可能にします。プロキシ パターンは、遅延読み込み、アクセス制御、ロギングの実装など、さまざまなシナリオで役立ちます。


JavaScript では、組み込みのProxyオブジェクトを使用してプロキシを作成できます。実際の例を使用して、JavaScript でプロキシ パターンを実装して使用する方法を見てみましょう。


実装例

プロキシを使用した遅延読み込み

必要な場合にのみ遅延ロードしたい、リソースを大量に消費するオブジェクトがあるとします。プロキシを使用して遅延読み込みを実現できます。

 class ExpensiveResource { constructor() { console.log('Creating an expensive resource...'); }
 fetchData() { console.log('Fetching data...'); } }class LazyResourceProxy { constructor() { this.resource = null; } fetchData() { if (!this.resource) { this.resource = new ExpensiveResource(); } this.resource.fetchData(); } }// Usage const lazyResource = new LazyResourceProxy(); // The actual resource is created and data is fetched only when needed lazyResource.fetchData();

この例では、LazyResourceProxy は ExpensiveResource の代理として機能し、 fetchData メソッドが初めて呼び出されたときにのみ実際のリソースを作成します。


プロキシによるアクセス制御

プロキシを使用して、オブジェクトとそのプロパティへのアクセスを制御することもできます。

 const user = { username: 'john_doe', password: 'secret123', };
 const userProxy = new Proxy(user, { get(target, property) { if (property === 'password') { throw new Error('Access denied to password.'); } return target[property]; }, });console.log(userProxy.username); // Output: 'john_doe' console.log(userProxy.password); // Throws an error: 'Access denied to password.'

この例では、プロキシが get 操作をインターセプトし、パスワード プロパティへのアクセスを制限します。


実際の使用例

プロキシ パターンは、次のようなさまざまなシナリオで役立ちます。

  • 遅延読み込み:プロキシを使用すると、リソースを大量に消費するオブジェクトの作成と初期化を、実際に必要になるまで延期できます。
  • アクセス制御:プロキシはアクセス制御ポリシーを適用し、特定のプロパティまたはメソッドへのアクセスを制限または許可できます。
  • キャッシュ:プロキシはキャッシュ メカニズムを実装し、コストのかかる操作を行う代わりに、キャッシュされたデータを保存して返すことでパフォーマンスを向上させることができます。
  • ロギングとプロファイリング:プロキシはメソッド呼び出しをログまたはプロファイリングすることができ、オブジェクトの動作についての洞察を得るのに役立ちます。


考慮事項

プロキシ パターンを使用する場合は、次の考慮事項に留意してください。

  • オーバーヘッド:プロキシでは、操作のインターセプトによりオーバーヘッドが発生する可能性があります。特にパフォーマンスが重要なコードでは、パフォーマンスへの影響に注意してください。
  • 互換性:既存のコードとの互換性を維持するために、プロキシがターゲット オブジェクトと同じインターフェイスに準拠していることを確認します。
  • セキュリティ:プロキシはアクセス制御に役立ちますが、唯一のセキュリティ対策として依存すべきではありません。特にサーバー側で追加のセキュリティ対策が必要になる場合があります。


JavaScript の複合パターン

複合パターンは、オブジェクトをツリー状の構造に合成して部分全体の階層を表現できる構造設計パターンです。これにより、クライアントは個々のオブジェクトとオブジェクトの構成を均一に扱うことができます。複合パターンは、一貫したインターフェイスを維持しながら、より小さな関連オブジェクトで構成される複雑な構造を操作する必要がある場合に特に便利です。


JavaScript では、共通のインターフェイスを共有するクラスまたはオブジェクトを使用して複合パターンを実装し、階層構造を構築できます。実際の例を使用して、JavaScript で複合パターンを実装して使用する方法を見てみましょう。


実装例

単純な形状と形状の複雑な構成 (グループなど) の両方を操作する必要があるグラフィック デザイン アプリケーションを構築しているとします。複合パターンを使用して、この階層を表すことができます。

 // Component interface class Graphic { draw() {} }
 // Leaf class (represents simple shapes) class Circle extends Graphic { constructor() { super(); // Circle-specific properties and methods } draw() { // Draw a circle } }// Composite class (represents groups of shapes) class Group extends Graphic { constructor() { super(); this.graphics = []; } add(graphic) { this.graphics.push(graphic); } draw() { // Draw each graphic in the group this.graphics.forEach((graphic) => graphic.draw()); } }// Usage const circle1 = new Circle(); const circle2 = new Circle(); const group = new Group();group.add(circle1); group.add(circle2);group.draw(); // Draws both circles in the group

この例では、Graphic クラスがコンポーネント インターフェイスとして機能します。 Circle クラスは単純な形状を表し、Group クラスは形状の構成を表します。 Circle クラスと Group クラスは両方とも描画メソッドを実装しているため、レンダリング時にそれらを均一に扱うことができます。


実際の使用例

複合パターンは、次のようなさまざまなシナリオで役立ちます。

  • グラフィックスおよび UI フレームワーク:個々の要素と要素のグループを一貫して扱う必要がある、複雑なユーザー インターフェイスまたはグラフィックス シーンを表すために使用されます。
  • ファイル システム:複合パターンは、ファイルとディレクトリが共通のインターフェイスを共有する階層ファイル システムを表すことができます。
  • 組織構造:企業内の部門や大学内の部門などの組織構造をモデル化するために使用できます。
  • ネストされたコンポーネント:他のコンポーネントを含むことができるコンポーネント (入力フィールドを含むフォームなど) がある場合、複合パターンは構造の管理に役立ちます。


考慮事項

複合パターンを使用する場合は、次の点を考慮してください。

  • 複雑さ:このパターンにより、複雑な構造の操作が簡素化されますが、特に深い階層を扱う場合には、複雑さが生じる可能性もあります。
  • 均一なインターフェイス:一貫性を維持するために、すべてのコンポーネント (リーフとコンポジット) が共通のインターフェイスを共有するようにします。
  • パフォーマンス:実装によっては、複合構造のトラバースがパフォーマンスに影響を与える可能性があるため、必要に応じて最適化してください。


JavaScript のブリッジ パターン

ブリッジ パターンは、オブジェクトの抽象化をその実装から分離する構造設計パターンです。これにより、2 つの間にブリッジを作成し、それらを独立して変更できるようになります。このパターンは、抽象化とその実装の間の永続的なバインディングを回避し、コードをより柔軟で保守しやすくする場合に特に役立ちます。


JavaScript では、抽象化のための抽象インターフェイスとさまざまなプラットフォームや機能のさまざまな具体的な実装を提供するクラスとオブジェクトを使用してブリッジ パターンを実装できます。実際の例を使用して、JavaScript でブリッジ パターンを実装して使用する方法を見てみましょう。


実装例

Web ブラウザやモバイル デバイスなど、さまざまなプラットフォームで図形をレンダリングできる描画アプリケーションを構築しているとします。ブリッジ パターンを使用すると、描画シェイプ (抽象化) をレンダリング ロジック (実装) から分離できます。

 // Abstraction class Shape { constructor(renderer) { this.renderer = renderer; }
 draw() { // Delegating the drawing to the specific renderer this.renderer.renderShape(this); } }// Implementor interface class Renderer { renderShape(shape) {} }// Concrete Implementors class WebRenderer extends Renderer { renderShape(shape) { console.log(`Drawing on the web: ${shape.constructor.name}`); } }class MobileRenderer extends Renderer { renderShape(shape) { console.log(`Drawing on mobile: ${shape.constructor.name}`); } }// Concrete Abstractions (Shapes) class Circle extends Shape { constructor(renderer) { super(renderer); } }class Square extends Shape { constructor(renderer) { super(renderer); } }// Usage const webRenderer = new WebRenderer(); const mobileRenderer = new MobileRenderer();const circle = new Circle(webRenderer); const square = new Square(mobileRenderer);circle.draw(); // Output: Drawing on the web: Circle square.draw(); // Output: Drawing on mobile: Square


この例では、Shape クラスは抽象化 (描画される形状) を表し、Renderer クラスは実装インターフェイス (プラットフォーム固有のレンダリング ロジック) を表します。さまざまな具体的な実装者 (WebRenderer と MobileRenderer) が、それぞれ Web プラットフォームとモバイル プラットフォームのレンダリング ロジックを提供します。 Circle クラスと Square クラスは、形状を表す具体的な抽象化です。


実際の使用例

ブリッジ パターンは、次のようなさまざまなシナリオで役立ちます。

  • プラットフォームの独立性:抽象化と実装を独立して変更できるようにして、複数のプラットフォームのサポートを容易にしたい場合。
  • データベース ドライバー:データベース ドライバーで使用して、データベース固有のコード (実装) をデータベース操作 (抽象化) から分離できます。
  • GUI フレームワーク:グラフィカル ユーザー インターフェイス (GUI) フレームワークでは、ブリッジ パターンは、基礎となるウィンドウ システムからユーザー インターフェイス要素を分離するのに役立ちます。
  • デバイス ドライバー:ハードウェアまたはデバイス ドライバーを扱う場合、このパターンを使用すると、デバイス固有のコードを上位レベルのアプリケーション コードから分離できます。


考慮事項

ブリッジ パターンを使用する場合は、次の点を考慮してください。

  • 複雑さ:パターンは柔軟性を提供しますが、特に多くの抽象化と実装を扱う場合、コードベースの複雑さが増す可能性があります。
  • メンテナンス:抽象化と実装は独立して進化する可能性があるため、変更が発生しても抽象化と実装が常に同期していることを確認します。
  • インターフェイスの設計:抽象化インターフェイスと実装インターフェイスを慎重に設計して、アプリケーションの要件を確実に満たすようにします。


JavaScript のフライウェイト パターン

フライウェイト パターンは、オブジェクトの共通部分を共有することでメモリ消費量を削減し、パフォーマンスを向上させることを目的とした構造設計パターンです。これは、オブジェクトの固有状態 (共有および不変) を外部状態 (固有でコンテキスト依存) から分離することによって実現されます。このパターンは、類似したオブジェクトが多数あり、メモリ フットプリントを最小限に抑えたい場合に特に便利です。


JavaScript では、共有の固有状態と個別の外部状態を表すクラスまたはオブジェクトを使用して Flyweight パターンを実装できます。実際の例を使用して、JavaScript で Flyweight パターンを実装して使用する方法を見てみましょう。


実装例

大量のテキストを表示する必要があるテキスト エディタを開発しているとします。キャラクタごとに個別のオブジェクトを作成する代わりに、同じ固有のプロパティ (フォントやサイズなど) を持つキャラクタ オブジェクトを共有するために Flyweight パターンを使用できます。

 class Character { constructor(char, font, size) { this.char = char; this.font = font; this.size = size; }
 render() { console.log(`Rendering character "${this.char}" in ${this.font}, size ${this.size}`); } }class CharacterFactory { constructor() { this.characters = {}; } getCharacter(char, font, size) { const key = `${char}-${font}-${size}`; if (!this.characters[key]) { this.characters[key] = new Character(char, font, size); } return this.characters[key]; } }// Usage const factory = new CharacterFactory();const charA1 = factory.getCharacter('A', 'Arial', 12); const charA2 = factory.getCharacter('A', 'Arial', 12); const charB = factory.getCharacter('B', 'Times New Roman', 14);charA1.render(); // Output: Rendering character "A" in Arial, size 12 charA2.render(); // Output: Rendering character "A" in Arial, size 12 (shared instance) charB.render(); // Output: Rendering character "B" in Times New Roman, size 14

この例では、Character クラスは、文字自体、フォント、サイズなどの固有のプロパティを持つ個々の文字を表します。 CharacterFactory クラスは、同じ固有プロパティを持つ文字が重複するのではなく共有されることを保証します。


実際の使用例

フライウェイト パターンは、次のようなさまざまなシナリオで役立ちます。

  • テキスト処理:大量のテキストを処理する場合、共通の文字、フォント、またはその他のテキスト関連プロパティを共有することで、メモリ消費を大幅に削減できます。
  • ゲーム開発:ゲーム開発では、テクスチャやマテリアルなど、特定の特性を共有するオブジェクトのレンダリングを最適化するために使用されます。
  • ユーザー インターフェイス (UI):共有スタイル、フォント、またはアイコンを持つ UI コンポーネントに適用して、リソースの使用量を最小限に抑えることができます。
  • キャッシュ:パターンを使用して、頻繁に使用されるオブジェクトまたはデータをキャッシュし、パフォーマンスを向上させることができます。


考慮事項

フライウェイト パターンを使用する場合は、次の点を考慮してください。

  • 固有状態の識別:オブジェクトの固有状態を慎重に識別し、外部状態から分離します。固有の状態は共有される必要がありますが、外部の状態は変化する可能性があります。
  • スレッド セーフ:アプリケーションがマルチスレッドの場合は、Flyweight オブジェクトがスレッド セーフであることを確認してください。
  • メモリとパフォーマンス: Flyweight パターンはメモリ使用量を削減しますが、ルックアップと共有インスタンスが必要なため、パフォーマンスにわずかなオーバーヘッドが発生する可能性があります。


JavaScript のオブザーバー パターン

オブザーバー パターンは、オブジェクト間に 1 対多の依存関係を確立する動作設計パターンです。これにより、1 つのオブジェクト (サブジェクトまたはオブザーバブル) がその状態やデータの変化を複数のオブザーバー (リスナー) に通知できるようになります。このパターンは、分散イベント処理システムの実装によく使用され、1 つのオブジェクトの状態変化が他の依存オブジェクトのアクションをトリガーします。

JavaScript では、カスタム クラスや、イベント リスナーやaddEventListenerメソッドなどの組み込み機能を使用して、オブザーバー パターンを実装できます。実際の例を使用して、JavaScript で Observer パターンを実装して使用する方法を見てみましょう。


実装例

カスタムオブザーバーパターン

天気アプリケーションを構築していて、気象条件が変化したときに UI のさまざまな部分を更新したいとします。 Observer パターンのカスタム実装を使用できます。

 class WeatherStation { constructor() { this.observers = []; }
 addObserver(observer) { this.observers.push(observer); } removeObserver(observer) { const index = this.observers.indexOf(observer); if (index !== -1) { this.observers.splice(index, 1); } } notifyObservers() { this.observers.forEach((observer) => { observer.update(this); }); } setWeatherData(weatherData) { this.weatherData = weatherData; this.notifyObservers(); } }class WeatherDisplay { update(weatherStation) { console.log(`Current weather: ${weatherStation.weatherData}`); } }// Usage const weatherStation = new WeatherStation(); const display1 = new WeatherDisplay(); const display2 = new WeatherDisplay();weatherStation.addObserver(display1); weatherStation.addObserver(display2);weatherStation.setWeatherData('Sunny'); // Both displays update with the new weather data

この例では、WeatherStation は、気象データが変化したときに観測者 (表示オブジェクト) に通知するサブジェクトとして機能します。オブザーバーは、addObserver メソッドを使用してサブジェクトをサブスクライブし、変更に反応するために update メソッドを実装します。


イベントリスナーの使用

JavaScript には、イベント リスナーを使用してオブザーバー パターンを実装する組み込みの方法も用意されています。

 class NewsPublisher { constructor() { this.subscribers = []; }
 subscribe(subscriber) { this.subscribers.push(subscriber); } unsubscribe(subscriber) { const index = this.subscribers.indexOf(subscriber); if (index !== -1) { this.subscribers.splice(index, 1); } } publishNews(news) { this.subscribers.forEach((subscriber) => { subscriber(news); }); } }// Usage const publisher = new NewsPublisher();const subscriber1 = (news) => { console.log(`Subscriber 1 received news: ${news}`); };const subscriber2 = (news) => { console.log(`Subscriber 2 received news: ${news}`); };publisher.subscribe(subscriber1); publisher.subscribe(subscriber2);publisher.publishNews('Breaking News: Important Announcement');

この例では、NewsPublisher がサブジェクトとして機能し、subscribe メソッドを使用して購読者 (関数) が追加されます。 pubNews メソッドは、ニュースで関数を呼び出すことによって購読者に通知します。


実際の使用例

オブザーバー パターンは、次のようなさまざまなシナリオで役立ちます。

  • ユーザー インターフェイス: UI コンポーネントがユーザーのアクションに反応するグラフィカル ユーザー インターフェイス (GUI) にイベント処理を実装します。
  • パブリッシュ/サブスクライブ システム:メッセージやイベントを複数のサブスクライバーに配信するためのパブリッシュ/サブスクライブ システムを構築します。
  • モデル ビュー コントローラー (MVC):モデル (データ) をビュー (UI) から分離し、モデルの変更についてビューに通知します。
  • カスタム イベント処理:状態の変更と対話を管理するためのカスタム イベント駆動型システムを作成します。

考慮事項

オブザーバー パターンを使用する場合は、次の点を考慮してください。

  • メモリ管理:オブザーバーがサブジェクトへの参照を保持する場合は、メモリ リークに注意してください。オブザーバーが不要になった場合は、オブザーバーを適切に削除してください。
  • 通知の順序:シナリオによっては、オブザーバーに通知される順序が重要になる場合があります。注文がアプリケーションの要件を満たしていることを確認してください。
  • イベント処理:組み込みのイベント処理メカニズムを使用する場合は、DOM 内のイベントの伝播とバブリング (該当する場合) に注意してください。


JavaScript の戦略パターン

戦略パターンは、交換可能なアルゴリズムのファミリーを定義し、それぞれをカプセル化し、交換可能にすることを可能にする動作設計パターンです。これにより、クライアントは実行時に適切なアルゴリズムを動的に選択できるようになります。このパターンは、アルゴリズムの動作をそれを使用するコンテキストから分離することにより、柔軟性と再利用性を促進します。

JavaScript では、さまざまな戦略を表すオブジェクトまたは関数と、これらの戦略を切り替えることができるコンテキスト オブジェクトを使用して戦略パターンを実装できます。実際の例を使用して、JavaScript で戦略パターンを実装して使用する方法を見てみましょう。


実装例

電子商取引アプリケーションを開発していて、さまざまなタイプの顧客に対する割引を計算したいとします。戦略パターンを使用して、割引戦略をカプセル化できます。

 // Discount Strategies const regularCustomerDiscount = (amount) => amount * 0.1; // 10% discount const premiumCustomerDiscount = (amount) => amount * 0.2; // 20% discount
 // Context class ShoppingCart { constructor(discountStrategy) { this.items = []; this.discountStrategy = discountStrategy; } addItem(item) { this.items.push(item); } calculateTotal() { const subtotal = this.items.reduce((total, item) => total + item.price, 0); return subtotal - this.discountStrategy(subtotal); } }// Usage const regularCustomerCart = new ShoppingCart(regularCustomerDiscount); const premiumCustomerCart = new ShoppingCart(premiumCustomerDiscount);regularCustomerCart.addItem({ name: 'Item 1', price: 50 }); premiumCustomerCart.addItem({ name: 'Item 2', price: 100 });console.log(`Regular Customer Total: $${regularCustomerCart.calculateTotal()}`); // Output: $45 (after 10% discount) console.log(`Premium Customer Total: $${premiumCustomerCart.calculateTotal()}`); // Output: $80 (after 20% discount)

この例では、2 つの割引戦略を関数として定義します (normalCustomerDiscount と premiumCustomerDiscount)。 ShoppingCart クラスは、割引戦略をパラメーターとして受け取り、選択された戦略に基づいて合計価格を計算します。


実際の使用例

戦略パターンは、次のようなさまざまなシナリオで役立ちます。

  • アルゴリズムの選択:アルゴリズム ファミリからアルゴリズムを動的に選択する必要がある場合。
  • 構成と設定:並べ替えアルゴリズムやデータ ストレージ戦略など、さまざまな動作オプションを使用してアプリケーションを構成します。
  • カスタマイズ可能な動作:さまざまな戦略を提供することで、ユーザーがアプリケーションの動作をカスタマイズおよび拡張できるようにします。
  • テストとモック:単体テストでは、戦略パターンを使用して、テスト用のコンポーネントのモック実装を提供できます。


考慮事項

戦略パターンを使用する場合は、次の点を考慮してください。

  • 明確な分離:クリーンで保守可能なコードベースを維持するために、コンテキストと戦略を明確に分離します。
  • 動的切り替え:戦略を動的に切り替える機能は、このパターンの重要な機能です。設計がこの柔軟性をサポートしていることを確認してください。
  • 戦略の初期化:正しい戦略が使用されるように、戦略がどのように初期化されてコンテキストに渡されるかに注意してください。


JavaScript のコマンド パターン

コマンド パターンは、リクエストまたは単純な操作をスタンドアロン オブジェクトに変換する動作設計パターンです。これにより、さまざまなリクエストを持つオブジェクトをパラメータ化したり、リクエストの実行を遅延またはキューに入れたり、取り消し可能な操作をサポートしたりすることができます。このパターンでは、リクエストの送信者と受信者が切り離され、コードの拡張と保守が容易になります。


JavaScript では、コマンドとそれらのコマンドを実行する呼び出し元を表すオブジェクトまたはクラスを使用してコマンド パターンを実装できます。実際の例を使用して、JavaScript でコマンド パターンを実装して使用する方法を見てみましょう。


実装例

スマート ホーム用のリモート コントロール アプリケーションを開発していて、さまざまなデバイスを制御する柔軟な方法を作成したいとします。


コマンド パターンを使用できます。

 // Command interface class Command { execute() {} }
 // Concrete Commands class LightOnCommand extends Command { constructor(light) { super(); this.light = light; } execute() { this.light.turnOn(); } }class LightOffCommand extends Command { constructor(light) { super(); this.light = light; } execute() { this.light.turnOff(); } }// Receiver (Device) class Light { turnOn() { console.log('Light is on.'); } turnOff() { console.log('Light is off.'); } }// Invoker (Remote Control) class RemoteControl { constructor() { this.commands = []; } addCommand(command) { this.commands.push(command); } executeCommands() { this.commands.forEach((command) => { command.execute(); }); } }// Usage const livingRoomLight = new Light(); const kitchenLight = new Light();const livingRoomLightOn = new LightOnCommand(livingRoomLight); const livingRoomLightOff = new LightOffCommand(livingRoomLight); const kitchenLightOn = new LightOnCommand(kitchenLight); const kitchenLightOff = new LightOffCommand(kitchenLight);const remoteControl = new RemoteControl();remoteControl.addCommand(livingRoomLightOn); remoteControl.addCommand(kitchenLightOff);remoteControl.executeCommands(); // Output: "Light is on." (for living room) // Output: "Light is off." (for kitchen)

この例では、コマンド パターンを使用して、照明のオンとオフのアクションをカプセル化します。 RemoteControl は呼び出し側として機能し、具体的なコマンド (LightOnCommand や LightOffCommand など) が実行されるアクションをカプセル化します。


実際の使用例

コマンド パターンは、次のようなさまざまなシナリオで役立ちます。

  • GUI アプリケーション:通常、グラフィカル ユーザー インターフェイス (GUI) で元に戻す機能とやり直し機能を実装するために使用され、各ユーザー アクションがコマンドとしてカプセル化されます。
  • リモート コントロール システム:スマート デバイスのリモート コントロール アプリケーションでは、さまざまなコマンドを使用してさまざまなデバイスを制御する柔軟な方法を提供します。
  • バッチ処理:異なるパラメーターまたは設定を使用して一連のリクエストまたはタスクをキューに入れて実行する必要がある場合。
  • トランザクション管理:データベース システムでは、データベース操作をコマンドとしてカプセル化し、トランザクション動作をサポートするために使用できます。


考慮事項

コマンド パターンを使用する場合は、次の点を考慮してください。

  • コマンドの抽象化:コマンドが適切に抽象化され、単一のアクションまたは操作がカプセル化されていることを確認します。
  • 元に戻すおよびやり直し:元に戻すおよびやり直しの機能が必要な場合は、コマンドの反転をサポートするために必要なメカニズムを実装します。
  • 複雑さ:特に使用可能なコマンドが多数あるシナリオでは、複数のコマンド クラスを作成することによって生じる複雑さに注意してください。


JavaScript の状態パターン

状態パターンは、内部状態が変化したときにオブジェクトの動作を変更できるようにする動作設計パターンです。状態を別のクラスとしてカプセル化し、動作を現在の状態オブジェクトに委任します。このパターンは、複雑な状態遷移の管理に役立ち、「オープン-クローズ」原則を促進するため、既存のコードを変更せずに新しい状態を簡単に追加できます。


JavaScript では、状態を表すクラスと、その動作を現在の状態に委任するコンテキスト オブジェクトを使用して状態パターンを実装できます。実際の例を使用して、JavaScript で状態パターンを実装して使用する方法を見てみましょう。


実装例

さまざまな製品を販売する自動販売機を開発しているとします。自動販売機の動作は、「準備完了」、「調剤中」、「売り切れ」などの現在の状態によって異なります。状態パターンを使用して、この動作をモデル化できます。

 // State interface class VendingMachineState { insertMoney() {} ejectMoney() {} selectProduct() {} dispenseProduct() {} }
 // Concrete States class ReadyState extends VendingMachineState { constructor(machine) { super(); this.machine = machine; } insertMoney() { console.log('Money inserted.'); this.machine.setState(this.machine.getDispensingState()); } selectProduct() { console.log('Please insert money first.'); } }class DispensingState extends VendingMachineState { constructor(machine) { super(); this.machine = machine; } dispenseProduct() { console.log('Product dispensed.'); this.machine.setState(this.machine.getReadyState()); } }class VendingMachine { constructor() { this.readyState = new ReadyState(this); this.dispensingState = new DispensingState(this); this.currentState = this.readyState; } setState(state) { this.currentState = state; } getReadyState() { return this.readyState; } getDispensingState() { return this.dispensingState; } insertMoney() { this.currentState.insertMoney(); } selectProduct() { this.currentState.selectProduct(); } dispenseProduct() { this.currentState.dispenseProduct(); } }// Usage const vendingMachine = new VendingMachine();vendingMachine.selectProduct(); // Output: "Please insert money first." vendingMachine.insertMoney(); // Output: "Money inserted." vendingMachine.dispenseProduct(); // Output: "Product dispensed."

この例では、自動販売機の動作を管理するために状態パターンが使用されます。 「準備完了」や「調剤中」などの状態は別のクラスとして表され、コンテキスト (自動販売機) がその動作を現在の状態に委任します。


実際の使用例

状態パターンは、次のようなさまざまなシナリオで役立ちます。

  • ワークフロー管理:注文処理や承認ワークフローなど、さまざまな状態や遷移を持つアプリケーションのワークフローを管理します。
  • ゲーム開発: 「アイドル」、「攻撃中」、「防御中」などのゲーム状態に基づいて変化するゲーム キャラクターの動作を実装します。
  • ユーザー インターフェイス (UI):さまざまなユーザー インタラクションやアプリケーションの状態に基づいて UI コンポーネントの動作を処理します。
  • 有限状態マシン:解析、検証、またはネットワーク通信用の有限状態マシンを実装します。


考慮事項

状態パターンを使用する場合は、次の点を考慮してください。

  • 状態遷移:状態遷移が明確に定義され、状態がその動作を効果的にカプセル化していることを確認します。
  • コンテキスト管理:コンテキストの状態遷移を管理し、動作が現在の状態に正しく委譲されるようにします。
  • 複雑さ:複雑なアプリケーションで多くの状態や遷移を処理するときに生じる可能性がある複雑さに注意してください。


JavaScript における責任連鎖パターン

Chain of Responsibility パターンは、リクエストを処理するオブジェクトのチェーンを構築するのに役立つ動作設計パターンです。チェーン内の各オブジェクトには、リクエストを処理するか、チェーン内の次のオブジェクトにリクエストを渡す機会があります。これにより、リクエストの送信者と受信者が切り離され、複数のハンドラーがチェーン内に存在できるようになります。このパターンでは、クライアント コードに影響を与えることなくハンドラーを追加または変更できるため、柔軟性と拡張性が向上します。


JavaScript では、ハンドラーとリクエストを開始するクライアントを表すオブジェクトまたはクラスを使用して、責任連鎖パターンを実装できます。各ハンドラーには、チェーン内の次のハンドラーへの参照があります。実際の例を使用して、JavaScript で責任連鎖パターンを実装して使用する方法を見てみましょう。


実装例

注文処理システムを開発していて、注文を合計金額に基づいて処理したいとします。責任チェーン パターンを使用して、特定の価格範囲内の注文の処理をそれぞれが担当するハンドラーのチェーンを作成できます。

 // Handler interface class OrderHandler { constructor() { this.nextHandler = null; }
 setNextHandler(handler) { this.nextHandler = handler; } handleOrder(order) { if (this.canHandleOrder(order)) { this.processOrder(order); } else if (this.nextHandler) { this.nextHandler.handleOrder(order); } else { console.log('No handler can process this order.'); } } canHandleOrder(order) {} processOrder(order) {} }// Concrete Handlers class SmallOrderHandler extends OrderHandler { canHandleOrder(order) { return order.amount <= 100; } processOrder(order) { console.log(`Processing small order for ${order.amount}`); } }class MediumOrderHandler extends OrderHandler { canHandleOrder(order) { return order.amount <= 500; } processOrder(order) { console.log(`Processing medium order for ${order.amount}`); } }class LargeOrderHandler extends OrderHandler { canHandleOrder(order) { return order.amount > 500; } processOrder(order) { console.log(`Processing large order for ${order.amount}`); } }// Client class Order { constructor(amount) { this.amount = amount; } }// Usage const smallOrderHandler = new SmallOrderHandler(); const mediumOrderHandler = new MediumOrderHandler(); const largeOrderHandler = new LargeOrderHandler();smallOrderHandler.setNextHandler(mediumOrderHandler); mediumOrderHandler.setNextHandler(largeOrderHandler);const order1 = new Order(80); const order2 = new Order(250); const order3 = new Order(600);smallOrderHandler.handleOrder(order1); // Output: "Processing small order for 80" smallOrderHandler.handleOrder(order2); // Output: "Processing medium order for 250" smallOrderHandler.handleOrder(order3); // Output: "Processing large order for 600"

この例では、責任連鎖パターンを使用して、さまざまな金額の注文を処理します。 SmallOrderHandler、MediumOrderHandler、LargeOrderHandler などのハンドラーはそれぞれ、注文の金額に基づいて注文を処理できるかどうかを決定します。可能であれば、処理します。それ以外の場合は、チェーン内の次のハンドラーに注文を渡します。


実際の使用例

責任連鎖パターンは、次のようなさまざまなシナリオで役立ちます。

  • リクエスト処理:各ミドルウェアまたはハンドラーがリクエストを処理または転送できる HTTP リクエスト処理パイプラインを管理します。
  • ロギングとエラー処理:各ハンドラーが特定の種類のログ メッセージまたはエラー状態を担当し、構造化された方法でログ メッセージまたはエラーを処理します。
  • イベント処理:イベント ドリブン システムでは、このパターンを使用して複数のサブスクライバでイベントを処理でき、各サブスクライバがイベントを処理またはフィルタリングできます。
  • 承認と認証:各ハンドラーがリクエストの特定の側面を検証することで、認証と承認のチェックを順番に実装します。


考慮事項

責任連鎖パターンを使用する場合は、次の点を考慮してください。

  • チェーン構成:ハンドラーが正しい順序でセットアップされ、チェーンが適切に構成されていることを確認します。
  • ハンドラーの責任:各ハンドラーは明確な責任を持つ必要があり、他のハンドラーの責任と重複すべきではありません。
  • デフォルトの処理:チェーン内のハンドラーがリクエストを処理できない場合のロジックを含めます。


JavaScript の訪問者パターン

Visitor パターンは、アルゴリズムを、アルゴリズムが動作するオブジェクト構造から分離できるようにする動作設計パターンです。クラスを変更せずにオブジェクトに新しい操作を追加する方法を提供し、複雑なオブジェクト階層の機能を簡単に拡張できます。このパターンは、個別の要素のセットがあり、コードを変更せずにそれらに対してさまざまな操作を実行したい場合に特に便利です。


JavaScript では、オブジェクト構造内の要素を訪問する訪問者を表す関数またはクラスを使用して訪問者パターンを実装できます。実際の例を使用して、JavaScript で訪問者パターンを実装して使用する方法を見てみましょう。


実装例

記事、画像、ビデオなどのさまざまな種類のコンテンツ要素を含むコンテンツ管理システムを開発しているとします。これらの要素に対して、クラスを変更せずにレンダリングやエクスポートなどのさまざまな操作を実行したいと考えています。訪問者パターンを使用できます。

 // Element interface class ContentElement { accept(visitor) {} }
 // Concrete Elements class Article extends ContentElement { accept(visitor) { visitor.visitArticle(this); } }class Image extends ContentElement { accept(visitor) { visitor.visitImage(this); } }class Video extends ContentElement { accept(visitor) { visitor.visitVideo(this); } }// Visitor interface class Visitor { visitArticle(article) {} visitImage(image) {} visitVideo(video) {} }// Concrete Visitors class RendererVisitor extends Visitor { visitArticle(article) { console.log(`Rendering article: ${article.title}`); } visitImage(image) { console.log(`Rendering image: ${image.caption}`); } visitVideo(video) { console.log(`Rendering video: ${video.title}`); } }class ExportVisitor extends Visitor { visitArticle(article) { console.log(`Exporting article: ${article.title}`); } visitImage(image) { console.log(`Exporting image: ${image.caption}`); } visitVideo(video) { console.log(`Exporting video: ${video.title}`); } }// Usage const elements = [new Article('Article 1'), new Image('Image 1'), new Video('Video 1')]; const renderer = new RendererVisitor(); const exporter = new ExportVisitor();elements.forEach((element) => { element.accept(renderer); element.accept(exporter); });

この例では、Article、Image、Video などのコンテンツ要素があり、それらのクラスを変更せずにレンダリングおよびエクスポート操作を実行したいと考えています。これを実現するには、要素にアクセスして必要な操作を実行する RendererVisitor や ExportVisitor などのビジター クラスを実装します。


実際の使用例

訪問者パターンは、次のようなさまざまなシナリオで役立ちます。

  • ドキュメント処理: HTML や XML などのドキュメント内の要素を処理し、さまざまな訪問者が解析、レンダリング、または変換操作を実行できます。
  • コンパイラーの設計:コンパイラーでは、訪問者は、型チェック、最適化、コード生成などのさまざまな目的で、プログラミング言語の抽象構文ツリー (AST) をたどって分析できます。
  • データ構造:ツリーやグラフなどの複雑なデータ構造を操作する場合、訪問者はデータの構造や内容を横断して操作できます。
  • レポートと分析:レポート システムでは、訪問者はレポートを作成したり、データ分析を実行したり、データセットから特定の情報を抽出したりできます。


考慮事項

訪問者パターンを使用する場合は、次の点を考慮してください。

  • 拡張性:このパターンにより、既存の要素を変更せずに新しい訪問者クラスを作成することで、新しい操作を簡単に追加できます。
  • 複雑さ:パターンによって、特に単純なオブジェクト構造の場合、さらに複雑になる可能性があることに注意してください。
  • カプセル化:要素がその状態を適切にカプセル化し、訪問者メソッド経由でアクセスできるようにします。


結論

JavaScript のデザイン パターンの包括的な調査では、開発者が柔軟で保守しやすく効率的なコードを作成できるようにするさまざまなパターンを詳しく掘り下げました。各設計パターンは特定の問題に対処し、一般的なソフトウェア設計の課題に対する洗練されたソリューションを提供します。


私たちはデザイン パターンの基本概念を理解することから始め、デザイン パターンを作成パターン、構造パターン、動作パターンの 3 つの主要なグループに分類しました。各カテゴリ内で、人気のあるデザイン パターンを調査し、JavaScript での実際の実装を紹介しました。


ここで取り上げた主要な設計パターンを簡単に要約します。

  • 作成パターン:これらのパターンは、クラスの単一インスタンスを保証するためのシングルトン パターン、柔軟なファクトリでオブジェクトを作成するためのファクトリ パターンおよび抽象ファクトリ パターン、複雑なオブジェクトを段階的に構築するためのビルダー パターン、クローン作成のためのプロトタイプ パターンなど、オブジェクト作成メカニズムに焦点を当てています。オブジェクト、およびオブジェクトを効率的に再利用するためのオブジェクト プール パターン。


  • 構造パターン:これらのパターンはオブジェクトの構成を扱い、より単純なコンポーネントから複雑な構造を構築する方法を提供します。インターフェイスを適応させるためのアダプター パターン、オブジェクトに動作を動的に追加するためのデコレーター パターン、オブジェクトへのアクセスを制御するためのプロキシ パターン、オブジェクトをツリー構造に構成するための複合パターン、実装から抽象化を分離するためのブリッジ パターン、およびフライウェイトを検討しました。共通の状態を共有することでメモリ使用量を最小限に抑えるパターン。


  • 行動パターン:これらのパターンは、オブジェクト間の相互作用とコミュニケーションに関係します。分散イベント処理システムを実装するためのオブザーバー パターン、交換可能なアルゴリズムをカプセル化するためのストラテジー パターン、リクエストをスタンドアロン オブジェクトに変換するためのコマンド パターン、内部状態に基づいてオブジェクトの動作を管理するための状態パターン、システムを構築するための責任連鎖パターンについて説明しました。リクエストを処理するためのハンドラーのチェーンと、オブジェクト構造からアルゴリズムを分離するための訪問者パターン。


デザイン パターンは、開発者ツールキットの貴重なツールであり、スケーラブルで保守可能なコードベースの作成を可能にします。これらのパターンを理解して JavaScript プロジェクトに適用すると、より効率的で適応性があり、堅牢なソフトウェアを作成できるようになります。


デザイン パターンは万能のソリューションではなく、その適用性はプロジェクトの特定の要件と課題によって異なることに注意してください。最良の結果を得るために、いつどのように適用するかを慎重に検討してください。


JavaScript 開発者として成長し続けるにつれて、これらの設計パターンをマスターすると、自信と創造性を持って複雑なソフトウェア設計の課題に取り組むことができるようになります。 Web アプリケーション、ゲーム エンジン、その他のソフトウェアを構築している場合でも、デザイン パターンは、エレガントで保守しやすいコードを作成する際の味方になります。コーディングを楽しんでください!


ここでも公開されています。