【发布时间】:2023-04-11 05:23:02
【问题描述】:
我正在研究 Addy Osmani 的“JavaScript 设计模式”一书中的观察者模式的设计模式示例。我的问题是,为什么在他的模式实现中有这么多层次的抽象很重要?
例如,在他的示例中,只是为了添加一个观察者(将一个观察者“推”到一个数组中),这涉及到:
- 使用原生的
push()方法。 - 创建
ObjectList.add()方法。 - 使用
Subject对象继承/扩展ObjectList对象。 - 创建用作接口的
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(){
// ...
};
}
这是实现/用法:
HTML
<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 );
}
现在我将他的实现与相同模式的其他实现(书籍和博客)进行了比较。似乎 Addy 比观察者模式的其他实现者添加了更多的抽象。问题是,为什么?难道这不能通过继承ObserverList 对象更简单地实现吗?这是否像 Addy 那样实现了更大程度的解耦?如果是这样,那到底是怎么回事?设计模式本身不会产生解耦吗?好像Subject 对象带来了很多不必要的代码。
【问题讨论】:
-
这里的
Subject对象需要更多的代码。在我看来,它既可以继承自ObserverList,也可以包含可公开访问的ObserverList,然后在这些情况下,它不必定义全新的观察者接口并重新实现一大堆已经存在的方法在ObserverList对象中。 -
这个问题对我来说感觉像是一个讨论问题 + 主要是基于意见的问题。
-
@jfriend00:我认为这个问题最适合 SO,因为它涉及理解如何在 JS 中实现这种设计模式的基本“理论”,这被认为是关于该主题的基础/权威书籍。这不仅仅是一个需要优化的任意代码块。
-
@shmuli -
Subject对象的接口与ObserverList对象的接口分离。Subject对象的实现仍然高度依赖于ObserverList。不同之处在于,如果ObserverList接口发生更改,您不必更改Subject接口,但您必须修复Subject实现以使用更改后的ObserverList。以这种方式解耦它会给您(Subject对象的实现者)带来保持最新的负担,但允许您永远不会更改Subject接口。这就是解耦。 -
@shmuli - 这种接口解耦的代价是重新实现一个本来很好的接口,重新记录一个本来已经记录的接口并重新测试一个新实现的接口。所以解耦两个接口并不总是最好的选择。而且,不言而喻,无论何时您使用另一个对象,您都对该对象的接口具有实现依赖性。因此,所有其他代码的使用都带有这种依赖性。问题是你的接口是否耦合?
标签: javascript design-patterns observer-pattern observers