プラグインは何を使用すべきですか:フック、イベント、または何か?


24

プラグインがプログラムフローに反応できるようにするアプリを検討してください。

これを達成する2つの方法を知っています:フックイベント

1.フック

メインプログラムフロー内の空の関数の呼び出しを使用します。これらの機能はプラグインによってオーバーライドできます。

たとえば、Drupal CMSはモジュールとテーマで利用可能なフックを実装しています。file_copy関数でフックを実装する方法の例を次に示します。

function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
    // ... [File copying routine]

    // Inform modules that the file has been copied.
    module_invoke_all('file_copy', $file, $source);

    return $file;
    // ...
}

モジュールはmodulename_file_copy($file, $source)module_invoke_allin によって呼び出される関数を実装できますfile_copy。この関数が終了すると、file_copy実行が再開されます。

2.イベント

プラグインがリッスンできるイベントをアプリにディスパッチさせます。サブスクライブされているイベントを受信した後、プラグインはプログラムフローをインターセプトし、必要な操作を実行します。

たとえば、jQueryギャラリープラグインFotorama はいくつかのイベントを実装します。例として、イベントshowを発生させるメソッドの一部を次に示しfotorama:showます。

  that.show = function (options) {
    // ... [show the new frame]

    // [fire the event]
    options.reset || triggerEvent('show', {
      user: options.user,
      time: time
    });

    // ... [do lots of other stuff with navigation bars, etc.]
  };

スクリプトは、このイベントをリッスンし、発生したときに何かを実行できます。

$('.fotorama').on(
  'fotorama:show',
  function (e, fotorama, extra) {
    console.log(e.type + (extra.user ? ' after user’s touch' : ''));
    console.log('transition duration: ' + extra.time);
  }
);

質問

  1. そのようなプラグインの動作を実装する他の主流の方法はありますか?

  2. そうでない場合、いつフックを使用する必要があり、いつイベントを使用する必要がありますか?究極の目標を考えることは、アプリとプラグイン開発者の両方の観点から、コードをより保守可能で読みやすいものにすることですか?

回答:


17

フックとイベントの主な違いは、疎結合と密結合です。

フックは、何かが起こったことをブロードキャストする一般的な方法です。プラグインを再コンパイルせずに新しいフックを追加でき、すべてのフックは一般的なデザインパターンに従います。フックAPIが定義された後は変更されないため、アプリとプラグインの間の結合が壊れることはほとんどありません。

イベントはアプリとより密接に結びついています。イベントは、イベントに添付されるパラメーターを定義できます。これらのパラメーターを変更すると、既存のプラグインでAPIが破損します。

どちらも同じ結果を達成します。プラグインをアプリに結合する方法に依存します。

フックは、アプリの新しいバージョンがリリースされても壊れない可能性のある、より動的なカップリングを提供できますが、デメリットは、プラグインに互換性がないというコンパイル時の警告が表示されないことです。

イベントでは、一部のイベントシグネチャが変更されているため、プラグインを変更する必要があるコンパイル時エラーを取得することができます。

別のアプローチを求めました。

コマンド:

トリガーされたイベントに応答するプラグインの代わり。プラグインは、コマンドオブジェクトをアプリケーションにプッシュします。各コマンドオブジェクトは、コマンドで使用されるインターフェイスを実装します。アプリケーションが機能を実行する必要がある場合、その機能のすべてのコマンドを実行します。これはイベントに非常に似ていますが、コールバック関数ではなくオブジェクトとして実装されている点が異なります。

マクロ:

事態が発生したときにプラグインが応答する代わりに。プラグインは物事を積極的に引き起こします。マクロは、アプリケーションの上で実行されて何をすべきかを伝える小さな高水準言語です。

状態変更リスナー:

イベントは、開発者が事前に考えたアプリケーションによってトリガーされます。開発者は、イベントを発行するコードを故意に記述する必要があります。代わりに、別のアプローチは、オブジェクトの内部状態が変化したときにオブジェクトを自動的にブロードキャストすることです。プロパティまたは他のインジケータの変更。その後、プラグインはこれらの特定の状態変化をリッスンし、それに応じて対応できます。このアプローチの利点は、プログラマがイベントをブロードキャストすることを覚えておく必要がないことです。たとえば、Documentオブジェクトがあり、プログラマがドキュメントを保存する必要があることを示すフラグを設定する場合があります。この状態変更はリスニングプラグインにブロードキャストされ、ドキュメントタイトルを変更してアスタリスクを含めるプラグインが存在する可能性があります。


2
代替、-1の定義とカップリング引数のための+1(ない存在するが、カップリングは、あなたのプラグインシステムに与える方の名前、設計の選択の結果である)

5
また、イベントがジェネレータからオブザーバ/リスナに移動する方法についても想定していると思います。実際のところ、それは逆であり、フックは密接に結合されていますが、イベントはそうではありません。
アーメドマスード

3

間違いなくイベントであり、既にアーキテクチャレベルで必要な抽象化を可能にします。

プラグインを作成する人が実際に文書化されているように、または何らかの形で正しいことを期待しないでください。私は何百万人ものユーザーで十分に文書化されたAPIを維持してきましたが、基本的に誰も文書を読んでおらず、ほとんど誰もAPIを正しく使用していないという非常につらい経験からお伝えできます。

フックを使用した次の例を見てください。20個のプラグインが実行されているシステムがあります。これらのプラグインの1つfile_copyは、文書化された方法でメソッドを呼び出し、文書化された結果を期待します。しかし、他のプラグインがその機能をフックしているため、次の問題のいずれかがクラッシュまたは誤動作を引き起こします。

  • フック関数は単純にクラッシュします。他のすべてのプラグインは現在file_copyできないか、関数が予想とは異なる方法で動作するため、ネジ止めされています。
  • 入力はドキュメントに基づいて正しいですが、他のプラグインはそれを予期せず、奇妙な結果またはクラッシュを提供します。
  • 呼び出しは正常に実行されますが、その結果はドキュメントに従って予想されるものではなくなったため、プラグインは失敗またはクラッシュします。

これらのプラグイン内で同じ問題を伴うイベントで上記と同じことを行うと、次のことが起こります。

  • プラグインXのイベント関数はクラッシュしますが、他のすべては正常に動作します。ただし、これらのプラグインは関連していないため、他のプラグインが正常に動作している間、そのクラッシュするプラグインを単純に無効にすることができます。
  • 奇妙な入力は関数によって適切に処理でき、プラグインごとに可能なすべてのものを個別に適切にチェックできます。プラグイン開発者は、プラグインを実際にテストする安定した信頼できる方法を手に入れました。1つのプラグインが誤った入力を提供する場合、その1つのプラグインに分離できます。
  • 同様に、結果はすべての状況下で適切にチェックおよび定義できるため、プラグイン開発者は、テストできる安定した信頼できる回答をその関数から取得できます。

1

継承はオプションの場合があります。

フック以外に、継承には追加のメソッド定義は必要ありません。また、何もフックされていない場合に空のメソッドを呼び出すことによるパフォーマンスの低下はありません。

イベント以外に、継承はイベント呼び出しのために追加のコードを必要としません。

ただし、1つのタイプの動作を変更するプラグインが1つしかない場合、継承が最適に機能します。多くのプラグインが必要な場合、2番目のプラグインは最初のプラグインなどから派生する必要がありますが、これは適切ではありません。


-1 ...あなたが継承を使用して、新しい行動が主なアプリケーションとして異なる目的を持っているとして、あなたの仕様や悪用の継承を使用するようにインスタンス化コードを変更するため
スパーク

0

間違いなくイベント。これにより、アーキテクチャをよりスケーラブルにできます。

たとえば、プラグインを別のマシンに配置する必要がある場合にどうなるかを想像してください。イベントを使用する-コードを少し変更するだけで、イベントをネットワークベースにすることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.