JavaScript:为什么在Addy';什么是观察者模式?

JavaScript:为什么在Addy';什么是观察者模式?,javascript,design-patterns,observer-pattern,observers,Javascript,Design Patterns,Observer Pattern,Observers,我正在研究Addy Osmani的书《JavaScript设计模式》中Observer模式的设计模式示例。我的问题是,为什么在他的模式实现中有如此多的抽象层次很重要 例如,在他的示例中,只需添加一个观察者(“推”一个观察者到一个数组),这包括: 使用本机push()方法 创建ObjectList.add()方法 使用主题对象继承/扩展对象列表对象 创建Subject.addObserver()方法,该方法将用作接口,但在引擎盖下使用ObjectList.add方法 为新对象扩展Subject.

我正在研究Addy Osmani的书《JavaScript设计模式》中Observer模式的设计模式示例。我的问题是,为什么在他的模式实现中有如此多的抽象层次很重要

例如,在他的示例中,只需添加一个观察者(“推”一个观察者到一个数组),这包括:

  • 使用本机
    push()
    方法
  • 创建
    ObjectList.add()
    方法
  • 使用
    主题
    对象继承/扩展
    对象列表
    对象
  • 创建
    Subject.addObserver()
    方法,该方法将用作接口,但在引擎盖下使用
    ObjectList.add
    方法
  • 为新对象扩展
    Subject.addObserver()
    方法
  • 通过对新扩展的对象调用
    addObserver()
    方法来实现它
下面是整个设计模式的代码示例:

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

// The Observer
function Observer(){
  this.update = function(){
    // ...
  };
}
现在,我将他的实现与相同模式的其他实现(书籍和博客)进行了比较。看起来Addy比observer模式的其他实现者添加了更多的抽象。问题是,为什么?难道不能更简单地通过继承
ObserverList
对象来实现吗?这是否能像Addy那样实现更大程度的解耦?如果是,具体情况如何?设计模式本身不是创造了一种解耦吗?似乎
主题
对象带来了很多不必要的代码

难道不能更简单地通过从 观察者列表对象

对。通过继承,不会重新实现所有的
ObserverList
方法。代码更少,测试更少,文档更少

这是否实现了更大程度的解耦,就像Addy那样 是吗

是的,这是因为
主题
对象的接口根本不依赖于
观察者列表
接口(因为
Subject
重新实现了它自己与这些方法的接口,所以它的接口与
observer列表
接口是分离的。这有它的优点和缺点。重新实现一个接口应该有很好的理由,因为它大部分只是一堆额外的代码,没有添加任何实际有用的功能。

如果是,具体情况如何

通过重新实现您自己的版本,将实际接口隐藏到
observer列表
,从而使这两个接口解耦。对底层
observer列表
接口的更改可以被
主题
接口隐藏。而
主题
实现仍然依赖并耦合到
observer列表
接口,
主题
接口本身独立于
观察者列表
接口。但是,也有很多理由不这样做,所以不要认为每个接口都应该与其他接口分离。这将是一场灾难

似乎Subject对象带来了很多不必要的代码

是的


当您想要使用来自另一个对象的功能,并且想要将该功能的部分或全部公开给您自己对象的客户时,您有许多设计选择

  • 您的对象可以从另一个对象继承,从而自动公开其整个接口(并允许您根据需要覆盖某些方法)

  • 您的对象可以包含另一个对象的实例,并公开该对象,以便您的对象的用户可以直接访问另一个对象,而无需重新实现任何内容。在这种特定情况下,这可能是我的选择,因此在
    主题中使用公开的观察者进行编码d看起来像这样:

    var s=新主题();
    s、 observer.add(函数(){
    //当主题更改时,将调用此函数
    });

  • 您的对象可以包含另一个对象的私有实例,您可以在该私有实例上手动创建自己的接口

  • 在OO语言中,这三种选择有时被称为isA、hasA和hidesA。在第一种情况下,您的对象“是一个”
    observer列表
    对象。在第二种情况下,您的对象“有一个”
    observer列表
    对象。在第三种情况下,您的对象“在其实现中隐藏一个”
    observer列表
    对象


    每种设计选择都有利弊。没有一种选择总是正确或错误的,因为每种选择都有不同的利弊,哪种选择是最佳的取决于具体情况

    选项1)继承的情况通常是当您的对象是基础对象的扩展时,从架构上讲,它被认为是基础对象的更强大版本和/或它可能重写基础对象上的方法。这里的情况并非如此。a
    Subject()
    对象不是一个功能更强大的
    observer列表
    对象。它是一种使用
    observer列表
    的不同类型的对象

    选项2)包含ObserverList的公共实例并允许对象的用户使用该公共实例的情况是,当对象实际上是一种不同类型的对象,但它希望使用另一种类型对象的功能并向其用户公开时。在我看来,这里发生的事情主要是这样的

    选项3)的情况是,您不希望对象的接口与任何其他接口之间存在任何接口依赖关系。在这种情况下,您不能公开其他对象的接口来让对象的用户使用这些接口。相反,你有
    <button id="addNewObserver">Add New Observer checkbox</button>
    <input id="mainCheckbox" type="checkbox"/>
    <div id="observersContainer"></div>
    
    // 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 );
    }