The Observer Pattern

The Observer is a design pattern where an object (known as a subject) maintains a list of objects depending on it (observers), automatically notifying them of any changes to state.

When a subject needs to notify observers about something interesting happening, it broadcasts a notification to the observers (which can include specific data related to the topic of the notification).

When we no longer wish for a particular observer to be notified of changes by the subject they are registered with, the subject can remove them from the list of observers.

It's often useful to refer back to published definitions of design patterns that are language agnostic to get a broader sense of their usage and advantages over time. The definition of the Observer pattern provided in the GoF book, Design Patterns: Elements of Reusable Object-Oriented Software, is:

"One or more observers are interested in the state of a subject and register their interest with the subject by attaching themselves. When something changes in our subject that the observer may be interested in, a notify message is sent which calls the update method in each observer. When the observer is no longer interested in the subject's state, they can simply detach themselves."

We can now expand on what we've learned to implement the Observer pattern with the following components:

  • Subject: maintains a list of observers, facilitates adding or removing observers
  • Observer: provides a update interface for objects that need to be notified of a Subject's changes of state
  • ConcreteSubject: broadcasts notifications to observers on changes of state, stores the state of ConcreteObservers
  • ConcreteObserver: stores a reference to the ConcreteSubject, implements an update interface for the Observer to ensure state is consistent with the Subject's

First, let's model the list of dependent Observers a subject may have:

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

Next, let's model the Subject and the ability to add, remove or notify observers on the observer list.

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

We then define a skeleton for creating new Observers. The update functionality here will be overwritten later with custom behaviour.

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

In our sample application using the above Observer components, we now define:

  • A button for adding new observable checkboxes to the page
  • A control checkbox which will act as a subject, notifying other checkboxes they should be checked
  • A container for the new checkboxes being added

We then define ConcreteSubject and ConcreteObserver handlers for both adding new observers to the page and implementing the updating interface. See below for inline comments on what these components do in the context of our example.

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( extension, obj ){
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( new Subject(), controlCheckbox ); // 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( new Observer(), check ); // 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 );
}

In this example, we looked at how to implement and utilize the Observer pattern, covering the concepts of a Subject, Observer, ConcreteSubject and ConcreteObserver.

Differences Between The Observer And Publish/Subscribe Pattern

Whilst the Observer pattern is useful to be aware of, quite often in the JavaScript world, we'll find it commonly implemented using a variation known as the Publish/Subscribe pattern. Whilst very similar, there are differences between these patterns worth noting.

The Observer pattern requires that the observer (or object) wishing to receive topic notifications must subscribe this interest to the object firing the event (the subject).

The Publish/Subscribe pattern however uses a topic/event channel which sits between the objects wishing to receive notifications (subscribers) and the object firing the event (the publisher). This event system allows code to define application specific events which can pass custom arguments containing values needed by the subscriber. The idea here is to avoid dependencies between the subscriber and publisher.

This differs from the Observer pattern as it allows any subscriber implementing an appropriate event handler to register for and receive topic notifications broadcast by the publisher.

Here is an example of how one might use the Publish/Subscribe if provided with a functional implementation powering publish(),subscribe() and unsubscribe() behind the scenes:

Learning JavaScript Design Patterns The Observer Pattern的更多相关文章

  1. Learning JavaScript Design Patterns The Module Pattern

    The Module Pattern Modules Modules are an integral piece of any robust application's architecture an ...

  2. Learning JavaScript Design Patterns The Singleton Pattern

    The Singleton Pattern The Singleton pattern is thus known because it restricts instantiation of a cl ...

  3. Learning JavaScript Design Patterns The Constructor Pattern

    In classical object-oriented programming languages, a constructor is a special method used to initia ...

  4. AMD - Learning JavaScript Design Patterns [Book] - O'Reilly

    AMD - Learning JavaScript Design Patterns [Book] - O'Reilly The overall goal for the Asynchronous Mo ...

  5. use getters and setters Learning PHP Design Patterns

    w Learning PHP Design Patterns Much of what passes as OOP misuses getters and setters, and making ac ...

  6. Learning PHP Design Patterns

    Learning PHP Design Patterns CHAPTER 1 Algorithms handle speed of operations, and design patterns ha ...

  7. [Design Patterns] 3. Software Pattern Overview

    When you're on the way which is unknown and dangerous, just follow your mind and steer the boat. 软件模 ...

  8. JavaScript Design Patterns: Mediator

    The Mediator Design Pattern The Mediator is a behavioral design pattern in which objects, instead of ...

  9. [Design Patterns] 4. Creation Pattern

    设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结,使用设计模式的目的是提高代码的可重用性,让代码更容易被他人理解,并保证代码可靠性.它是代码编制真正实现工程化. 四个关键元素 ...

随机推荐

  1. 正确配置jstl的maven依赖,jar包冲突的问题终于解决啦

    困扰了两天的问题,非常头疼,今天终于有了解决思路了,说到底,还是对maven不够了解吧.总是抱怨maven不好用,出现各种无厘头的问题,原来这些都是归于对它不够了解不够熟悉,它提供了很好的解决思路,只 ...

  2. Tomacat服务器的安装和配置

    一, Tomcat服务器的下载地址(Apache Tomcat的官网):  http://tomcat.apache.org/download-70.cgi 这里为了稳定性安装的版本为7.0. 截止目 ...

  3. Oracle客户端PL_SQL的安装

    Oracle数据库的操作大多还是在客户端完成的,因此在众多的客户端软件中我选择了PL_SQL,一下谈谈PL_SQL的基本安装和操作,以及在操作中碰到的一些问题: 1. 首先下载PL_SQL客户端软件, ...

  4. 机器学习算法与Python实践之(四)支持向量机(SVM)实现

    机器学习算法与Python实践之(四)支持向量机(SVM)实现 机器学习算法与Python实践之(四)支持向量机(SVM)实现 zouxy09@qq.com http://blog.csdn.net/ ...

  5. Android 在AlertDialog里添加布局控件

    android里很多时候需要在弹出的AlertDialog里有自己的控件,填写信息,比如弹出一个登陆对话框 那么首先你就要创建这么一个布局的inputphonenum.xml文件了 <?xml ...

  6. html5学习链接

    http://www.runoob.com/tags/html-colorpicker.html

  7. wzplayer V1.6正式版(无限制)不支持加密版本 2014-07-08

    2014-07-08 修复部分视频倾斜问题,同时给受影响的客户给予更新. wzplayer V1.6正式版(无限制)不支持加密版本 发布于官方,下载地址wzplayer V1.6正式版(无限制)不支持 ...

  8. CentOS5.5 下编译安装 LAMP

    大纲 1.安装gcc编译器 2.卸载rpm安装的http和mysql软件 3.编译安装php依赖包 4.安装apache软件 5.安装mysql软件 6.安装php软件 7.安装memcache ph ...

  9. bzoj1705

    话说这题很久以前就写过,然后好像一直忘了写题解…… 以前看这道题还觉得挺难的,现在觉得好水 首先朴素的想法肯定是动归 f[i,j]表示到处理到第i根电线,最终高度为j的最小花费 f[i,j]:=min ...

  10. PHP ‘asn1_time_to_time_t’函数内存损坏漏洞

    漏洞名称: PHP ‘asn1_time_to_time_t’函数内存损坏漏洞 CNNVD编号: CNNVD-201312-348 发布时间: 2013-12-18 更新时间: 2013-12-18 ...