標籤:跳過 返回 變化 type 訂閱模式 add bsp 策略模式 虛擬
2016.08.30
《JavaScript 設計模式與開發實踐》 曾探 人民郵電出版社 2016年5月第1版
p13
找到變化的部分並封裝之,以使得容易替換;而剩下的就是不變的部分。
P49
函數柯裡化(currying)的作用是多次收集參數,然後作為數組傳給處理函數再一次執行。
其意義在於預先處理——將預先處理的流程放到一個函數裡會更為清晰可控。
P57
惰性載入函數
在函數內部重寫引用函數的外部變數的引用,從而在第一次”調用”此變數後,此變數就指向新的正確的函數。
p84
這裡的errorMsg不應該包含在strategies的項裡。這些項只要返回true/false就好,msg關他們什麼事啊!
也許是msg要根據strategie而變化吧。
P86
策略模式的目的是將演算法獨立出代碼的執行部分(content),使計算的代碼可複用、實現靈活。
關鍵點在於看出何為策略的最低依賴描述、考慮如何從content進入演算法中。
P89
關於代理模式,所謂代理是在訪問者-被訪問者這兩者間的代理者。被訪問者提供了一些介面供任意人訪問。這些介面在某些情況下不應被調用或應以另一個形式被調用,這時為了保證介面內的職責單一性,應該隔一層調用這個介面;這一層用於處理那些特定情況,決定如何調用介面,這層就是代理。
代理模式的目的是為了保持介面的職責單一性。
代理模式的主要特點是代理者與被代理者(即被訪問者)擁有職責一致的介面,一般介面名稱也一致。
由於需要處理的情形很多,代理的模式也各種各樣:
保護代理: 用於介面有不同存取權限的情況
虛擬代理: 目的是延遲對介面的訪問,到有必要時再訪問
緩衝代理: 儲存介面結果,參數一致時不調用介面直接返回已存結果。
等等。也不是說使用了虛擬代理就不能使用保護代理,以上列表只是說明這些情景下需要代理。
P93
單一職責原則指的是:就一個類(也包括對象與函數)而言,應該僅有一個引起它變化的原因。如果一個類承擔了多項職責,就意味著這個類將變得巨大,引起它變化的原因可能會有多個。
職責被定義為”引起變化的原因”。當一個類承擔多個職責時,由於其高耦合性,一個職責的處理可能會影響到另一個職責的處理。
代理層能承擔部分職責,使介面職責單一。
p124
發布-訂閱模式的推模型的問題在於:訂閱者收到的資料是由發行者決定的。
而拉模型的問題在於發行者可能會變成一個公開的對象。
首先,選用拉模型。
1. 發布時傳一個對象,此對象上擁有一系列介面供訂閱者擷取資料。可能需要用代理模式。
2. 在訂閱時傳入一些函數,這些函數將在結果上調用並返回調用結果(太複雜)。
發布-訂閱模式裡也用到了策略模式。
P131
命令模式的意義在於將職責分到三部分中:命令者-命令-執行者。另一個更重要的意義是命令將變成可儲存、調用的資料。
p132
疑點在於這裡的示範,命令moveCommand與執行者animate是綁定的,也就是說moveCommand不能用於命令animate2。如果有多個執行者,必鬚生成多個對應的命令。
其實可以實現成moveCommand.execute(animate2)。
不過仔細想想假如執行者不止animate2還有xxx2的話?多執行者這種情況可以有的吧?
這樣子可能就是實現成:
var MoveCommand = function(receiver, pos){
......
this.receivers = [receiver]
}
MoveCommand.prototype.execute = function(){
if(!arguments.length){
this._execute.apply(this, this.receivers)
} else {
this._execute.apply(this, arguments)
}
}
MoveCommand.prototype._execute = function(receiver){
receiver.start......
}
命令裡包含執行者也有好處,儲存命令就能儲存執行者了。
p132
組合模式的關鍵是介面一致。
p151
模板方法模式由兩部分結構組成,第一部分是抽象父類,第二部分是具體的實現子類。通常在抽象父類中封裝了子類的演算法架構。,包括實現一些公用方法以及封裝子類中所有方法的執行順序。子類通過這個抽象類別,也繼承了整個演算法結構,並且可以選擇重寫父類的方法。
p160
鉤子方法為什麼一定要是方法呢?
因為可以動態得到結果,只是要怎樣讓外部能改變到鉤子是個問題。
- 通過this訪問。也就是這裡的實現。
2. 作為閉包暴露鉤子。
p173
假如有2000個檔案,uploadDatabase雷根本就會存2000個對象。只不過不是2000個upload對象。
所以我認為享元模式的意義在於將資料與模板分離,從而共用了一個結構、一些方法。
需要使用一個對象時,拿資料填模板,就得到指定結構的對象與可用的方法。
而這裡所謂的”有多少個內部狀態,就有多少個共用對象”,內部狀態指的是模板,共用對象指的是模板的相應資料化結果(具體到這裡的代碼就是flyWeightObj,而不是Upload構造器。Upload構造器說到底跟享元模式無關,只是”元”的抽象類別,是內部狀態的抽象。12.6.1有例證)。
由於將資料分離,所以在本書的實現中,資料需要一個UploadManager來管理,並為模板裡的方法提供一個UploadManager.setExternalState以讀取資料填充到指定對象(就是模板本身)。
關於這個實現,我覺得這裡flyWeightObj.delFile(id)和uploadManager.setExternalState(id, this)同時出現就不好,相互調用感覺就不好。(應該沒有相互引用)
是不是改成flyWeightObj.delFile(id); this.setExternalState(id, flyWeightObj); 會比較好?
貌似他這麼做也無不妥,在flyWeightObj.delFile裡先將資料填充到本身上再刪除資料,是一種合理的做法。畢竟delFile可能會被多次調用,總不能每次調用前都寫一句setExternalState吧?
如果不需要存在”元”,那就沒有必要使用享元模式。
p185
職責鏈模式的最大優點就是解耦了請求寄件者和N個接受者之間的複雜關係,由於不知道鏈中的哪個節點可以處理你發出的請求,所以你只需把請求傳遞給第一個節點即可。
本書實現的職責鏈的問題在於程式員不知道鏈的具體模樣。所以A-B-D,我要往B後面插個C就必須知道B與D的引用,並修改它們的nextNode屬性。
可以var chainCtrl = new ChainCtrl(); var A = chainCtrl.cteateAndAppendNode(fnA), B = ......; var C = chainCtrl.createNode(fnC); chainCtrl.insertAfter(C, B);
以上的具體實現裡,不僅要維護一個存在ChainCtrl內部的數組,仍需要實現node.nextNode屬性,這樣才能夠調用B.next()以跳過A從B開始執行職責鏈。
p187
這裡提到AOP(面向切面編程)。
AOP解決的問題是希望在函數執行前或執行後插入代碼又不希望改動原函數。(如果希望在函數執行中某個位置插入代碼,恐怕只能使用鉤子)
AOP的關鍵是用一個函數調用原函數並代替原函數被使用。
順便一說,LISP裡天然實現AOP。
p189
中介者模式解決代碼塊間聯絡太多,耦合強的問題。(就像在心臟旁邊拆掉一根毛細血管一般,即使一點很小的修改也必須小心翼翼)
p195
這裡在構造player時傳入teamColor我認為是不對的。”team”是一個外部狀態而不是player所固有的東西,應該隔一層在playerFactory這個函數裡調用setTeam(player)為player設定屬性。要不然以後需求改成”當一個隊伍滿2人時,新的玩家將進入不滿員隊伍或建立隊伍”,這時候勢必要:
- 在Player構造器加判斷邏輯。這當然不行。
- Player構造器不直接加判斷邏輯而是使用鉤子。我認為這也半斤八兩,畢竟要調用與team相關的外部介面。
- 使用AOP,替換掉Player。可替換掉構造器貌似不妥。
還是移除在Player裡對teamColor屬性的設定比較好。這時可以:
1.在playerFactory使用鉤子。
2.在playerFactory這個函數裡調用setTeam(player)
p195
此處Player.prototype.die 等方法裡調用了playerDirector.ReceiveMessage,這樣就依賴了中介者模式的playerDirector,日後如果想換個模式勢必要改動這裡的代碼。建議隔一層。
要這麼說,所有調用外部介面的都應隔一層了。
p198
這裡關於掉線的處理有問題,假如先die再remove,因為remove裡沒有檢查,另一方就不能獲得勝利了。
可以考慮使用AOP
p199
說到購買商品的例子,能不能創造一種”流程模式”?將流程放到數組裡,調用next()則進入下一個流程,調用jump(fn)則跳到fn對應的流程。
還有就是想說,這裡可以使用mvc的理念。在頁面上操作,然後進入中介者,然後中介者處理一個資料結構,然後中介者調用一個渲染函數根據這個資料結構對頁面進行渲染。
問題在於如何只渲染需要渲染的內容。
p206
這裡的changed太過臃腫,耦合度也高,應該把if-else分割成函數放到一個obj裡,然後在changed裡遍曆obj的成員並用職責鏈模式將它們串連起來。
而剩餘部分,或許也可以分割成函數並使用AOP串連。
p208
過度抽象會使得無形狀,不直觀。
p211
裝飾者模式的意圖是減少子類與執行個體的數量。
同名調用同名聽起來有點像代理模式。
就邏輯業務上兩者的區別,代理者的業務與被代理者的業務是相關的,裝飾者與被裝飾者的業務可以是毫無關聯的。
就實現上而言,代理者是擁有被代理者同名方法的一個對象(或函數);裝飾者是在類A的原型方法中調用另一個類B建立的執行個體的方法,最後調用new A()得到一個執行個體,或者使用AOP替換原方法。
如果被裝飾者是單例,裝飾者模式是會改變被裝飾者的,代理模式則可以不改變它。
【讀書】JavaScript 設計模式與開發實踐