TypeScriptでデコレーターを使って関数の実行回数を制限する方法

TypeScriptにおけるデコレーター機能は、コードの再利用性を高め、複雑なロジックを簡潔に記述するために非常に役立ちます。特に、関数の実行回数を制限する場合など、デコレーターを使うことでコードのメンテナンス性が向上します。例えば、ある関数を一定の回数以上実行させたくない場合、その制限をデコレーターとして組み込むことで、他の関数に対しても同じ制約を簡単に適用できます。本記事では、デコレーターを活用して関数の実行回数を制限する方法を具体例を交えて解説します。

目次

デコレーターとは何か

デコレーターとは、TypeScriptにおいてクラスや関数、プロパティに対して追加の機能を付加するための特殊な構文です。デコレーターは、オブジェクト指向プログラミングの概念であるアノテーションに似た機能で、コードの再利用やカスタマイズを行いやすくするために用いられます。

デコレーターは「関数として定義され」、その関数が対象のクラスやメソッドに適用されることで、その動作を変更したり拡張することができます。TypeScriptでは、@記号を使ってデコレーターを定義し、関数やクラスに適用します。

デコレーターの使用例としては、以下のようなケースがあります。

  • ログ出力を自動化
  • メソッドの実行タイミングを制御
  • 関数のパラメータや戻り値を検証

本記事では、このデコレーターの機能を用いて、関数の実行回数を制限する方法を見ていきます。

デコレーターを使う場面

デコレーターは、特定のロジックを複数の関数やクラスに繰り返し適用したいときに非常に便利です。関数の実行回数を制御する必要がある場面は多く、例えば次のようなユースケースが考えられます。

APIリクエストの制限

APIに対するリクエストを一定回数以上送るとサーバー側でエラーが発生したり、リソースが無駄になることがあります。このような場合、デコレーターを使ってリクエスト関数の実行回数を制限することで、無駄なリクエストを防ぐことができます。

再試行処理の制限

ネットワーク接続やデータベースへのアクセスなど、再試行が必要な処理に対しても、実行回数を制限したいことがあります。例えば、接続に失敗した場合に一定回数まで再試行するような制御もデコレーターで簡単に実現できます。

ユーザーインターフェースでのボタン連打防止

ユーザーがボタンを連続で押して、同じ操作を何度も実行してしまうことを防ぐために、ボタン押下時の処理回数を制限するケースもあります。デコレーターを使って、短時間での連続実行を制限することが可能です。

これらの場面で、デコレーターを使用することで、重複したコードを書くことなく、簡単に実行回数の制御を行うことができます。

関数の実行回数を制限するロジック

関数の実行回数を制限するための基本的なロジックは、関数の呼び出し回数をカウントし、そのカウントが指定された制限回数を超えた場合に関数の実行を停止する、というものです。これをデコレーターを使って実現すると、複数の関数に簡単に同じ制限を適用できるようになります。

以下に、この実行回数制限のロジックの基本的な流れを示します。

実行回数カウントの流れ

  1. 関数の呼び出しごとにカウンターをインクリメントする変数を用意します。
  2. 関数が呼ばれるたびに、そのカウンターをチェックします。
  3. カウンターが指定された最大実行回数に達していない場合は関数を実行します。
  4. カウンターが上限に達している場合は、関数を実行せずにエラーメッセージを返したり、何もしないようにします。

このロジックをデコレーターとして組み込むことで、関数がどのようなものであっても一律に実行回数制限をかけることができ、特定の条件に基づいて関数の実行を動的に制御できるようになります。

この後の項目では、具体的なデコレーターの実装方法についてコードを交えて説明していきます。

デコレーターの基本的な作成手順

デコレーターを作成するには、まずデコレーター自体が関数であることを理解する必要があります。この関数は、対象となる関数に対して追加の処理を行うために使われます。TypeScriptでは、デコレーターはクラス、メソッド、アクセサ、プロパティ、またはパラメータに適用することができ、関数の前に@をつけて宣言します。

関数の実行回数を制限するデコレーターの基本的な作成手順は次の通りです。

デコレーターの構造

デコレーター関数は、以下の構造を持ちます。

  1. 対象となる関数を引数として受け取る。
  2. その関数をラップする新しい関数を返す。
  3. ラップされた関数の中で、実行回数の制限ロジックを実装する。

基本的なデコレーターの例は次のようになります。

function executionLimit(limit: number) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    let count = 0;

    descriptor.value = function(...args: any[]) {
      if (count < limit) {
        count++;
        return originalMethod.apply(this, args);
      } else {
        console.log(`${propertyKey} can only be called ${limit} times.`);
      }
    };
  };
}

コードの解説

  • executionLimit(limit: number):実行回数の上限を指定するデコレーター関数。
  • target: any, propertyKey: string, descriptor: PropertyDescriptor:これはデコレーター関数が受け取る引数で、対象クラスやメソッド、メソッドのプロパティ情報などが含まれます。
  • originalMethod:元の関数(デコレーターが適用される関数)です。これをラップして実行回数を制限します。
  • count:関数の呼び出し回数を保持する変数です。
  • descriptor.value:関数の実行内容を上書きし、実行回数が制限を超えないようにします。

この構造により、関数の実行回数を制限しつつ、オリジナルの関数ロジックをそのまま利用することができます。

次の項目では、実際にこのデコレーターを使った具体的な実装例を見ていきます。

実行回数を制限するデコレーターの実装例

ここでは、関数の実行回数を制限するデコレーターの具体的な実装例を紹介します。このデコレーターを使うことで、指定された回数を超えた場合に関数の実行を停止し、メッセージを表示するような動作を行います。

次のコードは、前述の基本構造を使用し、関数が3回しか実行されないように制限するデコレーターの実装例です。

// 実行回数制限デコレーターの定義
function executionLimit(limit: number) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    let count = 0;

    descriptor.value = function(...args: any[]) {
      if (count < limit) {
        count++;
        console.log(`Call count: ${count}`);
        return originalMethod.apply(this, args);
      } else {
        console.log(`Error: ${propertyKey} has exceeded the call limit of ${limit} times.`);
      }
    };
  };
}

// サンプルクラスでデコレーターを使用
class Example {
  @executionLimit(3) // 関数を3回まで実行可能にするデコレーター
  printMessage() {
    console.log("Executing printMessage function.");
  }
}

// インスタンスを作成してメソッドを実行
const example = new Example();
example.printMessage(); // 実行回数 1
example.printMessage(); // 実行回数 2
example.printMessage(); // 実行回数 3
example.printMessage(); // 実行回数オーバー、エラーメッセージを表示

実行例

このコードを実行すると、次のような結果が得られます。

Call count: 1
Executing printMessage function.
Call count: 2
Executing printMessage function.
Call count: 3
Executing printMessage function.
Error: printMessage has exceeded the call limit of 3 times.

動作の説明

  • executionLimit(3)デコレーターが適用されたprintMessageメソッドは、最大3回まで実行されます。
  • countが3を超えた時点で、関数の実行は行われず、エラーメッセージが表示されます。
  • originalMethod.apply(this, args)によって、元の関数がラップされ、引数を保持したまま適用されます。

このデコレーターを他のメソッドに適用することで、同様に実行回数を制限することが簡単にできます。このシンプルな構造により、コード全体にわたる関数の実行制御が容易に行えるようになります。

次に、この実装の詳細解説を行い、どのようにロジックが動作しているかをさらに深掘りして説明します。

実装の詳細解説

前項で紹介したデコレーターによる実行回数制限のロジックについて、各部分がどのように機能しているのかをさらに詳しく見ていきます。これにより、実際のデコレーターの仕組みと応用方法をより深く理解できるでしょう。

デコレーターの動作フロー

  1. デコレーターの適用
    @executionLimit(3)という形で、printMessageメソッドにデコレーターが適用されています。この構文により、関数が呼び出される際にデコレーターがその関数をラップし、追加の動作(今回は実行回数の制御)を行います。
  2. デコレーター関数の定義
    デコレーターはexecutionLimitという関数で定義されています。ここでは実行回数を制限するための「上限値」を引数として受け取っています。
   function executionLimit(limit: number) {
     // デコレーターとしての関数を返す
     return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
       const originalMethod = descriptor.value;
       let count = 0;
  • limit: number:このパラメータは関数が何回まで実行可能かを指定します。例では3を指定しています。
  • target:デコレーターが適用されたクラスまたはオブジェクトです。
  • propertyKey:デコレーターが適用されているメソッドの名前(例ではprintMessage)。
  • descriptor: PropertyDescriptor:メソッドのプロパティ記述子です。ここでメソッドをラップし、カスタマイズします。
  1. 元の関数の取得とラップ
    descriptor.valueには元の関数が格納されています。これをoriginalMethodとして保存し、後でラップします。これにより、元の関数の動作を維持しつつ、追加のロジックを組み込むことが可能になります。
   const originalMethod = descriptor.value;
  1. 関数実行のカウントと制御
    関数の実行ごとにcountをインクリメントし、そのカウントが指定されたlimitを超えない限り、元の関数(originalMethod)を実行します。もしlimitを超えた場合は、エラーメッセージを表示し、関数の実行を停止します。
   if (count < limit) {
     count++;
     console.log(`Call count: ${count}`);
     return originalMethod.apply(this, args);
   } else {
     console.log(`Error: ${propertyKey} has exceeded the call limit of ${limit} times.`);
   }
  • count++:関数が実行されるたびにカウンターをインクリメント。
  • originalMethod.apply(this, args):元の関数を呼び出し、元々の引数(args)を保持して実行します。
  1. カスタマイズ可能な構造
    この実装では、デコレーターに渡す引数を変更することで、実行回数の制限を自由に設定できます。たとえば、@executionLimit(5)とすることで、同じデコレーターを異なる制限で使用することが可能です。

動作の可視化

デコレーターは関数の実行を制御する際、元の関数のロジックには一切手を加えず、追加の制御ロジックのみを付加します。そのため、実行回数制限のような汎用的な機能を簡単に再利用できます。実際の実行では次のように動作します。

  • 関数が初回から3回目までは実行され、Call countというカウントメッセージが出力されます。
  • 4回目以降の実行では、関数は実行されず、代わりにエラーメッセージが表示されます。

これにより、シンプルで分かりやすい方法で関数の実行回数を管理できるようになります。

次のセクションでは、このデコレーターを活用した応用例を紹介し、さらに実践的な使い方を見ていきます。

応用例:再試行回数の制限

デコレーターを使って関数の実行回数を制限する仕組みは、さまざまな場面で応用できます。その中でも特に役立つのが、再試行回数の制限です。例えば、APIリクエストやデータベースへのアクセスに失敗した際に、特定回数だけ再試行したい場合にデコレーターを使うことで、その制御が容易になります。

このセクションでは、関数がエラーを返した場合に一定回数まで自動で再試行するデコレーターの実装例を紹介します。

再試行回数制限のデコレーター

次に示すデコレーターは、関数が失敗した際に、指定された回数まで再試行するように実装されています。

// 再試行回数制限デコレーターの定義
function retryLimit(limit: number) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = async function(...args: any[]) {
      let attempts = 0;
      while (attempts < limit) {
        try {
          // 元の関数を実行
          return await originalMethod.apply(this, args);
        } catch (error) {
          attempts++;
          console.log(`Attempt ${attempts} failed.`);
          if (attempts >= limit) {
            console.log(`Error: ${propertyKey} failed after ${limit} attempts.`);
            throw error;  // 再試行回数を超えた場合にエラーを投げる
          }
        }
      }
    };
  };
}

サンプルクラスでの再試行デコレーターの使用

次に、デコレーターを適用した例を示します。このクラスでは、API呼び出しをシミュレートし、失敗した場合に再試行を行います。

class API {
  @retryLimit(3) // 3回まで再試行を許可
  async fetchData() {
    // ここではエラーを意図的に投げて再試行を確認
    const success = Math.random() > 0.7; // 成功確率を設定
    if (!success) {
      throw new Error("API request failed.");
    }
    console.log("Data fetched successfully.");
  }
}

// インスタンスを作成してAPI呼び出しを実行
const api = new API();
api.fetchData().catch(err => console.log(err.message));

動作の説明

  • @retryLimit(3)fetchData関数が失敗した場合に、最大3回まで再試行するように設定しています。
  • while (attempts < limit):再試行回数をカウントし、指定回数までリトライします。
  • originalMethod.apply(this, args):元の関数が実行されます。エラーが発生した場合、再度tryブロックで再試行します。
  • throw error:再試行回数が上限に達した場合、最終的なエラーを外に投げて、呼び出し元で処理できるようにします。

実行結果例

Attempt 1 failed.
Attempt 2 failed.
Data fetched successfully.

または、再試行回数が限度に達した場合には次のような結果になります。

Attempt 1 failed.
Attempt 2 failed.
Attempt 3 failed.
Error: fetchData failed after 3 attempts.

応用のポイント

このデコレーターは、以下のような状況で非常に役立ちます。

  • 不安定なAPIへのアクセス:特に外部のAPIが不安定な場合、一定の回数だけ自動で再試行できるため、プログラムがエラーで止まるのを防ぎます。
  • ネットワーク障害対応:一時的なネットワーク障害に対して、リトライをすることで信頼性を高めます。
  • 非同期処理の安定化:非同期処理を含む関数に対して、特定の条件下で再試行ロジックを容易に追加できます。

このように、デコレーターを使うことで、関数に追加のロジックを柔軟に組み込むことができ、さまざまな場面での応用が可能です。次に、実装したデコレーターのテストやデバッグ方法について解説します。

テストとデバッグ

デコレーターを実装した後、その動作が期待通りであるかを確認するために、テストとデバッグが不可欠です。特に、関数の実行回数を制限するようなデコレーターでは、実行の際にカウントが正しく行われているか、再試行のロジックが正常に機能しているかを慎重に確認する必要があります。このセクションでは、実装したデコレーターのテスト方法と、デバッグの際に注意すべきポイントを解説します。

デコレーターのテスト方法

まずは、デコレーターの機能を確認するためのテストを作成します。TypeScriptのテストには、JestやMochaといったテストフレームワークを利用すると効果的です。ここでは、Jestを使用した基本的なテスト例を紹介します。

Jestを使ったテストコード例

// 実行回数制限デコレーターのテスト
import { Example } from './example'; // 実装したクラスをインポート

describe('Execution Limit Decorator', () => {
  let example: Example;

  beforeEach(() => {
    example = new Example();
  });

  test('関数が指定回数まで実行されることを確認', () => {
    example.printMessage(); // 1回目
    example.printMessage(); // 2回目
    example.printMessage(); // 3回目
    expect(console.log).toHaveBeenCalledTimes(3); // 3回呼ばれていることを確認
  });

  test('指定回数を超えた場合に実行されないことを確認', () => {
    example.printMessage(); // 1回目
    example.printMessage(); // 2回目
    example.printMessage(); // 3回目
    example.printMessage(); // 4回目(超過)
    expect(console.log).toHaveBeenCalledTimes(4); // 超過後のメッセージも含めてログ出力が4回になることを確認
    expect(console.log).toHaveBeenLastCalledWith('Error: printMessage has exceeded the call limit of 3 times.');
  });
});

テストコードの説明

  • beforeEach:テストごとに新しいExampleクラスのインスタンスを作成します。
  • toHaveBeenCalledTimes(3):関数が正しく3回だけ呼ばれたことを確認します。
  • toHaveBeenLastCalledWith:実行回数制限を超えた後に、エラーメッセージが表示されたことを確認します。

このように、テストを通してデコレーターの動作が期待通りであることを自動的に検証できます。

デバッグ時に注意すべきポイント

デコレーターのデバッグを行う際は、以下の点に注意してください。

1. 実行回数の追跡

実行回数の制御が正常に行われているかどうかを確認するため、実行回数を追跡するログを挿入すると効果的です。例えば、以下のようにconsole.logで現在の実行回数を確認できます。

console.log(`Call count: ${count}`);

これにより、関数が呼び出されるたびに実行回数が表示され、想定通りにカウントが進んでいるかを簡単に確認できます。

2. オリジナル関数の正常な動作

デコレーターは元の関数の動作をラップするため、デコレーターによってオリジナルのロジックが壊れていないかを確認することも重要です。例えば、元の関数が正しい値を返しているか、戻り値に変化がないかをチェックする必要があります。

const result = originalMethod.apply(this, args);
console.log(`Result: ${result}`);

3. 非同期処理のエラーハンドリング

再試行ロジックのデコレーターの場合、非同期処理が絡むことが多く、エラーハンドリングが正しく行われているかを確認する必要があります。try-catchの中で、エラーが適切にキャッチされているか、再試行回数が制限されているかを確かめましょう。

デバッグツールの利用

ブラウザのデベロッパーツールやNode.jsのデバッガを使うと、デコレーターの動作をステップ実行して確認することができます。breakpointを使用して、各ステップで関数の実行状況や変数の状態を確認することが、複雑なデコレーターの動作を理解する助けとなります。

次に、読者が独自に実行回数をカスタマイズできるような演習課題を提供します。これにより、デコレーターの理解を深められるでしょう。

演習:実行回数のカスタマイズ

ここでは、デコレーターの仕組みをより深く理解するために、実行回数を自由にカスタマイズできるような演習課題を提供します。この演習を通して、デコレーターの柔軟性と応用力を身に付けることができます。課題では、既存のデコレーターを改良し、より高度な制御を実現していきます。

課題1:実行回数の制限を外部から設定できるようにする

これまでの例では、実行回数の制限はデコレーターに直接指定していましたが、外部からこの制限を動的に設定できるように変更しましょう。たとえば、ユーザーが自由に関数の実行回数を指定できる機能を持たせるために、クラスのプロパティやメソッドを利用します。

実装のヒント

  1. クラスのプロパティとして実行回数の上限値を保持します。
  2. メソッド内でデコレーターがこの上限値を参照するようにします。
class DynamicLimitExample {
  public limit: number;

  constructor(limit: number) {
    this.limit = limit;
  }

  @executionLimitFromProperty('limit') // 実行回数制限をプロパティで設定
  performTask() {
    console.log("Task performed.");
  }
}

function executionLimitFromProperty(limitProperty: string) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function(...args: any[]) {
      const limit = this[limitProperty];
      if (typeof limit !== 'number') {
        throw new Error('Limit must be a number.');
      }

      if (!this.hasOwnProperty('count')) {
        this.count = 0;
      }

      if (this.count < limit) {
        this.count++;
        return originalMethod.apply(this, args);
      } else {
        console.log(`Error: ${propertyKey} has exceeded the call limit of ${limit} times.`);
      }
    };
  };
}

// インスタンス作成と実行
const example = new DynamicLimitExample(5); // 5回まで実行可能
example.performTask();

この例では、DynamicLimitExampleクラスのインスタンスが持つlimitプロパティを参照して、実行回数を動的に設定しています。

演習1の目標

  • クラスのプロパティを通じて実行回数の上限を柔軟に設定する。
  • 複数のメソッドに異なる実行回数制限を適用する。

課題2:特定の条件下で実行回数をリセットする

次の課題では、一定の条件が満たされた場合に実行回数をリセットする機能を実装します。例えば、特定のフラグがtrueになったときに実行回数をリセットし、再度関数を実行できるようにするシナリオです。

実装のヒント

  1. shouldResetというフラグを持つプロパティを追加します。
  2. フラグがtrueのときに実行回数をリセットし、falseのときはリセットしないようにします。
class ResettableLimitExample {
  public limit: number;
  public shouldReset: boolean;

  constructor(limit: number) {
    this.limit = limit;
    this.shouldReset = false;
  }

  @resettableExecutionLimit('limit', 'shouldReset')
  performTask() {
    console.log("Task performed.");
  }
}

function resettableExecutionLimit(limitProperty: string, resetFlagProperty: string) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function(...args: any[]) {
      const limit = this[limitProperty];
      const shouldReset = this[resetFlagProperty];

      if (!this.hasOwnProperty('count')) {
        this.count = 0;
      }

      if (shouldReset) {
        this.count = 0; // 実行回数リセット
        this[resetFlagProperty] = false; // リセット後はフラグを元に戻す
      }

      if (this.count < limit) {
        this.count++;
        return originalMethod.apply(this, args);
      } else {
        console.log(`Error: ${propertyKey} has exceeded the call limit of ${limit} times.`);
      }
    };
  };
}

// インスタンス作成と実行
const resettableExample = new ResettableLimitExample(3);
resettableExample.performTask(); // 1回目
resettableExample.performTask(); // 2回目
resettableExample.shouldReset = true; // リセットフラグを有効に
resettableExample.performTask(); // 実行回数リセットされ再度1回目

この例では、shouldResetフラグがtrueのときに実行回数をリセットし、再度実行可能にしています。

演習2の目標

  • 実行回数のリセット機能を実装し、条件に応じてカウントをリセットする。
  • フラグやプロパティを使って動的な挙動を実現する。

課題のまとめ

これらの演習を通して、デコレーターの柔軟な使い方と応用の幅を学びます。デコレーターを使うことで、コードに追加の機能を簡単に持たせることができ、汎用的なロジックを複数の箇所で再利用することができます。

他のデコレーターとの併用

TypeScriptでは、デコレーターを複数組み合わせて使うことで、より柔軟で複雑な機能を簡潔に実装することができます。関数の実行回数制限のデコレーターも、他のデコレーターと組み合わせることで、さらに効果的な制御を実現できます。このセクションでは、実行回数制限のデコレーターを他のデコレーターと併用する方法や、その際の注意点について説明します。

デコレーターのチェーン

TypeScriptでは、1つのメソッドに対して複数のデコレーターを適用することができ、これを「デコレーターのチェーン」と呼びます。デコレーターは上から下の順に適用され、最後に定義されたデコレーターが最初に実行されます。以下は、実行回数制限デコレーターと、関数の実行前にログを出力するデコレーターを併用した例です。

// ログ出力デコレーターの定義
function logExecution(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function(...args: any[]) {
    console.log(`Executing ${propertyKey} with arguments: ${args}`);
    return originalMethod.apply(this, args);
  };
}

// 実行回数制限デコレーターの定義(前述)
function executionLimit(limit: number) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    let count = 0;

    descriptor.value = function(...args: any[]) {
      if (count < limit) {
        count++;
        return originalMethod.apply(this, args);
      } else {
        console.log(`Error: ${propertyKey} has exceeded the call limit of ${limit} times.`);
      }
    };
  };
}

// サンプルクラスでデコレーターを併用
class Example {
  @logExecution // 関数の実行前にログを出力
  @executionLimit(3) // 実行回数を3回に制限
  performTask() {
    console.log("Task performed.");
  }
}

// インスタンス作成と実行
const example = new Example();
example.performTask(); // 1回目
example.performTask(); // 2回目
example.performTask(); // 3回目
example.performTask(); // 4回目(実行回数超過)

デコレーターの順序

上記の例では、@logExecution@executionLimitの2つのデコレーターが使用されていますが、デコレーターは定義された順に呼び出されます。この場合、次の順序で実行されます。

  1. 実行回数制限デコレーターが最初に呼び出されます。実行回数が制限に達していない場合のみ、関数の実行を許可します。
  2. ログ出力デコレーターは、関数が実行される直前にログを出力します。

結果として、実行回数制限に引っかかる場合は、関数の実行自体が行われないため、ログ出力もされなくなります。デコレーターの順序により、動作が変わることに注意しましょう。

併用時の注意点

デコレーターを併用する際には、いくつかの注意点があります。

1. デコレーターの実行順序

デコレーターは下から順に適用されるため、適用順によって動作が異なることがあります。特に実行順序が重要なロジックを扱う場合、デコレーターが正しい順序で実行されるように意識する必要があります。

2. データの共有

複数のデコレーターが同じデータ(例:実行回数や状態)に依存する場合、そのデータの管理方法に注意が必要です。例えば、デコレーター間で状態を共有する場合には、クラスのプロパティを使って情報を管理することが有効です。

3. パフォーマンスの影響

複数のデコレーターが同時に適用されると、処理が増えるため、実行パフォーマンスに影響を与える可能性があります。特に、リソースを多く消費するデコレーターを組み合わせる場合、実行速度やメモリ消費に注意が必要です。

複雑なデコレーションの実例

以下は、デコレーターをさらに組み合わせ、複雑なロジックを実現する例です。ここでは、関数の実行前にログを出力し、実行回数を制限し、かつ特定の例外が発生した場合にリトライを試みるデコレーターを併用しています。

// リトライ回数制限デコレーターの定義
function retryOnFailure(limit: number) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = async function(...args: any[]) {
      let attempts = 0;
      while (attempts < limit) {
        try {
          return await originalMethod.apply(this, args);
        } catch (error) {
          attempts++;
          if (attempts >= limit) {
            console.log(`Error: ${propertyKey} failed after ${limit} attempts.`);
            throw error;
          }
        }
      }
    };
  };
}

// サンプルクラスでデコレーターを併用
class ComplexExample {
  @logExecution
  @executionLimit(3)
  @retryOnFailure(2)
  async fetchData() {
    const success = Math.random() > 0.7; // 成功率を設定
    if (!success) {
      throw new Error("Data fetch failed.");
    }
    console.log("Data fetched successfully.");
  }
}

// インスタンス作成と実行
const complexExample = new ComplexExample();
complexExample.fetchData().catch(err => console.log(err.message));

この例では、次のデコレーターが適用されています。

  • @logExecution:メソッドの実行前にログを出力。
  • @executionLimit(3):メソッドの実行回数を3回に制限。
  • @retryOnFailure(2):エラー発生時に最大2回までリトライ。

まとめ

デコレーターを併用することで、柔軟かつ強力な制御が可能になりますが、実行順序やデータの共有、パフォーマンスの考慮が必要です。他のデコレーターと組み合わせることで、実装の効率が上がり、よりモジュール化されたコードを実現できます。

まとめ

本記事では、TypeScriptにおけるデコレーターの基本概念から、実行回数を制限するデコレーターの実装方法、さらに他のデコレーターとの併用方法までを解説しました。デコレーターを使うことで、コードの再利用性が高まり、複雑なロジックも簡潔に管理できるようになります。実行回数制限のデコレーターは、APIリクエストやユーザーインターフェースでの操作制御など、さまざまな場面で役立つ強力なツールです。

コメント

コメントする

目次