本文的幾個第一:
第一次嘗試翻譯,原文來自codeproject(http://www.codeproject.com/jscript/Observer_Pattern_JS.asp)
第一次使用Windows Live Writer,beta 版.
第一次在自己的blog中寫上:Flyingchen-蘇州.Net俱樂部。
文章導言:
任何使用過javascript的人,不論你使用多久,都應該熟悉如何通過javascript建立自訂對象。如果你不瞭解OOP在JavaScript中的運用,那麼你也可以看個大概。本文就是用JavaScript來實現觀察者模式。
對JavaScript的大概介紹:
JavaScript是一種基於原型的教本語言(最初叫做LiveScript),有著類似C的文法。JavaScript最初開發人員是美國網景公司,用於他的Navigator瀏覽器。和C一樣,這種語言沒有自己的輸入或輸出的構建方法。正如C依賴於標準輸入/輸出函數庫一樣,JavaScript引擎依賴於主機環境,它是一種嵌入式語言。其他語言,有的用程序呼叫,有的用子程式,有的是函數。JavaScript協調其他語言的實現,統一實現為:自訂函數。JavaScript一個重大的運用,就是和Web的文檔結構模型(DOM)結合,而不僅僅是HTML去完成功能。JScript,和網景的JavaScript類似的,用於微軟IE的教本語言。
用JavaScript建立一個使用者自訂對象
建立一個JavaScript對象需要兩個步驟:
第一步:你需要建立一個自訂函數,它的名字也就是這個新的對象的名字。這樣一個函數類似於一個構造方法。
第二步:你需要建立一個執行個體。和OO語言一樣,用new操作符,氣候緊跟這個對象的名字和必要的參數(如果有的話)。下面這些代碼示範了如何定義一個Person函數,和建立一個Person對象執行個體。
function Person( name, surname )
{
this.name = name;
this.surname = surname;
}
var salvo = new Person( ‘Salvatore’, ‘Vetro’ );
this指標指向當前對象,你可以用它來增加或者修改這個對象的屬性。
如何在對象上添加一個方法:
In JavaScript, every object that can be created by calling a constructor function has an associated prototype property.
下面的文法用來為對象增加一個新的方法:
customeObject.prototype.newMethodName = function;
// Specific case
Person.prototype.Speak = function(){...}
如果你為一個對象的原型添加了一個新的方法,那麼這個對象的執行個體都將擁有這個新的方法。注意這個prototype本身就是一個對象,也可以通過它的文法給它的屬性和方法賦值:
function NewObject()
{
alert("I am a new object");
}
NewObject.prototype =
{
alert1:unction(str){alert(str),/new method
name:'flyingchen',
alert2:function(){alert('bye')}//new method
}
var newobject = new NewObject();
newobject.alert1(newobject.name);
newobject.alert2();
每次,當你的script試圖去讀或寫一個對象的屬性的時候,JavaScript根據下面這個明確的順序去搜尋匹配的屬性的名字。這個順序是:
- 如果當前對象這個屬性已經被賦值,那麼這個值就是你擷取的。
- 如果屬性沒有被賦值,那麼就要check這個屬性的原型中構造方法是如何賦值的。
- 繼續沿原型這個鏈向上,直到任一個匹配的屬性被發現(已經被賦值後的),或者當這個匹配過程一直到最定層的對象。因此,如果你改變構造的原型的屬性的值,並且沒有在執行個體中改寫這個屬性值,那麼JavaScript將把構造器當前的值返回。
觀察者模式的類圖:
觀察者模式定義了一個被觀察對象和多個觀察對象之間的一對多的依賴關係。因此,被觀察者狀態的改變,所有觀察者對象都將知道並自動更新自己的狀態。
參與者:
1.知道自己的觀察者
2.提供一個介面能夠添加和分離觀察者對象
1.定義一個介面,能夠被subject在狀態變化的時候調用
1.存放ConcreteObserver感興趣的狀態資訊÷
2.當狀態改變的時候,通知觀察者。
1.擁有一個ConcreteSubject的引用。
2.儲存一個狀態資訊,這個狀態資訊應該和subject中一致。
3.實現Observer的Update介面,以此用來保持與Subject中狀態資訊的協調。
協作關係:
1。當subject狀態改變的時候,ConcreteSubject發送訊息給觀察者們。
2。ConcreteObserver在接受到訊息後,可能要查詢subject當前的資訊。ConcreteObserver通過這些資訊來改變自己的狀態。
下一步做什麼呢?
現在你知道了什麼是觀察者模式,同時你也知道了如何用JavaScript來建立對象。正如你從類圖中看到的,你需要定義兩個方法(Attach 和 Detach )在你的被觀察者類(subject)中。為了這個目的,你需要一個結合來完成這兩個操作。現在是你動手,用JavaScript寫ArrayList的時候了:
Count---Add---GetAt---Clear---RemoveAt---Insert---IndexOf---LastIndexOf
function ArrayList()
{
this.aList = []; //initialize with an empty array
}
ArrayList.prototype.Count = function()
{
return this.aList.length;
}
ArrayList.prototype.Add = function( object )
{
//Object are placed at the end of the array
return this.aList.push( object );
}
ArrayList.prototype.GetAt = function( index ) //Index must be a number
{
if( index > -1 && index < this.aList.length )
return this.aList[index];
else
return undefined; //Out of bound array, return undefined
}
ArrayList.prototype.Clear = function()
{
this.aList = [];
}
ArrayList.prototype.RemoveAt = function ( index ) // index must be a number
{
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:
var head = this.aList.slice( 0, 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 = this.aList.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 )
{
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;
}
很好,現在你可以建立Observer對象和Subject對象了。
Observer Class :
function Observer()
{
this.Update = function()
{
return;
}
}
Subject Class:
下面,我們來實現Subject中的三個中的重要方法:
function Subject()
{
this.observer = 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
{
extension[property] = base[property];
}
catch( warning ){}
}
}
一個簡單的實現:
現在你需要實現一個用戶端,以便你能夠給subject添加觀察者。例如,你可以建立一個簡單的運用,定義一個checkbox作為subject,同時再定義一些checkboxes作為觀察者。當concrete subject狀態改變的時候,將把訊息發送給所有的觀察者。
************* 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 ****************/</CODE>
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);
//代碼下載
2006-8-16