「労働者が自分の仕事をうまくやりたいなら、まず自分の道具を研ぎ澄まさなければなりません。」 - 孔子、「論語。陸霊公」
表紙 > プログラミング > プロキシ設計パターン

プロキシ設計パターン

2024 年 11 月 6 日に公開
ブラウズ:146

Proxy Design Pattern

以前のブログでは、オブジェクト作成メカニズムを扱うさまざまな作成デザイン パターンを検討しました。ここで、構造設計パターンについて詳しく見ていきましょう。オブジェクトとクラスを柔軟かつ効率的に保ちながら、より大きな構造を形成するためにどのように構成されるかに焦点を当てます。プロキシ設計パターンから始めましょう

JavaScript のプロキシ設計パターン

プロキシ デザイン パターンは、別のオブジェクトを表すオブジェクトを提供する構造的なデザイン パターンです。これは、実際のオブジェクトへのアクセスを制御する仲介者として機能し、元のオブジェクトのコードを変更することなく、遅延初期化、ロギング、アクセス制御、キャッシュなどの追加動作を追加します。

JavaScript では、プロキシは Proxy オブジェクトによって提供される組み込み機能であり、プロパティ アクセス、割り当て、関数呼び出しなどの基本的な操作のカスタム動作を定義できます。

プロキシ パターンが必要になるのはどのような場合ですか?

プロキシ パターンは、次の場合に特に役立ちます。

  • 遅延初期化: リソースを大量に使用するオブジェクトの作成を、必要になるまで遅らせたいとします。
  • アクセス制御: オブジェクトへのアクセスを制御する必要があります。たとえば、無許可のアクセスを制限したり、条件に基づいて操作を制限したりする必要があります。
  • Logging: オブジェクトに対するアクション (プロパティ アクセスやメソッド呼び出しなど) をログに記録します。
  • キャッシュ: 冗長な計算を避けるために、負荷の高い操作の結果をキャッシュしたいとします。

プロキシ パターンのコンポーネント

  1. Subject: 実際のオブジェクトとプロキシの両方に共通の操作を定義するインターフェイス。
  2. RealSubject: 実際の作業を実行する実際のオブジェクト。
  3. Proxy: RealSubject へのアクセスを制御する仲介者。

類推:

ゲストに見せたい大きな絵画があるが、倉庫から取り出すのに時間がかかると想像してください (重くて運ぶのに時間がかかるため)。毎回それを待つのではなく、実際の絵が取り込まれるのを待っている間に、絵の小さなポストカード画像を使用してすぐに見せることにしました。

この例えでは:

  • 大きな絵は実物です(読み込みに時間がかかる画像など)。
  • ポストカードは代理です (実際のオブジェクトの準備ができるまでの代わりとなる軽量の代替品)。
  • 本物の絵の準備ができたら、実際の絵をゲストに見せます。

現実世界の例え:

不動産業者は代理店だと考えてください。家を購入したいと思ったとき、すぐにすべての家を訪問する(実物を積み込む)わけではありません。代わりに、不動産業者(代理人)が最初に写真と説明を見せます。購入する準備ができたとき (つまり、display() を呼び出したとき) にのみ、エージェントは自宅訪問を手配します (実際のオブジェクトをロードします)。

実際の例: 画像の読み込み (仮想プロキシ)

Web アプリケーションでの画像の読み込みの例を使用してみましょう。ここでは、ユーザーが要求するまで画像の読み込みを遅らせます (遅延読み込み)。プロキシは、実際のイメージがロードされるまでプレースホルダとして機能します。

ここでは、JavaScript でプロキシ設計パターンを実装する方法を説明します。

例: 画像ロードのプロキシ

// Step 1: The real object
class RealImage {
  constructor(filename) {
    this.filename = filename;
    this.loadImageFromDisk();
  }

  loadImageFromDisk() {
    console.log(`Loading ${this.filename} from disk...`);
  }

  display() {
    console.log(`Displaying ${this.filename}`);
  }
}

// Step 2: The proxy object
class ProxyImage {
  constructor(filename) {
    this.realImage = null; // no real image yet
    this.filename = filename;
  }

  display() {
    if (this.realImage === null) {
      // load the real image only when needed
      this.realImage = new RealImage(this.filename);
    }
    this.realImage.display(); // display the real image
  }
}

// Step 3: Using the proxy to display the image
const image = new ProxyImage("photo.jpg");
image.display(); // Loads and displays the image
image.display(); // Just displays the image (already loaded)

説明:

1)。実像:

  • RealImageクラスは実際のイメージを表します。
  • ファイル名を入力として受け取り、ディスクからイメージをロードする時間のかかるプロセス (loadImageFromDisk メソッドで示される) をシミュレートします。
  • ロードされると、display メソッドを使用して画像が表示されます。

2)。プロキシ画像:

  • ProxyImage クラスは RealImage の代わりとして機能します。実際のイメージはすぐにはロードされません。
  • 実際のイメージへの参照を保持します (ただし、実際のイメージがまだロードされていないため、最初は null です)。
  • プロキシ上で display メソッドを呼び出すと、実際のイメージがロードされているかどうかがチェックされます。そうでない場合は、最初にロードしてから表示します。

3)。使用法:

  • ProxyImage のインスタンスを作成するとき、実際のイメージはまだロードされていません (リソースを大量に消費するため)。
  • 最初にdisplayが呼び出されるとき、プロキシは(RealImageクラスを使用して)画像をロードし、それを表示します。
  • 2 回目の表示呼び出しでは、実際の画像は既にロードされているため、再ロードせずに画像のみを表示します。

組み込みの Proxy オブジェクト

ES6 プロキシは、ターゲットとハンドラーを引数として受け入れるプロキシ コンストラクターで構成されています

const proxy = new Proxy(target, handler)

ここで、ターゲットはプロキシが適用されるオブジェクトを表し、ハンドラーはプロキシの動作を定義する特別なオブジェクトです。

ハンドラー オブジェクトには、トラップ メソッド (apply、get、set、has など) と呼ばれる事前定義された名前を持つ一連のオプションのメソッドが含まれています。これらのメソッドは、対応する操作がプロキシ インスタンスで実行されるときに自動的に呼び出されます。

組み込みプロキシを使用して電卓を実装してこれを理解しましょう

// Step 1: Define the Calculator class with prototype methods
class Calculator {
  constructor() {
    this.result = 0;
  }

  // Prototype method to add numbers
  add(a, b) {
    this.result = a   b;
    return this.result;
  }

  // Prototype method to subtract numbers
  subtract(a, b) {
    this.result = a - b;
    return this.result;
  }

  // Prototype method to multiply numbers
  multiply(a, b) {
    this.result = a * b;
    return this.result;
  }

  // Prototype method to divide numbers
  divide(a, b) {
    if (b === 0) throw new Error("Division by zero is not allowed.");
    this.result = a / b;
    return this.result;
  }
}

// Step 2: Create a proxy handler to intercept operations
const handler = {
  // Intercept 'get' operations to ensure access to prototype methods
  get(target, prop, receiver) {
    if (prop in target) {
      console.log(`Accessing property: ${prop}`);
      return Reflect.get(target, prop, receiver); // Access property safely
    } else {
      throw new Error(`Property "${prop}" does not exist.`);
    }
  },

  // Intercept 'set' operations to prevent mutation
  set(target, prop, value) {
    throw new Error(`Cannot modify property "${prop}". The calculator is immutable.`);
  }
};

// Step 3: Create a proxy instance that inherits the Calculator prototype
const calculator = new Calculator(); // Original calculator object
const proxiedCalculator = new Proxy(calculator, handler); // Proxy wrapping the calculator

// Step 4: Use the proxy instance
try {
  console.log(proxiedCalculator.add(5, 3)); // Output: 8
  console.log(proxiedCalculator.multiply(4, 2)); // Output: 8
  console.log(proxiedCalculator.divide(10, 2)); // Output: 5

  // Attempt to access prototype directly through proxy
  console.log(proxiedCalculator.__proto__ === Calculator.prototype); // Output: true

  // Attempt to modify a property (should throw an error)
  proxiedCalculator.result = 100; // Error: Cannot modify property "result".
} catch (error) {
  console.error(error.message); // Output: Cannot modify property "result". The calculator is immutable.
}

この方法でプロキシを使用する最良の部分:

  • プロキシ オブジェクトは、元の Calculator クラスのプロトタイプを継承します。
  • 突然変異は、プロキシの設定されたトラップによって回避されます。

コードの説明

1)。 プロトタイプの継承:

  • プロキシは **Calculator **クラスの元のプロトタイプに干渉しません。
  • これは、proxiedCalculator.proto === Calculator.prototype かどうかをチェックすることで確認されます。結果は true.
  • になります。

2)。 getOperations の処理:

  • 取得トラップは、プロキシ オブジェクトのプロパティ アクセスをインターセプトします。
  • Reflect.get を使用して、元のオブジェクトのプロパティとメソッドに安全にアクセスします。

3)。 突然変異の防止:

ターゲット オブジェクトのプロパティを変更しようとするたびに、設定されたトラップはエラーをスローします。これにより不変性が保証されます。

4)。 プロキシを介したプロトタイプ メソッドの使用:

プロキシを使用すると、元のオブジェクトのプロトタイプで定義されている加算、減算、乗算、除算などのメソッドにアクセスできます。

ここで注意すべき重要なポイントは次のとおりです:

  • プロトタイプの継承を保持します: プロキシはすべてのプロトタイプ メソッドへのアクセスを保持し、元の電卓と同様に動作します。
  • 突然変異の防止: 設定されたトラップにより、電卓オブジェクトの内部状態が予期せず変更されないことが保証されます。
  • プロパティとメソッドへの安全なアクセス: get トラップにより、有効なプロパティのみがアクセスされることが保証され、堅牢性が向上します。

ここまで進んだ場合は、忘れずに「いいね!」を押して、質問や意見があれば下にコメントを残してください。あなたのフィードバックは私にとってとても大切なものです。ぜひご意見をお待ちしております!

リリースステートメント この記事は次の場所に転載されています: https://dev.to/srishtikprasad/proxy-design-pattern-4mm?1 侵害がある場合は、[email protected] に連絡して削除してください。
最新のチュートリアル もっと>

免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。

Copyright© 2022 湘ICP备2022001581号-3