The Observer Pattern

옵서버는 객체 (주체)가 객체 (옵저버)에 따라 객체 목록을 유지하고 상태에 대한 변경 사항을 자동으로 알리는 디자인 패턴입니다.

흥미로운 주제에 대해 관찰자에게 알릴 필요가 있을 때, 관찰자에게 알림을 전송합니다 (알림의 주제와 관련된 특정 데이터가 포함될 수 있음).

특정 관찰자가 등록 된 주제에 대한 변경 사항을 더 이상 통지받지 않기를 바라는 경우, 주제가 관찰자 목록에서 제거 될 수 있습니다.

시간이 지남에 따라 사용법과 장점에 대한 더 폭 넓은 의미를 갖기 위해 언어에 구애받지 않는 디자인 패턴의 게시 된 정의를 다시 언급하는 것이 유용합니다. GoF 서적, 디자인 패턴 : 재사용 가능한 객체 지향 소프트웨어의 요소에서 제공되는 Observer 패턴의 정의는 다음과 같습니다.

한 명 이상의 관찰자는 주체의 상태에 관심을 갖고 자신의 관심사를 첨부하여 주제에 등록합니다. 관찰자가 관심을 가질만 한 주제가 변경되면 각각의 업데이트 방법을 호출하는 알림 메시지가 전송됩니다. 관찰자가 더 이상 주체의 상태에 관심이 없으면 간단히 스스로 분리 할 수 ​​있습니다.

Observer 패턴을 구현하기 위해 배운 내용을 다음 구성 요소로 확장 할 수 있습니다.

  • Subject : 옵서버 목록을 관리하고 옵저버 추가 또는 제거를 용이하게 합니다.
  • Observer : 주체의 상태 변경을 통보 받아야하는 객체에 대한 업데이트 인터페이스를 제공합니다.
  • ConcreteSubject : 상태 변경에 대한 관찰자에게 알림을 브로드 캐스트하고 ConcreteObservers의 상태를 저장합니다.
  • ConcreteObserver : ConcreteSubject에 대한 참조를 저장하고, 관찰자가 주체의 상태와 일치하는지 확인하기위한 업데이트 인터페이스를 구현합니다.

첫째, 피험자가 가질 수있는 종속 관찰자 목록을 모델링 해 봅시다.

function ObserverList(){
  this.observerList = [];
}

ObserverList.prototype.add = function( obj ){
  return this.observerList.push( obj );
};

ObserverList.prototype.count = function(){
  return this.observerList.length;
};

ObserverList.prototype.get = function( index ){
  if( index > -1 && index < this.observerList.length ){
    return this.observerList[ index ];
  }
};

ObserverList.prototype.indexOf = function( obj, startIndex ){
  var i = startIndex;

  while( i < this.observerList.length ){
    if( this.observerList[i] === obj ){
      return i;
    }
    i++;
  }

  return -1;
};

ObserverList.prototype.removeAt = function( index ){
  this.observerList.splice( index, 1 );
};

다음으로, 주체와 옵저버 목록에 옵서버를 추가, 제거 또는 알리는 기능을 모델링 해 봅시다.

function Subject(){
  this.observers = new ObserverList();
}

Subject.prototype.addObserver = function( observer ){
  this.observers.add( observer );
};

Subject.prototype.removeObserver = function( observer ){
  this.observers.removeAt( this.observers.indexOf( observer, 0 ) );
};

Subject.prototype.notify = function( context ){
  var observerCount = this.observers.count();
  for(var i=0; i < observerCount; i++){
    this.observers.get(i).update( context );
  }
};

그런 다음 새로운 관찰자를 만들 수있는 골격을 정의합니다. 여기의 update 기능은 나중에 사용자 정의 동작으로 덮어 씁니다.

// The Observer
function Observer(){
  this.update = function(){
    // ...
  };
}

이제 위의 Observer 구성 요소를 사용하는 샘플 응용 프로그램에서 다음을 정의합니다.

  • 새로운 관찰 가능 체크 박스를 페이지에 추가하기 위한 버튼
  • 제목으로 작동하고 다른 체크 박스에 체크해야 함을 알리는 컨트롤 체크 박스
  • 새로운 체크 박스가 추가되는 컨테이너

그런 다음 새로운 관찰자를 페이지에 추가하고 update인터페이스를 구현하기위한 ConcreteSubject 및 ConcreteObserver 핸들러를 정의합니다. 이 구성 요소가 우리의 예제와 관련하여 무엇을 하는지에 대한 인라인 코멘트는 아래를 참조하십시오.

HTML

<button id="addNewObserver">Add New Observer checkbox</button>
<input id="mainCheckbox" type="checkbox"/>
<div id="observersContainer"></div>

Sample Script:

// Extend an object with an extension
function extend( obj, extension ){
  for ( var key in extension ){
    obj[key] = extension[key];
  }
}

// References to our DOM elements

var controlCheckbox = document.getElementById( "mainCheckbox" ),
  addBtn = document.getElementById( "addNewObserver" ),
  container = document.getElementById( "observersContainer" );


// Concrete Subject

// Extend the controlling checkbox with the Subject class
extend( controlCheckbox, new Subject() );

// Clicking the checkbox will trigger notifications to its observers
controlCheckbox.onclick = function(){
  controlCheckbox.notify( controlCheckbox.checked );
};

addBtn.onclick = addNewObserver;

// Concrete Observer

function addNewObserver(){

  // Create a new checkbox to be added
  var check = document.createElement( "input" );
  check.type = "checkbox";

  // Extend the checkbox with the Observer class
  extend( check, new Observer() );

  // Override with custom update behaviour
  check.update = function( value ){
    this.checked = value;
  };

  // Add the new observer to our list of observers
  // for our main subject
  controlCheckbox.addObserver( check );

  // Append the item to the container
  container.appendChild( check );
}

이 예제에서는 Observer 패턴을 구현하고 활용하는 방법을 살펴 보았고, Subject, Observer, ConcreteSubject 및 ConcreteObserver의 개념을 다루었습니다.

Observer와 Publish / Subscribe 패턴의 차이점

Observer 패턴은 자바 스크립트 세계에서 흔히 볼 수있는 유용한 기능이지만, Publish / Subscribe 패턴으로 알려진 변형을 사용하여 일반적으로 구현됩니다. 매우 유사하지만 주목할 차이점이 있습니다.

Observer 패턴은 주제 알림을 수신하고자 하는 관찰자 (또는 객체)가 이벤트 (대상)를 실행하는 객체에 이 관심을 등록해야 한다고 요구합니다.

그러나 Publish / Subscribe 패턴은 알림 (구독자)을 수신하려는 객체와 이벤트를 발생시키는 객체 (게시자) 사이에 위치하는 주제 / 이벤트 채널을 사용합니다. 이 이벤트 시스템을 통해 코드는 구독자가 필요로 하는 값을 포함하는 사용자 정의 인수를 전달할 수 있는 응용 프로그램 특정 이벤트를 정의 할 수 있습니다. 여기에 있는 아이디어는 구독자와 게시자 간의 종속성을 피하는 것입니다.

Observer 패턴과 다른 점은 적절한 이벤트 핸들러를 구현하는 모든 구독자가 게시자의 브로드 캐스트하는 주제 알림을 등록하고 수신 할 수 있기 때문입니다.

다음은 publish(), subscribe()unsubscribe() 기능을 구현하는 기능 구현과 함께 제공된 경우 Publish / Subscribe를 사용하는 방법의 예입니다.

// A very simple new mail handler

// A count of the number of messages received
var mailCounter = 0;

// Initialize subscribers that will listen out for a topic
// with the name "inbox/newMessage".

// Render a preview of new messages
var subscriber1 = subscribe( "inbox/newMessage", function( topic, data ) {

  // Log the topic for debugging purposes
  console.log( "A new message was received: ", topic );

  // Use the data that was passed from our subject
  // to display a message preview to the user
  $( ".messageSender" ).html( data.sender );
  $( ".messagePreview" ).html( data.body );

});

// Here's another subscriber using the same data to perform
// a different task.

// Update the counter displaying the number of new
// messages received via the publisher

var subscriber2 = subscribe( "inbox/newMessage", function( topic, data ) {

  $('.newMessageCounter').html( ++mailCounter );

});

publish( "inbox/newMessage", [{
  sender: "[email protected]",
  body: "Hey there! How are you doing today?"
}]);

// We could then at a later point unsubscribe our subscribers
// from receiving any new topic notifications as follows:
// unsubscribe( subscriber1 );
// unsubscribe( subscriber2 );

여기에 일반적인 생각은 느슨한 커플 링의 촉진입니다. 단일 객체가 다른 객체의 메소드를 직접 호출하지 않고 대신 다른 객체의 특정 작업 또는 활동을 구독하여 발생하면 이를 통지합니다.

장점

Observer와 Publish/Subscribe 패턴은 우리가 애플리케이션의 다른 부분들 간의 관계에 대해 열심히 생각하도록 합니다. 또한 직접적인 관계가 포함 된 계층을 식별하는 데 도움이 되며 대신 관계 집합과 관찰자 세트로 대체 될 수 있습니다. 이는 효과적으로 애플리케이션을 더 작고 느슨하게 결합 된 블록으로 분해하여 코드 관리 및 재사용 가능성을 개선하는 데 사용될 수 있습니다.

Observer 패턴을 사용하는 것의 또 다른 동기는 클래스를 단단히 결합하지 않고도 관련 객체간에 일관성을 유지해야하는 경우입니다. 예를 들어, 객체가 다른 객체에 대해 신경쓰지 않고 알릴 수 있어야 할 때 입니다.

두 패턴 중 하나를 사용할 때 관찰자와 대상간에 동적 인 관계가 존재할 수 있습니다. 이는 응용 프로그램의 서로 다른 부분이 긴밀하게 결합되어있을 때 구현하기가 쉽지 않을 수 있는 많은 유연성을 제공합니다.

모든 문제에 항상 최선의 해결책이 되는 것은 아니지만 이러한 패턴은 분리 된 시스템 설계를 위한 최상의 도구 중 하나로 남아 있으며 모든 JavaScript 개발자 유틸리티 벨트에서 중요한 도구로 간주되어야 합니다.

단점

결과적으로 이러한 패턴의 문제점 중 일부는 실제로 주요 이점에서 비롯됩니다. Publish/Subscribe 에서는 게시자와 구독자를 분리함으로써 때로는 응용 프로그램의 특정 부분이 제대로 작동하는지 보증하는 것이 어려워 질 수 있습니다.

예를 들어 게시자는 한 명 이상의 가입자가 자신의 말을 듣고 있다고 가정 할 수 있습니다. 일부 응용 프로그램 프로세스와 관련된 오류를 기록하거나 출력하기 위해 이러한 가정을 사용한다고 가정 해보십시오. 로깅을 수행하는 구독자가 충돌하거나 (또는 어떤 이유로 인해 작동하지 않는 경우) 게시자는 시스템의 분리 된 특성으로 인해 이를 볼 수 없습니다.

패턴의 또 다른 결점은 가입자가 서로의 존재에 대해 무지하고 게시자를 전환하는 비용에 대해 눈이 멀다는 것입니다. 구독자와 게시자 간의 동적 관계로 인해 업데이트 종속성을 추적하기가 어려울 수 있습니다.

Publish/Subscribe Implementations

Publish / Subscribe는 JavaScript 생태계에 매우 잘 어울립니다. 주로 ECMAScript 구현이 이벤트 중심이기 때문입니다. DOM은 스크립팅의 주요 상호 작용 API로 이벤트를 사용하므로 브라우저 환경에서 특히 그렇습니다.

즉, ECMAScript 나 DOM은 구현 코드에서 커스텀 이벤트 시스템을 생성하기 위한 핵심 객체 나 메소드를 제공하지 않습니다. (DOM에 바인딩되어 있으므로 일반적으로 유용하지 않은 DOM3 CustomEvent 제외).

다행스럽게도 dojo, jQuery (사용자 정의 이벤트) 및 YUI와 같은 널리 사용되는 JavaScript 라이브러리에는 이미 거의 손쉽게 Publish / Subscribe 시스템을 쉽게 구현할 수있는 유틸리티가 있습니다. 아래에서 몇 가지 예를 볼 수 있습니다.

// Publish

// jQuery: $(obj).trigger("channel", [arg1, arg2, arg3]);
$( el ).trigger( "/login", [{username:"test", userData:"test"}] );

// Dojo: dojo.publish("channel", [arg1, arg2, arg3] );
dojo.publish( "/login", [{username:"test", userData:"test"}] );

// YUI: el.publish("channel", [arg1, arg2, arg3]);
el.publish( "/login", {username:"test", userData:"test"} );


// Subscribe

// jQuery: $(obj).on( "channel", [data], fn );
$( el ).on( "/login", function( event ){...} );

// Dojo: dojo.subscribe( "channel", fn);
var handle = dojo.subscribe( "/login", function(data){..} );

// YUI: el.on("channel", handler);
el.on( "/login", function( data ){...} );


// Unsubscribe

// jQuery: $(obj).off( "channel" );
$( el ).off( "/login" );

// Dojo: dojo.unsubscribe( handle );
dojo.unsubscribe( handle );

// YUI: el.detach("channel");
el.detach( "/login" );

보의 자바 스크립트 (또는 다른 라이브러리)와 함께 Publish / Subscribe 패턴을 사용하고자하는 경우, AmplifyJS에는 라이브러리 또는 툴킷과 함께 사용할 수있는 깨끗한 라이브러리 독립적 방법이 포함되어 있습니다. Peter Higgins의 Radio.js (http://radio.uxder.com/), PubSubJS (https://github.com/mroderick/PubSubJS) 또는 Pure JS PubSub (https://github.com/phiggins42/bloody- jquery-plugins / blob / 55e41df9bf08f42378bb08b93efcb28555b61aeb / pubsub.js)도 비슷한 가치가있는 대안입니다.

특히 jQuery 개발자는 몇 가지 다른 옵션을 가지고 있으며 Peter Higgins의 jQuery 플러그인에서 GitHub의 Ben Alman (최적화 된) Pub / Sub jQuery 요점에 이르기까지 잘 구현 된 많은 구현 중 하나를 사용할 수 있습니다. 이 중 몇 가지에 대한 링크는 아래에서 찾을 수 있습니다.

Observer 패턴의 보통의 자바 스크립트 구현이 얼마나 효과적인지 평가할 수 있도록 pubsubz라는 프로젝트에서 GitHub에 공개 된 Publish / Subscribe 의 미니멀리스트 버전을 살펴 보겠습니다. 구독 취소, 게시 및 구독 취소의 개념에 대한 핵심 개념을 보여줍니다.

필자는 고전적인 Observer 패턴의 JavaScript 버전에서 볼 수있는 구현 방법과 메소드 서명에 밀접하게 관련되어 있으므로이 코드에서 예제를 기반으로 선택했습니다.

A Publish/Subscribe Implementation

var pubsub = {};

(function(myObject) {

    // Storage for topics that can be broadcast
    // or listened to
    var topics = {};

    // A topic identifier
    var subUid = -1;

    // Publish or broadcast events of interest
    // with a specific topic name and arguments
    // such as the data to pass along
    myObject.publish = function( topic, args ) {

        if ( !topics[topic] ) {
            return false;
        }

        var subscribers = topics[topic],
            len = subscribers ? subscribers.length : 0;

        while (len--) {
            subscribers[len].func( topic, args );
        }

        return this;
    };

    // Subscribe to events of interest
    // with a specific topic name and a
    // callback function, to be executed
    // when the topic/event is observed
    myObject.subscribe = function( topic, func ) {

        if (!topics[topic]) {
            topics[topic] = [];
        }

        var token = ( ++subUid ).toString();
        topics[topic].push({
            token: token,
            func: func
        });
        return token;
    };

    // Unsubscribe from a specific
    // topic, based on a tokenized reference
    // to the subscription
    myObject.unsubscribe = function( token ) {
        for ( var m in topics ) {
            if ( topics[m] ) {
                for ( var i = 0, j = topics[m].length; i < j; i++ ) {
                    if ( topics[m][i].token === token ) {
                        topics[m].splice( i, 1 );
                        return token;
                    }
                }
            }
        }
        return this;
    };
}( pubsub ));

Example: Using Our Implementation

이제 구현을 사용하여 다음과 같이 관심있는 이벤트를 게시하고 구독 할 수 있습니다.

/ Another simple message handler

// A simple message logger that logs any topics and data received through our
// subscriber
var messageLogger = function ( topics, data ) {
    console.log( "Logging: " + topics + ": " + data );
};

// Subscribers listen for topics they have subscribed to and
// invoke a callback function (e.g messageLogger) once a new
// notification is broadcast on that topic
var subscription = pubsub.subscribe( "inbox/newMessage", messageLogger );

// Publishers are in charge of publishing topics or notifications of
// interest to the application. e.g:

pubsub.publish( "inbox/newMessage", "hello world!" );

// or
pubsub.publish( "inbox/newMessage", ["test", "a", "b", "c"] );

// or
pubsub.publish( "inbox/newMessage", {
  sender: "[email protected]",
  body: "Hey again!"
});

// We can also unsubscribe if we no longer wish for our subscribers
// to be notified
pubsub.unsubscribe( subscription );

// Once unsubscribed, this for example won't result in our
// messageLogger being executed as the subscriber is
// no longer listening
pubsub.publish( "inbox/newMessage", "Hello! are you still there?" );

Example: User-Interface Notifications

다음으로 실시간 주식 정보를 표시하는 웹 애플리케이션이 있다고 가정 해 보겠습니다.

응용 프로그램에는 주식 통계를 표시하는 격자와 마지막 업데이트 시점을 표시하는 카운터가 있을 수 있습니다. 데이터 모델이 변경되면 응용 프로그램은 그리드와 카운터를 업데이트해야 합니다. 이 시나리오에서는 주제 (게시 주제 / 알림)가 데이터 모델이며 구독자는 그리드 및 카운터입니다.

구독자가 모델 자체가 변경 되었다는 알림을 받으면 적절하게 업데이트 할 수 있습니다.

우리 구현에서 구독자는 "newDataAvailable"이라는 주제를 듣고 새로운 주식 정보가 있는지 확인할 것 입니다. 이 알림에 새 알림이 게시되면 이 정보를 포함하는 그리드에 새 행을 추가하기 위해 gridUpdate가 실행됩니다. 마지막으로 업데이트 된 카운터를 업데이트하여 데이터가 마지막으로 추가 된 시간을 기록합니다.

// Return the current local time to be used in our UI later
getCurrentTime = function (){

   var date = new Date(),
         m = date.getMonth() + 1,
         d = date.getDate(),
         y = date.getFullYear(),
         t = date.toLocaleTimeString().toLowerCase();

        return (m + "/" + d + "/" + y + " " + t);
};

// Add a new row of data to our fictional grid component
function addGridRow( data ) {

   // ui.grid.addRow( data );
   console.log( "updated grid component with:" + data );

}

// Update our fictional grid to show the time it was last
// updated
function updateCounter( data ) {

   // ui.grid.updateLastChanged( getCurrentTime() );
   console.log( "data last updated at: " + getCurrentTime() + " with " + data);

}

// Update the grid using the data passed to our subscribers
gridUpdate = function( topic, data ){

  if ( data !== undefined ) {
     addGridRow( data );
     updateCounter( data );
   }

};

// Create a subscription to the newDataAvailable topic
var subscriber = pubsub.subscribe( "newDataAvailable", gridUpdate );

// The following represents updates to our data layer. This could be
// powered by ajax requests which broadcast that new data is available
// to the rest of the application.

// Publish changes to the gridUpdated topic representing new entries
pubsub.publish( "newDataAvailable", {
  summary: "Apple made $5 billion",
  identifier: "APPL",
  stockPrice: 570.91
});

pubsub.publish( "newDataAvailable", {
  summary: "Microsoft made $20 million",
  identifier: "MSFT",
  stockPrice: 30.85
});

예 : Ben Alman의 Pub / Sub 구현을 사용하여 응용 프로그램 분리

다음 무비 등급 예에서는 Ben Alman의 jQuery 구현 인 Publish / Subscribe를 사용하여 사용자 인터페이스를 분리 할 수있는 방법을 보여줍니다. 등급을 제출하는 것이 새로운 사용자 및 등급 데이터를 사용할 수 있다는 사실을 공개하는 효과가 있는지 확인하십시오.

이러한 주제에 대한 가입자에게 남겨진 것은 해당 데이터에서 일어나는 일을 위임하는 것입니다. 여기서는 새 데이터를 기존 배열에 넣은 다음 템플릿에 Underscore 라이브러리의 .template () 메서드를 사용하여 렌더링합니다.

HTML/Templates
<script id="userTemplate" type="text/html">
   <li><%= name %></li>
</script>


<script id="ratingsTemplate" type="text/html">
   <li><strong><%= title %></strong> was rated <%= rating %>/5</li>
</script>


<div id="container">

   <div class="sampleForm">
       <p>
           <label for="twitter_handle">Twitter handle:</label>
           <input type="text" id="twitter_handle" />
       </p>
       <p>
           <label for="movie_seen">Name a movie you've seen this year:</label>
           <input type="text" id="movie_seen" />
       </p>
       <p>

           <label for="movie_rating">Rate the movie you saw:</label>
           <select id="movie_rating">
                 <option value="1">1</option>
                  <option value="2">2</option>
                  <option value="3">3</option>
                  <option value="4">4</option>
                  <option value="5" selected>5</option>

          </select>
        </p>
        <p>

            <button id="add">Submit rating</button>
        </p>
    </div>



    <div class="summaryTable">
        <div id="users"><h3>Recent users</h3></div>
        <div id="ratings"><h3>Recent movies rated</h3></div>
    </div>

 </div>
JavaScript
;(function( $ ) {

  // Pre-compile templates and "cache" them using closure
  var
    userTemplate = _.template($( "#userTemplate" ).html()),
    ratingsTemplate = _.template($( "#ratingsTemplate" ).html());

  // Subscribe to the new user topic, which adds a user
  // to a list of users who have submitted reviews
  $.subscribe( "/new/user", function( e, data ){

    if( data ){

      $('#users').append( userTemplate( data ));

    }

  });

  // Subscribe to the new rating topic. This is composed of a title and
  // rating. New ratings are appended to a running list of added user
  // ratings.
  $.subscribe( "/new/rating", function( e, data ){

    if( data ){

      $( "#ratings" ).append( ratingsTemplate( data ) );

    }

  });

  // Handler for adding a new user
  $("#add").on("click", function( e ) {

    e.preventDefault();

    var strUser = $("#twitter_handle").val(),
       strMovie = $("#movie_seen").val(),
       strRating = $("#movie_rating").val();

    // Inform the application a new user is available
    $.publish( "/new/user", { name: strUser } );

    // Inform the app a new rating is available
    $.publish( "/new/rating", { title: strMovie, rating: strRating} );

    });

})( jQuery );

예 : Ajax 기반 jQuery 애플리케이션 분리

마지막 예제에서는 개발 프로세스 초기에 Pub / Sub를 사용하여 코드를 분리하는 방법을 사용하여 이후의 잠재적인 고통스러운 리팩토링을 줄일 수 있습니다.

Ajax가 많이 사용되는 응용 프로그램에서는 요청에 대한 응답을 받으면 하나 이상의 고유 한 작업을 수행하기를 원합니다. 단순히 post-request 콜백 로직을 모두 성공 콜백에 추가 할 수는 있지만 이 방법에는 단점이 있습니다.

고도로 결합 된 응용 프로그램은 증가 된 함수 간 / 코드 종속성으로 인해 기능을 재사용하는 데 필요한 노력을 때로는 증가시킵니다. 이것이 의미하는 바는 콜백에서 POST 요청 로직을 하드 코드로 유지하는 것은 결과 세트를 한 번만 잡으려고 할 때 유용 할 수 있지만 동일한 데이터 소스에 대한 Ajax 호출을 추가로 만들려는 경우 적절하지 않습니다. 코드의 여러 부분을 여러 번 다시 작성하지 않고도 (그리고 다른 종료 동작) 사용할 수 있습니다. 동일한 데이터 소스를 호출하고 나중에 일반화하는 각 레이어를 거치지 않고 처음부터 pub / sub를 사용하여 시간을 절약 할 수 있습니다.

Observers를 사용하면 다른 이벤트에 대한 애플리케이션 전반의 통지를 다른 패턴을 사용하여 덜 우아하게 처리 할 수있는 어떤 수준의 세분화 수준으로 쉽게 분리 할 수 ​​있습니다.

아래 예제에서 사용자가 검색 쿼리를하고 싶다고 표시하면 요청한 항목이 반환되고 실제 데이터를 사용할 수있을 때 하나의 항목 알림이 어떻게 생성되는지 확인하십시오. 구독자에게 이러한 이벤트 (또는 반환 된 데이터)에 대한 지식을 사용하는 방법을 결정해야합니다. 이것의 이점은 우리가 원한다면 다른 방식으로 리턴 된 데이터를 사용하는 10 명의 다른 가입자를 가질 수 있지만 Ajax 계층에 관한 한 상관하지 않는다는 것입니다. 유일한 의무는 데이터를 요청하고 반환 한 다음 사용하려는 사람에게 데이터를 전달하는 것입니다. 이러한 우려의 분리는 코드의 전반적인 디자인을 조금 더 깨끗하게 만들 수 있습니다.

HTML/Templates:
<form id="flickrSearch">

   <input type="text" name="tag" id="query"/>

   <input type="submit" name="submit" value="submit"/>

</form>



<div id="lastQuery"></div>

<ol id="searchResults"></ol>



<script id="resultTemplate" type="text/html">
    <% _.each(items, function( item ){ %>
        <li><img src="<%= item.media.m %>"/></li>
    <% });%>
</script>
JavaScript:
;(function( $ ) {

   // Pre-compile template and "cache" it using closure
   var resultTemplate = _.template($( "#resultTemplate" ).html());

   // Subscribe to the new search tags topic
   $.subscribe( "/search/tags", function( e, tags ) {
       $( "#lastQuery" )
                .html("<p>Searched for:<strong>" + tags + "</strong></p>");
   });

   // Subscribe to the new results topic
   $.subscribe( "/search/resultSet", function( e, results ){

       $( "#searchResults" ).empty().append(resultTemplate( results ));

   });

   // Submit a search query and publish tags on the /search/tags topic
   $( "#flickrSearch" ).submit( function( e ) {

       e.preventDefault();
       var tags = $(this).find( "#query").val();

       if ( !tags ){
        return;
       }

       $.publish( "/search/tags", [ $.trim(tags) ]);

   });


   // Subscribe to new tags being published and perform
   // a search query using them. Once data has returned
   // publish this data for the rest of the application
   // to consume

   $.subscribe("/search/tags", function( e, tags ) {

       $.getJSON( "http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?", {
              tags: tags,
              tagmode: "any",
              format: "json"
            },

          function( data ){

              if( !data.items.length ) {
                return;
              }

              $.publish( "/search/resultSet", { items: data.items } );
       });

   });


})( jQuery );

Observer 패턴은 응용 프로그램 디자인에서 다양한 시나리오를 분리하는 데 유용하며, 아직 사용하지 않은 경우에는 오늘 언급 된 미리 작성된 구현 중 하나를 선택하여 시험해 보는 것이 좋습니다. 시작하기 쉬운 디자인 패턴 중 하나이지만 가장 강력한 것 중 하나입니다.

results matching ""

    No results matching ""