IT

발행 / 구독 패턴 (JS / jQuery에서)을 사용하는 이유는 무엇입니까?

lottoking 2020. 8. 21. 08:03
반응형

발행 / 구독 패턴 (JS / jQuery에서)을 사용하는 이유는 무엇입니까?


그래서 동료가 저에게 게시 / 구독 패턴 (JS / jQuery를에서)을 소개했지만 '일반적인'스크립트 / 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) 패턴과 함께 사용되는듯한 결합과 단일 책임에 관한 것입니다.

신경 결합 은 시스템의 각 구성 요소가 자신의 책임을 알고 다른 구성 요소에 쓰지 않는 지향 원칙입니다 (또는 최소한 한 가지 신경 쓰지 않는 것). 즉흥적으로 결합은 다른 모듈을 쉽게 할 수 있기 때문에 좋은 것입니다. 다른 모듈의 인터페이스와 연결되지 않습니다. 발행 / 구독을 사용하면 큰 문제가 아닌 발행 / 구독 인터페이스와 결합됩니다. 두 가지 방법 만 있습니다. 이에 따라 다른 프로젝트에서 모듈을 사용하기로 결정한 경우 복사하여 넣기 만하면 작동 할 수 있고 작동하도록 만드는 데 많은 노력이 필요하지 않습니다.

결합에 대해 느슨한 이야기 할 때 우려분리를 언급해야 우리합니다.. MV * 아키텍처 패턴을 사용하여 애플리케이션을 빌드하는 경우 항상 모델과 뷰가 있습니다. 모델은 애플리케이션의 비즈니스 부분입니다. 다른 응용 프로그램에서는 다른 응용 프로그램에서 일반적으로 다른 응용 프로그램에서 다른보기를 갖기 때문에 표시하려는 단일 응용 프로그램의보기와 결합하는 것은 좋지 않습니다. 따라서 Model-View 통신을 위해 게시 / 구독을 사용하는 것이 좋습니다. 모델이 변경되면 이벤트를 게시하고 뷰는 이벤트를 자체적으로 업데이트합니다. / 구독으로 인한 오버 헤드가 게시 디커플링에 도움이됩니다. 같은 방식으로 컨트롤러에 애플리케이션을 유지하고 (MVVM, MVP는 정확히 컨트롤러가 아님) 가능한 한 간단하게 볼 수 있습니다. 뷰가 변경되면 (예를 들어 사용자가 조치를 클릭) 새 이벤트를 게시하고 컨트롤러가이를 수행하여 수행 할 작업을 결정합니다. 당신이 익숙한 경우 MVC 패턴 또는 Microsoft 기술 (WPF / Silverlight)의 MVVM 을 사용하면 게시 / 구독을 관찰자 패턴 과 같이 생각할 수 있습니다 .이 접근 방식은 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"이벤트를 구독하고 처리 할 수 ​​있습니다. 이 접근 방식은 매우 유용하며 애플리케이션을 매우 분리 할 수 ​​있습니다. 모듈을 매우 용이하게 할 수 있습니다.

다음은 마지막 접근 방식의 기본 예입니다 (이는 원본 트위터 코드가 아니라 제가 메시지를 일뿐입니다).

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);
           });
       }
   };
}());

이 접근 방식에 대해 Nicholas Zakas 의 훌륭한 강연이 있습니다. MV * 접근 방식의 경우 내가 아는 최고의 가이드와 책은 Addy Osmani 가 출판되었습니다 .

단점 : 발행 / 구독의 과도한 사용에주의해야합니다. 많은 이벤트가있는 경우 모든 이벤트를 관리하는 것이 매우 혼란 스럽습니다. 네임 스페이스를 사용하지 않고 올바른 방식으로 사용하지 않는 경우에도 충돌이 있습니다. 게시 / 구독과 사용 가능 Mediator의 고급 구현은 여기 https://github.com/ajacksified/Mediator.js 에서 사용할 수 있습니다 . 물론 중단 될 수있는 이벤트 "버블 링"과 같은 네임 스페이스 및 기능이 있습니다. 게시 / 구독의 또 다른 단점은 하드 단위 테스트입니다. 모듈에서 다른 기능을 분리하고 독립적으로 테스트하기가 어려울 수 있습니다.


주요 목표는 코드 결합을 줄이는 것입니다. 다소 이벤트 기반 사고 방식이지만 "이벤트"는 특정 대상에 묶여 있지 않습니다.

JavaScript와 작성 의사 코드로 아래에 큰 예제를 작성하겠습니다.

라디오 클래스와 릴레이 클래스가 가정 해 보겠습니다.

class Relay {
    function RelaySignal(signal) {
        //do something we don't care about right now
    }
}

class Radio {
    function ReceiveSignal(signal) {
        //how do I send this signal to other relays?
    }
}

라디오가 신호를 수신 할 때마다 우리는 여러 릴레이가 어떤 방식 으로든 으로든 메시지를 릴레이하기를 원합니다. 릴레이의 수와 유형은 다를 수 있습니다. 다음과 같이 할 수 있습니다.

class Radio {
    var relayList = [];

    function AddRelay(relay) {
        relayList.add(relay);
    }

    function ReceiveSignal(signal) {
        for(relay in relayList) {
            relay.Relay(signal);
        }
    }

}

이것은 잘 작동합니다. 그러나 이제 다른 구성 요소가 라디오 클래스가 수신하는 신호, 즉 스피커의 일부를 취하기를 원합니다.

(비유가 최고 수준이 아니라면 죄송합니다 ...)

class Speakers {
    function PlaySignal(signal) {
        //do something with the signal to create sounds
    }
}

패턴을 다시 반복 할 수 있습니다.

class Radio {
    var relayList = [];
    var speakerList = [];

    function AddRelay(relay) {
        relayList.add(relay);
    }

    function AddSpeaker(speaker) {
        speakerList.add(speaker)
    }

    function ReceiveSignal(signal) {

        for(relay in relayList) {
            relay.Relay(signal);
        }

        for(speaker in speakerList) {
            speaker.PlaySignal(signal);
        }

    }

}

"SignalListener"와 같은 인터페이스를 만들어서 더 좋게 만들 수 있습니다. 따라서 라디오 클래스에서 하나의 목록 만 필요하고 신호를 듣고 자하는 할 객체에 대해 항상 호출 할 수 있습니다. 그러나 그것은 우리가 결정한 인터페이스 / 기본 클래스 / 라디오 클래스 사이에 여전히 결합을 생성합니다. 기본적으로 라디오, 신호 또는 릴레이 클래스 중 하나를 설명 할 때마다 다른 클래스에 영향을 미칠 수 있습니다.

이제 다른 것을 시도해 보겠습니다. RadioMast라는 네 번째 클래스를 만들어 보겠습니다.

class RadioMast {

    var receivers = [];

    //this is the "subscribe"
    function RegisterReceivers(signaltype, receiverMethod) {
        //if no list for this type of signal exits, create it
        if(receivers[signaltype] == null) {
            receivers[signaltype] = [];
        }
        //add a subscriber to this signal type
        receivers[signaltype].add(receiverMethod);
    }

    //this is the "publish"
    function Broadcast(signaltype, signal) {
        //loop through all receivers for this type of signal
        //and call them with the signal
        for(receiverMethod in receivers[signaltype]) {
            receiverMethod(signal);
        }
    }
}

이제 우리는 우리가 알고 있는 패턴가지고 있고 다음과 같은 한 어떤 수와 유형의 클래스에도 사용할 수 있습니다.

  • RadioMast (모든 메시지 전달을 처리하는 클래스)를 알고 있습니다.
  • 메시지 보내기 / 받기를위한 메소드 서명을 알고 있습니다.

그래서 우리는 Radio 클래스를 최종 간단하고 간단하게 변경합니다.

class Radio {
    function ReceiveSignal(signal) {
        RadioMast.Broadcast("specialradiosignal", signal);
    }
}

그리고 우리는 RadioMast의 수신 목록에 스피커와 릴레이를 추가합니다.

RadioMast.RegisterReceivers("specialradiosignal", speakers.PlaySignal);
RadioMast.RegisterReceivers("specialradiosignal", relay.RelaySignal);

이제 Speakers 및 Relay 클래스는 신호를 수신 할 수있는 방법이 있고 게시자 인 Radio 클래스가 신호를 게시하는 RadioMast를 알고있는 점을 제외하고 아무것도 알지 못합니다. 이것이 발행 / 구독과 같은 메시지 전달 시스템을 사용하는 지점입니다.


다른 답변은 패턴이 어떻게 작동하는지 그래픽 데 큰 역할을했습니다. 나는 최근에 이로 인해 패턴 작업을 해왔 기 때문에 " 이전 방식에 무엇이 잘못 되었습니까? " 라는 묵시적인 질문에 답하고 싶었습니다 .

우리가 경제 정보에 가입되어 있다고 상상해보세요. 이 게시판에는 " 다우 존스를 200 포인트 낮추기 "라는 제목이 게시 되어 있습니다. 그것은 이상하고 다소 무책임한 메시지가 될 것입니다. 그러나 " Enron이 오늘 아침 11 장 파산 보호를 위해 신청했습니다 "라고 게시 됐습니다. 메시지로이 인해 다우 존스가 200 포인트 하락할 수 있지만 이는 또 다른 문제 입니다 .

명령을 받았거나 일어난 일에 대해 조언하는 것에는 차이가 있습니다. 염두에두고 지금은이를 무시하고 pub / sub 패턴의 원래 버전을 사용합니다.

$.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'));
});

사용자 행동 (클릭)과 시스템 반응 (주문 제거) 사이에는 이미 암시 된 강력한 결합이 있습니다. 귀하의 예에서 행동은 명령을 내리고 있습니다. 이 버전을 고려하십시오.

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

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

이제는 모든 것이 처리 할 의무는 없습니다. 실제로는 주문은 제거와 직접적으로 관련되지는 않지만 호출 작업과 관련이있을 수있는 모든 종류의 작업을 수행 할 수 있습니다. 예를 들면 :

handleRemoveOrderRequest = function(e, orders) {
    logAction(e, "remove order requested");
    if( !isUserLoggedIn()) {
        adviseUser("You need to be logged in to remove orders");
    } else if (isOkToRemoveOrders(orders)) {
        orders.last().remove();
        adviseUser("Your last order has been removed");
        logAction(e, "order removed OK");
    } else {
        adviseUser("Your order was not removed");
        logAction(e, "order not removed");
    }
    remindUserToFloss();
    increaseProgrammerBrowniePoints();
    //etc...
}

명령과 알림을 구분하는 것은 패턴 인 IMO를 사용하는 데 유용한 구분입니다.


메소드 / 함수 호출을 하드 코딩 할 필요가 있는지 누가 수신하는지 신경 쓰지 않고 이벤트를 게시하면됩니다. 이렇게하면 게시자가 구독자로부터 독립된 애플리케이션의 서로 다른 두 부분이 있습니다 (또는 원하는 용어에 관계없이 결합).

wikipedia에서 언급 한 커플 링의 단점은 다음과 가변합니다.

사용하게 결합 된 시스템은 종종 사용되는 다음과 같은 경향을 갖습니다.

  1. 한 모듈의 변경은 일반적으로 다른 모듈의 변경으로 인한 파급 효과를 강제합니다.
  2. 모듈 조립은 모듈 간 더 증가로 인해 더 많은 노력 및 / 또는 시간이 필요할 수 있습니다.
  3. 구체적인 모듈은 다음과 같은 모듈을 포함해야합니다.

비즈니스 데이터를 캡슐화하는 것과 같은 것을 고려하십시오. 연령이 설정 될 때마다 페이지를 업데이트하는 하드 코딩 된 메서드 호출이 있습니다.

var person = {
    name: "John",
    age: 23,

    setAge: function( age ) {
        this.age = age;
        showAge( age );
    }
};

//Different module

function showAge( age ) {
    $("#age").text( age );
}

이제 showAge함수를 포함 하지 않고 사람을 테스트 할 수 없습니다 . 또한 다른 GUI 모듈에서 나이를 표시해야하는 경우 해당 메서드 호출을 하드 코딩해야 .setAge하며 이제 사람 개체에 관련되지 않은 2 개의 모듈에 대한 것이 있습니다. 또한 이러한 호출이 수행되고 파일에 있지 않을 때 유지 관리하기가 어렵습니다.

물론 동일한 모듈 내에서 직접 메서드를 호출 할 수 있습니다. 그러나 비즈니스 데이터와 피상적 인 GUI 동작은 합당 표준에 따라 동일한 모듈에 상주 상관 없습니다.


PubSub 구현은 일반적으로 다음 위치에서 볼 수 있습니다.

  1. 이벤트 버스의 도움으로 통신하는 여러 포틀릿이있는 구현과 같은 포틀릿이 있습니다. 이것은 aync 아키텍처에서 생성하는 데 도움이됩니다.
  2. 동일한 결합으로 다양한 시스템에서 pubsub는 다양한 모듈 통신을 사용합니다.

예제 코드-

var pubSub = {};
(function(q) {

  var messages = [];

  q.subscribe = function(message, fn) {
    if (!messages[message]) {
      messages[message] = [];
    }
    messages[message].push(fn);
  }

  q.publish = function(message) {
    /* fetch all the subscribers and execute*/
    if (!messages[message]) {
      return false;
    } else {
      for (var message in messages) {
        for (var idx = 0; idx < messages[message].length; idx++) {
          if (messages[message][idx])
            messages[message][idx]();
        }
      }
    }
  }
})(pubSub);

pubSub.subscribe("event-A", function() {
  console.log('this is A');
});

pubSub.subscribe("event-A", function() {
  console.log('booyeah A');
});

pubSub.publish("A"); //executes the methods.

"발행 / 구독의 다양한면" 이라는 논문 은 좋은 읽기이며 그들이 강조하는 한 가지입니다 세 가지 "차원"의 감소입니다. 여기에 내 조잡한 요약이 대신 논문을 참조하십시오.

  1. 공간 감소. 상호 작용하는 것은 서로를 알 필요가 없습니다. 게시자는 누가 있는지, 이벤트를 통해 얼마나 많이 또는 무엇을 있는지 확인합니다. 구독자는 이러한 이벤트를 누가 제작하는지 또는 얼마나 많은 제작자가 있는지 등을 확인합니다.
  2. 시간이 모든 것. 상호 작용하는 것은 상호 작용하는 동시에 활성화 될 필요가 없습니다. 예를 들어 게시자가 일부 이벤트를 게시하는 동안 구독자의 연결이 더러워 질 수 있으며 온라인 상태가 이에 반응 할 수 있습니다.
  3. 동기화 디커플링. 게시자는 이벤트를 생성하는 동안 차단되지 않고 구독자는 구독 한 이벤트가 도착할 때마다 수신 할 수 있습니다.

간단한 대답 원래 질문은 간단한 대답을 대답합니다. 여기 내 시도가 있습니다.

자바 펼쳐는 코드 객체가 자체 이벤트를 생성하는 것을 제공하지 않습니다. 꼭 잊지 마세요 이벤트가 필요합니다. 게시 / 구독 패턴은 요구에 응답 할 수있는 자신의 요구에 가장 많이 사용하는 것이 선택하는 사용자에게 달려 있습니다.

이제 pub / sub 패턴이 발생하는 것을 알 수 있습니다. 그러면 pub / sub 이벤트를 처리하는 방법과 다르게 DOM 이벤트를 처리해야합니까? SOC (관심 분리를 위해)와 기타 개념을 분리 할 수 ​​있습니다.

따라서 역설적이게도 코드가 많을수록 우려 사항이 더 잘 분리되어 복잡한 웹 페이지까지 확장됩니다.

나는 누군가가 자세히 설명하지 않고 충분히 좋은 토론이라고 생각하기를 바랍니다.

참고 URL : https://stackoverflow.com/questions/13512949/why-would-one-use-the-publish-subscribe-pattern-in-js-jquery

반응형