1.弱類型語言- 在JavaScript中,定義變量時不必聲明其類型。但這并不意味著變量沒有類型。一個變量可以屬于幾種類型之一,這取決于其包含的數(shù)據(jù)。JavaScript中有三種原始類型:布爾型、數(shù)值型和字符串類型(不區(qū)分整數(shù)和浮點數(shù)是JavaScript與大多數(shù)其他主流語言的一個不同之處)。此外,還有對象類型和包含可執(zhí)行代碼的函數(shù)類型,前者是一種復合數(shù)據(jù)類型(數(shù)組是一種特殊的對象,它包含著一批值的有序集合)。最后,還有空類型(null)和未定義類型(undefined)這兩種數(shù)據(jù)類型。原始數(shù)據(jù)類型按值傳送,而其他數(shù)據(jù)類型則按引用傳送。
- 與其他弱類型語言一樣,JavaScript中的變量可以根據(jù)所賦的值改變類型。原始類型之間也可以進行類型轉換。toString可以把數(shù)值或布爾值轉為字符串。parseFloat和parseInt函數(shù)可以把字符串轉變?yōu)閿?shù)值。雙重“非”可以把字符串或數(shù)值轉變?yōu)椴紶栔担?code>var bool = !!num;
2.初談閉包匿名函數(shù)最有趣的用途是用來創(chuàng)建閉包。閉包是一個受到保護的變量空間,由內嵌函數(shù)生成。JavaScript具有函數(shù)級的作用域。這意味著定義在函數(shù)內部的變量在函數(shù)外部不能被訪問。JavaScript的作用域又是詞法性質的。這意味著函數(shù)運行在定義它的作用域中,而不是在調用它的作用域中。把這兩個因素結合起來,就能通過把變量包裹在匿名函數(shù)中而對其加以保護。
3.依賴于接口的設計模式下面列出的設計模式,尤其依賴接口: - 工廠模式。對象工廠所創(chuàng)建的具體對象會因具體情況而異。使用接口可以確保所創(chuàng)建出來的這些對象可以互換使用。也就是說,對象工廠可以保證其生產(chǎn)出來的對象都實現(xiàn)了必需的方法。
- 組合模式。如果不用接口你就不可能用這個模式。組合模式的中心思想在于可以將對象群體與其組成對象同等對待。這是通過讓它們實現(xiàn)同樣的接口來做到的。如果不進行某種形式的鴨式辨型或類型檢查,組合模式就會失去大部分作用。
- 裝飾者模式。裝飾者通過透明地為另一對象提供包裝而發(fā)揮作用。這是通過實現(xiàn)與另外那個對象完全相同的接口而做到的。對于外界而言,一個裝飾者和它所包裝的對象看不出有什么區(qū)別。
- 命令模式。代碼中所有的命令對象都要實現(xiàn)同一批方法。通過使用接口,你為執(zhí)行這些命令對象而創(chuàng)建的類可以不必知道這些對象具體是什么,只要知道它們都實現(xiàn)了正確的接口即可。
4.用命名規(guī)范區(qū)別私用成員在一些方法和屬性的名稱前面加下劃線以示其私用性。下劃線的這種用法是一個眾所周知的命名規(guī)范,它表明一個屬性(或方法)僅供對象內部使用,直接訪問它或設置它可能會導致意想不到的后果。這有助于防止程序員對它的無意使用,卻不能防止對它的有意使用。后一個目標的實現(xiàn)需要有真正私用性的方法。
5.作用域下面這個示例說明了JavaScript中作用域的特點: function foo() { var a = 10; function bar() { a *= 2; } bar(); return a;}
在這個示例中,a定義在函數(shù)foo中,但函數(shù)bar可以訪問它,因為bar也定義在foo中。bar在執(zhí)行過程中將a設置為a乘以2。當bar在foo中被調用時它能夠訪問a,這可以理解。但是如果bar是在foo外部被調用呢? function foo() { var a = 10; function bar() { a *= 2; return a; } return bar;}var baz = foo();console.log(baz());//20console.log(baz());//40console.log(baz());//80var blat = foo();console.log(blat());//20
在上述代碼中,所返回的對bar函數(shù)的引用被賦給變量baz。這個函數(shù)現(xiàn)在是在foo外部被調用,但它依然能夠訪問a。這是因為JavaScript的作用域是詞法性的。函數(shù)是運行在定義它們的作用域中(本例中是foo內部的作用域),而不是運行在調用它們的作用域中。只要bar被定義在foo中,它就能訪問在foo中定義的所有變量,即使foo的執(zhí)行已經(jīng)結束。 這就是閉包的一個例子。在foo返回后,它的作用域被保存下來,但只有它返回的那個函數(shù)能夠訪問這個作用域。在前面的示例中,baz和blat各有這個作用域及a的一個副本,而且只有它們自己能對其進行修改。返回一個內嵌函數(shù)是創(chuàng)建閉包最常用的手段。 6.用閉包實現(xiàn)私用成員的弊端在門戶打開型對象創(chuàng)建模式中,所有方法都創(chuàng)建在原型對象中,因此不管派生多少對象實例,這些方法在內存中只存在一份。而包含特權方法、私用成員的創(chuàng)建模式中,每生成一個新的對象示例都將為每一個私用方法和特權方法生成一個新的副本。這會比其他做法耗費更多內存,所以只宜用在需要真正的私用成員的場合。這種對象創(chuàng)建模式也不利于派生子類,因為所派生出的子類不能訪問超類的任何私用屬性或方法。相比之下,在大多數(shù)語言中,子類都能訪問超類的所有私有屬性和方法。故在JavaScript中用閉包實現(xiàn)私用成員導致的派生問題稱為“繼承破壞封裝”。
7.靜態(tài)方法和屬性前面所講的作用域和閉包的概念可用于創(chuàng)建靜態(tài)成員,包括公用和私用的。大多數(shù)方法和屬性所關聯(lián)的是類的實例,而靜態(tài)成員所關聯(lián)的則是類本身。換句話說,靜態(tài)成員是在累的層次上操作,而不是在實例的層次上操作。每個靜態(tài)成員都只有一份。稍后將會看到,靜態(tài)成員是直接通過類對象訪問的。 下面是添加了靜態(tài)屬性和方法的Book類:
var Book = (function () { //私有靜態(tài)變量 var numOfBooks = 0; //私有靜態(tài)方法 function checkIsbn(isbn) { } //返回一個構造器 return function (newIsbn, newTitle, newAuthor) { //私有屬性 var isbn, title, author; //特權方法 this.getIsbn = function () { return isbn; }; this.setIsbn = function (newIsbn) { if (!checkIsbn(newIsbn)) { throw new Error('Book: Invalid ISBN.'); } isbn = newIsbn; }; this.getTitle = function () { return title; }; this.setTitle = function (newTitle) { title = newTitle || 'No title specified'; }; this.getAuthor = function () { return author; }; this.setAuthor = function (newAuthor) { author = newAuthor || 'No author specified'; }; //Constructed code. numOfBooks++; if (numOfBooks > 50) { throw new Error('.'); } this.setIsbn(newIsbn); this.setTitle(newTitle); this.setAuthor(newAuthor); }})();//公共靜態(tài)方法Book.convertToTitleCase = function (inputString) {};//公共非特權方法Book.prototype = { display: function () { }};
這里的私用成員和特權成員仍然被聲明在構造器中(分別使用var和this關鍵字)。但哪個構造器卻從原來的普通函數(shù)變成了一個內嵌函數(shù),并且被作為包含它的函數(shù)的返回值賦給變量Book。這就創(chuàng)建了一個閉包,你可以把靜態(tài)的私用成員聲明在里面。位于外層函數(shù)聲明之后的一對空括號很重要,其作用是一段代碼載入就立即執(zhí)行這個函數(shù)(而不是在調用Book構造函數(shù)時)。這個函數(shù)的返回值是另一個函數(shù),它被賦給Book變量,Book因此成了一個構造函數(shù)。在實例化Book時,所調用的是這個內層函數(shù)。外層那個函數(shù)只是用于創(chuàng)建一個可以用來存放靜態(tài)私用成員的閉包。 - 在本例中,checkIsbn被設計為靜態(tài)方法 ,原因是為Book的每個實例都生成這個方法的一個新副本毫無道理。此外還有一個靜態(tài)屬性numOfBooks,其作用在于跟蹤Book構造器的總調用次數(shù)。本例利用這個屬性將Book實例的個數(shù)限制為不超過50個。
- 這些私用的靜態(tài)成員可以從構造器內部訪問,這意味著所有私用函數(shù)和特權函數(shù)都能訪問它們。與其他方法相比,它們有一個明顯的優(yōu)點,那就是內存中只會存放一份。因為其中那些靜態(tài)方法被聲明在構造器之外,所以它們不是特權方法,不能訪問任何定義在構造器中的私用屬性。定義在構造器中的私用方法能夠調用那些私用靜態(tài)方法,反之則不然。要判斷一個私用方法是否應該被設計為靜態(tài)方法,一條經(jīng)驗法則是看它是否需要訪問任何實例數(shù)據(jù)。如果它不需要,那么將其設計為靜態(tài)方法會更有效率(從內存占用的意義上來講),因為它只會被創(chuàng)建一份。
- 創(chuàng)建公用的靜態(tài)成員則容易得多,只需直接將其作為構造函數(shù)這個對象的屬性創(chuàng)建即可,前述代碼中的方法converToTitleCase就是一例。這實際上相當于把構造器作為命名空間來使用。
- 所有公用靜態(tài)方法如果作為獨立的函數(shù)來聲明其實也同樣簡單,但最好還是像這樣把相關行為集中在一起。這些方法用于與類這個整體相關的任務,而不是與類的任一特定實例相關的任務。它們并不直接依賴于對象實例中包含的任何數(shù)據(jù)。
8.私用變量模仿常量通過創(chuàng)建只有取值器而沒有賦值器的私用變量可以模仿常量。
var Class = (function () { var UPPER_BOUND = 100; //構造器 var ctor = function (constructorArgument) { }; //靜態(tài)特權方法 ctor.getUPPER_BOUND = function () { return UPPER_BOUND; }; return ctor;})();
9.封裝之弊- 私用方法很難進行單元測試。因為它們及其內部變量都是私用的,所以在對象外部無法訪問到它們。這個問題沒有什么很好的應對之策。你要么通過使用公用方法來提供訪問途徑(這樣一來就葬送了使用私有方法所帶來的大多數(shù)好處),要么設法在對象內部定義并執(zhí)行所有單元測試。最好的解決辦法是只對公用方法進行單元測試。這應該能覆蓋到所有私用方法,盡管對它們的測試只是間接的。這種問題不是JavaScript所獨有的,只對公用方法進行單元測試是一種廣為接收的處理方式。
- 使用封裝意味著不得不與復雜的作用域鏈打交道。
- 封裝可能會損害類的靈活性,致使其無法被用于某些你未曾想到過的目的。
10.單體模式單體模式是JavaScript中最基本但又最有用的模式之一,它可能比其他任何模式都更常用。這種模式提供了一種將代碼組織為一個邏輯單元的手段,這個邏輯單元中的代碼可以通過單一的變量進行訪問。通過確保單體對象只存在一份實例,你就可以確信自己的所有代碼使用的都是同樣的全局資源。 單體類在JavaScript中有許多用處。它們可以用來劃分命名空間,以減少網(wǎng)頁中全局變量的數(shù)目。更重要的是,借助于單體模式,你可以把代碼組織得更為一致,從而使其更容易閱讀和維護。
11.單體的基本結構var Singleton = { attribute1: true, attribute2: 10, method1: function () { }, method2: function (args) { }};
- 這個單體對象可以被修改。你可以為其添加新成員,這一點與別的對象字面量沒有什么不同。你也可以用delete運算符刪除其現(xiàn)有成員。這實際上違背了面向對象設計的一條原則:類可以被擴展,但不應該被修改。
- 按傳統(tǒng)的定義,單體是一個只能被實例化一次并且可以通過一個眾所周知的訪問點訪問的類。要是嚴格按照這個定義來說,前面的例子所示的并不是一個單體,因為它不是一個可實例化的類。我們打算把單體模式定義的更廣義一些:單體是一個用來劃分命名空間并將一批相關方法和屬性組織在一起的對象,如果可以被實例化,那么它只能被實例化一次。
12.劃分命名空間為了避免無意中改寫變量,最好的解決辦法之一是用單體對象將代碼組織在命名空間之中。下面是前面的例子用單體模式改良后的結果: var MyNamespace = { findProduct: function (id) { }};
現(xiàn)在findProduct函數(shù)是MyNamespace中的一個方法,它不會被全局命名空間中聲明的任何新變量改寫。要注意,該方法仍然可以從各個地方訪問。不同之處在于現(xiàn)在其調用方式不是findProduct(id),而是MyNamespace.findProduct(id)。還有一個好處就是,這可以讓其他程序員大體知道這個方法的聲明地點及其作用。用命名空間把類似的方法組織到一起,也有助于增強代碼的文檔性。 13.模塊模式有一種單體模式被稱為模塊模式,因為它可以把一批相關方法和屬性組織為模塊并起到劃分命名空間的作用。例如:
MyNamespace.Singleton = (function () { //私有成員 var privateAttribute1 = false; var privateAttribute2 = [1, 2, 3]; function privateMethod1() { } function privateMethod2() { } return { //public members publicAttribute1: true, publicAttribute2: 10, publicMethod1: function () { }, publicMethod2: function (args) { } }})();
14.簡單工廠模式最好用一個例子來說明簡單工廠模式的概念。假設你想開幾個自行車商店,每個店都有幾種型號的自行車出售。這可以用一個類來表示:
/*BicycleShop class.*/var BicycleShop = function () {};BicycleShop.prototype = { sellBicycle: function (model) { var bicycle; switch (model) { case 'The Speedster': bicycle = new SpeedSter(); break; case 'The Lowrider': bicycle = new Lowrider(); break; case 'The Comfort Cruiser': default: bicycle = new ComfortCruiser(); } Interface.ensureImplements(bicycle, Bicycle); bicycle.assemble(); bicycle.wash(); return bicycle; }};
sellBicycle方法根據(jù)所要求的自行車型號用switch語句創(chuàng)建一個自行車的實例。各種型號的自行車實例可以互換使用,因為它們都實現(xiàn)了Bicycle接口:
/* The Bicycle interface. */var Bicycle = new Interface('Bicycle', ['assemble', 'wash', 'ride', 'repair']);/* Speedster class. */var Speedster = function () {};Speedster.prototype = { assemble: function () { }, wash: function () { }, ride: function () { }, repair: function () { }};
要出售某種型號的自行車,只要調用sellBicycle方法即可:
var californiaCruisers = new BicycleShop();var yourNewBike = californiaCruisers.sellBicycle('The Speedster');
在情況發(fā)生變化之前,這倒也挺管用。但要是你想在供貨目錄中加入一款新車型又會怎么樣呢?你得為此修改BicycleShop的代碼,哪怕這個類的實際功能實際上并沒有發(fā)生改變——依舊是創(chuàng)建一個自行車的新實例,組裝它,清洗它,然后把它交給顧客。更好的解決辦法是把sellBicycle方法中“創(chuàng)建新實例”這部分工作轉交給一個簡單工廠對象:
/* BicycleFactory namespace. */var BicycleFactory = { createBicycle:function(model){ var bicycle; switch (model) { case 'The Speedster': bicycle = new SpeedSter(); break; case 'The Lowrider': bicycle = new Lowrider(); break; case 'The Comfort Cruiser': default: bicycle = new ComfortCruiser(); } Interface.ensureImplements(bicycle, Bicycle); return bicycle; }};
BicycleFactory是一個單體,用來把createBicycle方法封裝在一個命名空間中。這個方法返回一個實現(xiàn)了Bicycle接口的對象,然后你可以照常對其進行組裝和清洗: /* BicycleShop class, improved. */var BicycleShop = function () {};BicycleShop.prototype = { sellBicycle: function (model) { var bicycle = BicycleFactory.createBicycle(model); bicycle.assemble(); bicycle.wash(); return bicycle; }};
這個BicycleFactory對象可以供各種類用來創(chuàng)建新的自行車實例。有關可供車型的所有信息集中在一個地方管理 ,所以添加更多車型很容易: /* BicycleFactory namespace,with more models. */var BicycleFactory = { createBicycle: function (model) { var bicycle; switch (model) { case 'The Speedster': bicycle = new SpeedSter(); break; case 'The Lowrider': bicycle = new Lowrider(); break; case 'The Flatlander': bicycle = new Flatlander(); break; case 'The Comfort Cruiser': default: bicycle = new ComfortCruiser(); } Interface.ensureImplements(bicycle, Bicycle); return bicycle; }};
15.工廠模式真正的工廠模式與簡單工廠模式的區(qū)別在于,它不是另外使用一個類或對象來創(chuàng)建自行車,而是使用一個子類。按照正式定義,工廠是一個將其成員對象的實例化推遲到子類中進行的類。
16.工廠模式的適用場合- 動態(tài)實現(xiàn):如果需要創(chuàng)建一些用不同方式實現(xiàn)同一接口的對象,那么可以使用一個工廠方法或簡單工廠對象來簡化選擇實現(xiàn)的過程。
- 節(jié)省設置開銷:如果對象需要進行復雜并且彼此相關的設置,那么使用工廠模式可以減少每種對象所需的代碼量。如果這種設置只需要為特定類型的所有實例執(zhí)行一次即可,這種作用尤為突出。把這種設置代碼放到類的構造函數(shù)中并不是一種高效的做法,這是因為即便設置工作已經(jīng)完成,每次創(chuàng)建新實例的時候這些代碼還是會執(zhí)行,而且這樣做會把設置代碼分散到不同的類中。工廠方法非常適合于這種場合。它可以在實例化所有需要的對象之前先一次性地進行設置。無論有多少類會被實例化,這種辦法都可以讓設置代碼集中在一個地方。
- 用許多小型對象組成一個大對象
17.工廠模式之利- 工廠模式的主要好處在于消除對象間的耦合。通過使用工廠方法而不是new關鍵字及具體類,你可以把所有實例化的代碼集中在一個位置。這可以大大簡化更換所用的類或在運行期間動態(tài)選擇所用的類的工作。在派生子類時它也提供了更強大的靈活性。
- 所有這些好處都與面向對象設計的這兩條原則有關:弱化對象間的耦合;防止代碼的重復。在一個方法中進行類的實例化,可以消除重復性的代碼。這是在用一個對接口的調用取代一個具體的實現(xiàn)。這些都有助于創(chuàng)建模塊化的代碼。
18.橋接模式橋接模式最常見和實際的應用場合之一就是事件監(jiān)聽器回調函數(shù)。假設有一個名為getBeerById的API函數(shù),它根據(jù)一個標識符返回有關某種啤酒的信息。你希望用戶在點擊的時候獲取這種信息。那個被點擊的元素很可能有啤酒的標識符信息,它可能是作為元素自身的ID保存,也可能是作為別的自定義屬性保存。下面是一種做法: addEvent(element, 'click', getBeerById);function getBeerById(e) { var id = this.id; asyncRequest('GET', 'beer.uri?id=' + id, function (resp) { console.log(resp.responseText); });}
這個API只能工作在瀏覽器中,如果要對這個API函數(shù)做單元測試,或者在命令行中執(zhí)行,可能會報錯。一個優(yōu)良的API設計,不應該把它與任何特定的實現(xiàn)攪在一起。 function getBeerById(id, callback) { asyncRequest('GET', 'beer.uri?id=' + id, function (resp) { callback(resp.responseText); })}
現(xiàn)在我們將針對接口而不是實現(xiàn)進行編程,用橋接模式把抽象隔離開來: addEvent(element, 'click', getBeerByIdBridge);function getBeerBIdBridge(e) { getBeerById(this.id, function (beer) { console.log(beer); });}
這下getBeerById并沒有和事件對象捆綁在一起了。 19.用橋接模式聯(lián)結多個類var Class1 = function (a, b, c) { this.a = a; this.b = b; this.c = c;};var Class2 = function (d) { this.d = d;};var BridgeClass = function (a, b, c, d) { this.one = new Class1(a, b, c); this.two = new Class2(d);};
20.適配器模式適配器模式可以用來在現(xiàn)有接口和不兼容的類之間進行適配。使用這種模式的對象又叫包裝器,因為它們是在用一個新的接口包裝另一個對象。 21.適配器的特點- 適配器可以被添加到現(xiàn)有代碼中以協(xié)調兩個不同的接口。如果現(xiàn)有代碼的接口能很好地滿足需要,那就可能沒有必要使用適配器。
- 從表面上看,適配器模式很像門面模式。它們都要對別的對象進行包裝并改變其呈現(xiàn)的接口。二者的差別在于它們如何改變接口。門面元素展現(xiàn)的是一個簡化的接口,它并不提供額外的選擇,而且有時為了方便完成某些常見任務它還會做出一些假定。而適配器則要把一個接口轉換為另一個接口,它并不會濾除某些能力,也不會簡化接口。如果客戶系統(tǒng)期待的API不可用,那就需要用到適配器。
- 適配器可被實現(xiàn)為不兼容的方法調用之間的一個代碼薄層。
- 示例:
假如你有一個對象還有一個以三個字符串為參數(shù)的函數(shù): var clientObject = { string1: 'foo', string2: 'bar', string3: 'baz' }; function interfaceMethod(str1, str2, str3) { }
為了把clientObject作為參數(shù)傳遞給interfaceMethod,需要用到適配器。我們可以這樣創(chuàng)建一個: function clientToInterfaceAdapter(o) { interfaceMethod(o.string1, o.string2, o.string3);
} //現(xiàn)在就可以把整個對象傳給這個函數(shù) clientToInterfaceAdapter(clientObject);
clientToInterfaceAdapter函數(shù)的作用就在于對interfaceMethod函數(shù)進行包裝,并把傳遞給它的參數(shù)轉換給后者需要的形式。 22.裝飾者模式裝飾者模式可用來透明地把對象包裝在具有同樣接口的另一對象中。這樣一來,你可以給一個方法添加一些行為,然后將方法調用傳遞給原始對象。相對于創(chuàng)建子類來說,使用裝飾者對象是一種更靈活的選擇。
23.享元模式享元模式最適合于解決因創(chuàng)建大量類似對象而累及的性能問題。這種模式在JavaScript中尤其有用,因為復雜的JavaScript代碼可能很快就會用光瀏覽器的所有可用內存。通過把大量獨立對象轉化為少量共享對象,可以降低運行Web應用程序所需的資源數(shù)量。
享元模式用于減少應用程序所需對象的數(shù)量。這是通過將對象的內部狀態(tài)劃分為內在數(shù)據(jù)和外在數(shù)據(jù)兩類而實現(xiàn)的。內在數(shù)據(jù)是指類的內部方法所需的信息,沒有這種數(shù)據(jù)的話類不能正常運轉。外在數(shù)據(jù)則是可以從類身上剝離并存儲在其外部的信息。我們可以將內在狀態(tài)相同的所有對象替換為同一個共享對象,這種方法可以把對象數(shù)量減少到不同內在狀態(tài)的數(shù)量。 24.實現(xiàn)享元模式的一般步驟- 將所有外在數(shù)據(jù)從目標剝離。具體做法是盡可能多地刪除該類的屬性,所刪除的應該是那種因實例而異的屬性。構造函數(shù)的參數(shù)也要這樣處理。這些參數(shù)應該被添加到該類的各個方法。這些外在數(shù)據(jù)現(xiàn)在不再保存在類的內部,而是由管理器提供給類的方法。經(jīng)過這樣的處理后,目標類應該依然具有與之前一樣的功能。唯一的區(qū)別在于數(shù)據(jù)的來源發(fā)生了變化。
- 創(chuàng)建一個用來控制該類的實例化的工廠。這個工廠應該掌握該類所有已創(chuàng)建出來的獨一無二的實例。其具體做法之一是用一個對象字面量來保存每一個這類對象的引用,并以用來生成這些對象的參數(shù)的唯一性組合作為它們的索引。這樣一來,每次要求工廠提供一個對象時,它會先檢查那個對象字面量,看看以前是否請求過這個對象。如果是,那么只要返回那個現(xiàn)有對象的引用就行。否則它會創(chuàng)建一個新對象并將其引用保存在那個對象字面量中,然后返回這個對象。另一種做法稱為對象池,這種技術用數(shù)組來保存所創(chuàng)建的對象的引用。它適合于注重可用對象的數(shù)量而不是那些單獨配置的實例的場合。這種技術可用來將所實例化的對象的數(shù)目維持在最低值。工廠會處理根據(jù)內在數(shù)據(jù)創(chuàng)建對象的所有事宜。
- 創(chuàng)建一個用來保存外在數(shù)據(jù)的管理器。該管理器對象負責控制處理外在數(shù)據(jù)的種種事宜。在實施優(yōu)化之前,要是需要一個目標類的實例,你會把所有數(shù)據(jù)傳給構造函數(shù)以創(chuàng)建其新實例。而現(xiàn)在要是需要一個實例,你會調用管理器的某個方法,把所有數(shù)據(jù)都提供給它。這個方法會分辨內在數(shù)據(jù)和外在數(shù)據(jù)。它把內在數(shù)據(jù)提供給工廠對象以創(chuàng)建一個對象(或者,如果已經(jīng)存在這樣一個對象的話,則重用該對象)。外在數(shù)據(jù)則被保存在管理器內的一個數(shù)據(jù)結構中。管理器隨后會根據(jù)需要將這些數(shù)據(jù)提供給共享對象的方法,其效果就如同該類有許多實例一樣。
25.觀察者模式- 在事件驅動的環(huán)境中,比如瀏覽器這種持續(xù)尋求用戶關注的環(huán)境中,觀察者模式(又名發(fā)布者-訂閱者模式)是一種管理人與其任務之間的關系(確切的說,是對象及其行為和狀態(tài)之間的關系)的得力工具。
- 觀察者模式中存在兩個角色:觀察者和被觀察者。
|