1. ホーム
  2. javascript

[解決済み] なぜPublish/Subscribeパターンを(JS/jQueryで)使用するのか?

2022-11-24 11:08:52

質問

同僚が(JS/jQueryの)公開/購読パターンを紹介してくれたのですが、私はこのパターンを理解するのに苦労しています。 なぜ を理解するのに苦労しています。

例えば、以前は以下のようなコードでした。

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    var orders = $(this).parents('form:first').find('div.order');
    if (orders.length > 2) {
        orders.last().remove();
    }
});

そして、代わりにこうすることのメリットもわかりました、例えば...。

removeOrder = function(orders) {
    if (orders.length > 2) {
        orders.last().remove();
    }
}

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    removeOrder($(this).parents('form:first').find('div.order'));
});

を再利用する機能を導入しているためです。 removeOrder 機能を再利用できるようになるからです。

しかし、同じことをするのであれば、なぜパブリッシュ/サブスクライブパターンを実装しようと思い、以下のような長さにしたのでしょうか?(参考までに、私は jQuery tiny pub/sub )

removeOrder = function(e, orders) {
    if (orders.length > 2) {
        orders.last().remove();
    }
}

$.subscribe('iquery/action/remove-order', removeOrder);

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    $.publish('iquery/action/remove-order', $(this).parents('form:first').find('div.order'));
});

このパターンについて確かに読みましたが、なぜこれが必要になるのか想像がつきません。私が見たチュートリアルの説明では どのように を説明しているチュートリアルを見たことがありますが、私が作成したものと同じような基本的な例しかカバーしていません。

pub/subの有用性は、より複雑なアプリケーションで明らかになると想像していますが、想像がつきません。私は完全にポイントを逃していると思いますが、もしポイントがあれば知りたいです!

次のような説明をお願いします。 簡潔に なぜ、そしてどのような状況でこのパターンが有利なのか?上記の私の例のようなコードスニペットに pub/sub パターンを使用する価値はありますか?

どのように解決するのですか?

それは、緩やかな結合と単一の責任であり、ここ数年非常にモダンなJavaScriptのMV*(MVC/MVP/MVVM)パターンに密接に関連しています。

疎結合 はオブジェクト指向の原則で、システムの各コンポーネントが自分の責任を自覚し、他のコンポーネントを気にしない(少なくとも可能な限り気にしないようにする)ことを指します。疎結合は、異なるモジュールを簡単に再利用できるため、良いことです。他のモジュールのインターフェースと結合することはない。publish/subscribeを使う場合、publish/subscribeインターフェースとしか結合していないことになりますが、これは大きな問題ではありません。ですから、もしあなたが別のプロジェクトでモジュールを再利用しようと決めたら、それをコピー&ペーストするだけで、おそらく動作するでしょうし、少なくとも動作させるためにそれほど努力する必要はないでしょう。

ルースカップリングについて話すとき、私たちは 懸念の分離 . MV*アーキテクチャパターンを使ってアプリケーションを構築する場合、常にモデル(複数可)とビュー(複数可)が存在します。モデルはアプリケーションのビジネス部分です。このモデルは、異なるアプリケーションで再利用することができます。そのため、1つのアプリケーションのビューと組み合わせて表示することは良いアイデアではありません。そのため、モデル-ビュー間の通信にパブリッシュ/サブスクライブを使用するのは良い考えです。モデルが変更されると、イベントが発行され、ビューはそれをキャッチして自分自身を更新します。パブリッシュ/サブスクライブによるオーバーヘッドはなく、デカップリングのために役立ちます。同じように、例えばアプリケーションのロジックをコントローラ(MVVM、MVPは正確にはコントローラではありません)に保持し、Viewをできるだけシンプルに保つことができます。View が変更されたとき (あるいはユーザが何かをクリックしたときなど) は、単に新しいイベントを発行し、Controller がそれを捕捉して何をすべきかを決定します。もしあなたが MVC パターンに詳しい方、あるいは MVVM のようなパブリッシュ/サブスクライブを考えることができます。 Observerパターン . このアプローチは、Backbone.jsやKnockout.js(MVVM)などのフレームワークで使用されています。

以下はその例です。

//Model
function Book(name, isbn) {
    this.name = name;
    this.isbn = isbn;
}

function BookCollection(books) {
    this.books = books;
}

BookCollection.prototype.addBook = function (book) {
    this.books.push(book);
    $.publish('book-added', book);
    return book;
}

BookCollection.prototype.removeBook = function (book) {
   var removed;
   if (typeof book === 'number') {
       removed = this.books.splice(book, 1);
   }
   for (var i = 0; i < this.books.length; i += 1) {
      if (this.books[i] === book) {
          removed = this.books.splice(i, 1);
      }
   }
   $.publish('book-removed', removed);
   return removed;
}

//View
var BookListView = (function () {

   function removeBook(book) {
      $('#' + book.isbn).remove();
   }

   function addBook(book) {
      $('#bookList').append('<div id="' + book.isbn + '">' + book.name + '</div>');
   }

   return {
      init: function () {
         $.subscribe('book-removed', removeBook);
         $.subscribe('book-aded', addBook);
      }
   }
}());

もうひとつの例です。もしMV*のアプローチが好きでないなら、少し違うものを使うことができます(次に説明するものと最後に述べたものとの間に交差点があるのです)。アプリケーションを異なるモジュールで構成すればいいのです。例えば、Twitterを見てください。

インターフェイスを見ると、単純にいろいろな箱がありますね。それぞれのボックスは、異なるモジュールと考えることができます。たとえば、ツイートを投稿することができます。このアクションでは、いくつかのモジュールの更新が必要です。まず、プロフィールデータ(左上のボックス)を更新しなければなりませんが、タイムラインも更新しなければなりません。もちろん、両方のモジュールへの参照を保持し、それぞれのパブリック・インターフェースを使用して別々に更新することもできますが、イベントを公開するだけの方が簡単です(そして、より良いです)。そうすることで、カップリングが緩やかになり、アプリケーションの修正が容易になります。もし、新しいツイートに依存する新しいモジュールを開発したら、"publish-tweet" イベントを購読して、それを処理すればいいだけです。このアプローチは非常に便利で、アプリケーションを非常に非連続にすることができます。モジュールを簡単に再利用することができます。

最後のアプローチの基本的な例です(これはオリジナルのtwitterのコードではなく、単なる私のサンプルです)。

var Twitter.Timeline = (function () {
   var tweets = [];
   function publishTweet(tweet) {
      tweets.push(tweet);
      //publishing the tweet
   };
   return {
      init: function () {
         $.subscribe('tweet-posted', function (data) {
             publishTweet(data);
         });
      }
   };
}());


var Twitter.TweetPoster = (function () {
   return {
       init: function () {
           $('#postTweet').bind('click', function () {
               var tweet = $('#tweetInput').val();
               $.publish('tweet-posted', tweet);
           });
       }
   };
}());

このアプローチについては、以下のような素晴らしい講演があります。 ニコラス・ザカス . MV*のアプローチについては、私が知る限り最も優れた論文や書籍が、以下の出版社から出版されています。 アディ・オスマニ .

欠点は publish/subscribeの使いすぎに注意する必要があります。何百ものイベントがある場合、それらをすべて管理するのは非常に困難になります。また、名前空間を使っていない(または正しい方法で使っていない)場合、衝突が発生する可能性があります。パブリッシュ/サブスクライブによく似たMediatorの高度な実装は、こちらにあります。 https://github.com/ajacksified/Mediator.js . 名前空間やイベントの「バブリング」のような機能があり、もちろん中断することも可能です。パブリッシュ/サブスクライブのもう一つの欠点は、ユニットテストが難しいことです。モジュール内の異なる機能を分離し、それらを独立してテストすることが難しくなる可能性があります。