zoukankan      html  css  js  c++  java
  • 翻译:观察者模式—使用JavaScript实现

    原文出处:http://www.codeproject.com/KB/scripting/Observer_Pattern_JS.aspx

    介绍

        任何一个曾经使用过JavaScript的人都应该了解创建自定义对象的过程。不熟悉在JavaScript 中用面向对象方法(OOP)来创建对象的读者将在这里读到关于这方面的简要的介绍。本篇文章主要介绍 观察者模式 JavaScript中的实现。

    JavaScript的简单介绍

        JavaScript是一种基于原型(prototype)的脚本语言(以前叫LiveScript)。它的语法松散,类似于C语言。该脚本语言是由Netscape社团开发的,用于Navigator浏览器。和C语言一样,JavaScript本身没有构造函数和析构函数。C语言依赖于标准输入/输出库;而JavaScript则依赖于执行它的宿主环境。这种脚本语言使用自定义函数,其他语言中可能会被称为过程、例程或功能。基于webJavaScript主要用于在web页面中和DOM(即文档对象模型)进行交互,以便完成一些仅使用HTML无法实现的功能。JScript是微软推出的和 JavaScript 对应的一种脚本语言,它用于微软的IE浏览器当中。

    JavaScript中创建自定义对象

        创建一个新的JavaScript对象需要2个步骤。首先,你需要创建一个函数,函数名就是新建的类的名称。这个函数也就是我们经常说的 构造函数。然后,你必须使用 new 操作符,后面跟上对象的名称以及一些必要的参数来创建一个对象的实例。下面的代码定义了一个 Person 函数,然后使用 new 操作符创建了 Person 的实例:

    function Person(name, surname)
    {
        
    this.name = name;
        
    this.surname = surname;
    }

    var salvo = new Person('Salvatore''Vetro');

        this关键字是指向你当前正在执行的对象的实例,因此,允许你在当前对象上添加或修改对象的属性。

    如何为对象添加方法?

        JavaScript中,通过调用绑定了原型属性的 构造函数 来创建任何对象。添加新方法的语法如下:

    customeObject.prototype.newMethodName = function()
    {
        //方法体
    }
    Person.prototype.Speak = function() {};

        如果你在一个对象的prototype属性上添加一个方法,那么,所有通过该对象的构造函数创建的实例都具有这个新方法。注意,prototype 本身也是一个对象,而且能够通过 对象文字语法(object literal syntax 来为它定义属性和方法:


    function NewObject()
    {
        alert(
    "I am a new object.");
    }

    NewObject.prototype 
    =
    {
        alert1 : 
    function(str){alert(str);}, //新方法
        name : 'As you want'//新属性
        Alert2 : function(){alert('Bye.');}, //新方法
    };

    var newObject = new NewObject();
    newObject.alert1(
    "Ciao");
    newObject.name;
    newObject.alert2();

    脚本每次尝试读/写对象的属性的时候,JavaScript会按照特定的顺序来搜寻和指定名称匹配的属性。顺序如下:

    l         如果该属性已经分配给当前对象,则使用该属性的值;

    l         如果在当前对象中没有搜索到指定的属性,则检查该对象构造函数的prototype属性的值;

    l         沿着prototype链一直查找,直到找到匹配的属性(已经为它赋值),否则,一直会查找到 Object 对象。因此,如果你改变了构造函数的prototype属性的值,并且没有在构造函数的某个实例中重写属性的值,JavaScript会返回对象当前prototype属性的值。

    观察者模式类图

        观察者模式 定义了一个 主题对象 和若干 观察者对象之间的一对多的依赖。因此,当 主题对象 改变了其状态,所有的 观察者对象 都会被通知并且自动更新。

    观察者模式序列图

    1 观察者模式类图

    l         参与者:

    u        Subject(抽象主题)

    Ø    能够知道它自己的观察者,若干观察者对象可能监视一个主题对象;

    Ø    提供一个接口,用来附加和取消观察者对象;

    u        Observer(抽象观察者)

    Ø    它为对象定义了一个(自我)更新的接口,并且当主题对象发生改变的时候能够被通知;

    u        ConcreteSubject(具体主题)

    Ø    存储具体观察者感兴趣的状态;

    Ø    当它的状态改变的时候,通知它所有的观察者对象;

    u        ConcreteObserver(具体观察者)

    Ø    持有一个具体主题的引用;

    Ø    存储和主题对象一致的状态,并且该能够保留该状态;

    Ø    实现抽象观察者的Update接口,以便能够同主题对象的状态保持一致;

    l         工作方式:

    u      当某个具体主题状态发生改变的时候,通知它的所有观察者对象,以便保证这些观察者对象的状态同它的状态保持一致;

    u      当被告知具体主题对象发生改变,一个具体观察者对象可能会去查询主题对象,以便获得一个消息;该观察者使用这个消息来使它的状态同主题对象的状态保持一致;

    观察者模式序列图

        下面这张交互图展示了一个主题对象和两个观察者对象之间的协作:

    2 观察者模式序列图

        观察者对象初始化变化请求,它不会立即更新,直到通过主题对象的Notify方法来获得一个变化的通知。主题的Notify方法并非总是被主题对象调用,它也能够被观察者对象调用,或者完全被其他不同类型的对象调用。

    接下来我们将做什么

        现在你已经知道了什么是 观察者模式,并且也了解了如何使用JavaScript来创建自己的对象。正如你在图1中看到的一样,你必须在 主题类 中定义2个方法(Attach Detach)。为了达到这个目的,你需要一个 集合 来完成 Attach/Detach 方法。现在是时候写你的第一个JavaScript ArrayList 对象了。在定义 ArrayList 对象之前,你必须知道 ArrayList 能够完成一些什么样的功能。

        ArrayList功能列表:

        1、Count

        2、Add

        3、GetAt

        4、Clear

        5、RemoveAt

        6、Insert

        7、IndexOf

        8、LastIndexOf

    function ArrayList()
    {
        
    //初始化空数组
        this.aList = [];
    }
    ArrayList.prototype.Count 
    = function()
    {
        
    return this.aList.length;
    }
    ArrayList.prototype.Add 
    = function(object)
    {
        
    //把新添加的对象放在数组的最后
        return this.aList.push(object);
    }
    ArrayList.prototype.GetAt 
    = function(index)  //index必须是整数
    {
        
    if (index > -1 && index < this.aList.length)
        {
           
    return this.aList[index];
        }
        
    else
        {
           
    return undefined;  //超出了数组范围,返回undefined
        }
    }
    ArrayList.prototype.clear 
    = function()
    {
        
    this.aList = [];
    }
    ArrayList.prototype.RemoveAt 
    = function(index)
    {
        
    var m_count = this.aList.length;
        
    if (m_count > 0 && index > -1 && index < this.aList.length)
        {
           
    switch (index)
           {
               
    case 0:
                  
    //移除数组中的第一个元素
                  this.aList.shift();
                  
    break;
               
    case m_count - 1:
                  
    //移除数组中最后一个元素
                  this.aList.pop();
                  
    break;
               
    default:
                  
    //获取前面index个元素,生成一个新数组
                  var head = this.aList.slice(0, index);
                  
    //获取index之后的元素,生成一个新数组
                  var tail = this.aList.slice(index + 1);
                  
    //组合两个子数组
                  this.aList = head.concat(tail);
                  
    break;
           }
        }
    }
    ArrayList.prototype.Insert 
    = function(object, index)
    {
        
    var m_count = this.aList.length;
        
    var m_returnValue = -1;
        
    if (index > -1 && index <= m_count)
        {
           
    switch (index)
           {
               
    case 0:
                  
    this.aList.unshift(object);
                  m_returnValue 
    = 0;
                  
    break;
               
    case m_count:
                  
    this.aList.push(object);
                  m_returnValue 
    = m_count;
                  
    break;
               
    default:
                  
    var head = this.aList.slice(0, index - 1);
                  
    var tail = this.aList.slice(index);
                  
    this.aList = header.concat(tail.unshift(object));
                  m_returnValue 
    = index;
                  
    break;
           }
        }
        
    return m_returnValue;
    }
    ArrayList.prototype.IndexOf 
    = function(object, startIndex)
    {
        
    var m_count = this.aList.length;
        
    var m_returnValue = -1;
        
    if (startIndex > -1 && startIndex < m_count)
        {
           
    var i = startIndex;
           
    while (i < m_count)
           {
               
    //循环遍历数组,直到找到和参数object相同的元素
               if (this.aList[i] == object)
               {
                   m_returnValue 
    = i;
                  
    break;
               }
               i
    ++;
           }
        }
        
    return m_returnValue;
    }
    ArrayList.prototype.LastIndexOf 
    = function(object, startIndex)
    {
        
    var m_count = this.aList.length;
        
    var m_returnValue = -1;
        
    if (startIndex > -1 && startIndex < m_count)
        {
           
    var i = m_count - 1;
           
    while (i >= startIndex)
           {
               
    if (this.aList[i] == object)
               {
                  m_returnValue 
    = i;
                  
    break;
               }
               i
    --;
           }
        }
        
    return m_returnValue;
    }

        非常不错!你现在可以创建观察者模式中的 观察者类 主题类 了。


    观察者模式中的
    观察者类

        你只需要定义Update方法:

    function Observer()
    {
        
    this.Update = function()
        {
           
    return;
        }
    }


    观察者模式中的
    主题类

        好了,让我们定义三个主要的方法:NofifyAddObserverRemoveObserver

    function Subject()
    {
        
    this.observers = new ArrayList();
    }
     
    Subject.prototype.Notify 
    = function(context)
    {
        
    var m_count = this.observers.Count;
        
    for (var i = 0; i < m_count; i++)
        {
           
    this.observers.GetAt(i).Update(context);
        }
    }
     
    Subject.prototype.AddObserver 
    = function(observer)
    {
        
    if (!observer.Update)
        {
           
    throw 'Wrong parameter';
        }
        
    this.observers.Add(observer);
    }
     
    Subject.prototype.RemoveObserver 
    = function(observer)
    {
        
    if (!observer.Update)
        {
           
    throw 'Wrong parameter';
        }
        
    this.observers.RemoveAt(this.observers.IndexOf(observer, 0));
    }

     
    JavaScript
    中的继承

        JavaScript中有许多方法来模拟继承。一个非常简单的方式是定义一个名称为 Inherits 的方法,在该方法中,你可以复制一个对象的所有属性和方法到另一个对象中。

    function Inherits(base, extension)
    {
        
    for (var property in base)
        {
           
    try
           {
               extention[property] 
    = base[property];
           }
           
    catch (e)
           {
               
    //Exception Handle
           }
        }
    }


    一个简单的实现

        现在你需要实现客户端功能以便能够附加“观察者”到相应的“主题”上。例如,你可以创建一个简单的应用程序,在应用程序中定义一个主checkbox,作为被观察对象,然后,再定义另一些checkbox作为观察者。当 主题 对象自身的状态发生变化的时候,它将通知所有依附它的 观察者。在 主题 对象中各自独立的 观察者 将独自处理这个消息。

    /************* Concrete Subject *************/
    var mainCheck = document.createElement("input");
    mainCheck.type 
    = 'checkbox';
    mainCheck.id 
    = 'MainCheck';
     
    Inherits(
    new Subject(), mainCheck); 
    mainCheck[
    "onclick"= new Function("mainCheck.Notify(mainCheck.checked)");
     
    /**************** Observer ****************/
    var obsCheck1 = document.createElement("input");
    var obsCheck2 = document.createElement("input");
     
    obsCheck1.type 
    = 'checkbox';
    obsCheck1.id 
    = 'Obs1';
     
    obsCheck2.type 
    = 'checkbox';
    obsCheck2.id 
    = 'Obs2';
     
    Inherits(
    new Observer(), obsCheck1);
    Inherits(
    new Observer(), obsCheck2);
     
    obsCheck1.Update 
    = function(value)
    {
        
    this.checked = value;
    }
     
    obsCheck2.Update 
    = function(value)
    {
        
    this.checked = value;
        
    //Add
    }
     
    mainCheck.AddObserver(obsCheck1);
    mainCheck.AddObserver(obsCheck2);


    附:程序实例

        在特定情况下,所有的 观察者 对象都使用同样的方式来处理(主题对象的)消息。当对应的 主题 对象通知这些观察者有关它的“新状态”的时候,任何一个观察者都会改变自己的状态,并把这个可被观察的状态作为它的新的值。

    点击此处下载示例代码

  • 相关阅读:
    ElasticSearch原理
    redis master配置了密码进行主从同步
    redis sentinel 高可用(HA)方案部署,及python应用示例
    Linux Redis集群搭建与集群客户端实现
    字符串倒序
    单链表反转
    【python】面试常考数据结构算法
    面试中的排序算法总结
    Memcached 真的过时了吗?
    Activity生命周期
  • 原文地址:https://www.cnblogs.com/Selfocus/p/1326935.html
Copyright © 2011-2022 走看看