學(xué)習(xí)基礎(chǔ):熟悉《設(shè)計(jì)模式》和《重構(gòu)》的概念,熟悉基本的Java語法和XML語法,熟悉Eclipse和JUnit的使用,有相對較好的英語基礎(chǔ)。
學(xué)習(xí)過程:
先按P40的學(xué)習(xí)順序讀完序號1~9。理解每個(gè)重構(gòu)模式的動(dòng)機(jī),嘗試讀懂示例中的代碼(實(shí)在不懂就放過,找機(jī)會(huì)上機(jī)也能明白)。
在時(shí)間允許的條件下,可以重讀和對書中代碼重構(gòu),從而更加充分地理解重構(gòu)與模式這兩個(gè)重要的概念和實(shí)踐方法。
學(xué)習(xí)目的:使自己編寫的代碼更容易被人讀懂。
學(xué)習(xí)感悟:
代碼的重構(gòu)應(yīng)該是一步步完成的,每次重構(gòu)的部分不要超過自己的理解能力的5%。雖然這樣操作略顯繁瑣,但是可以減輕頭腦重構(gòu)過程中的記憶強(qiáng)度,減少代碼出錯(cuò)的機(jī)會(huì)。
代碼的重構(gòu)一定要配合JUnit(TDD,測試驅(qū)動(dòng)開發(fā))完成,再加上Git(版本管理)和Eclipse(IDE的重構(gòu)工具)那就事半功倍了。
一、構(gòu)造函數(shù)
(一)、構(gòu)造函數(shù)的重構(gòu):
6.1. (P43)用Creation Method替換構(gòu)造函數(shù):用明確的創(chuàng)建方法代替類中的多個(gè)構(gòu)造函數(shù),避免創(chuàng)建新類時(shí)客戶無法正確理解和使用構(gòu)造函數(shù)的意圖。
優(yōu)點(diǎn)與缺點(diǎn):
+ 相比構(gòu)造函數(shù)能夠更好地表達(dá)所創(chuàng)建的實(shí)例的意圖;
+ 避免了構(gòu)造函數(shù)的局限性,例如:兩個(gè)構(gòu)造函數(shù)不能通過相同的參數(shù)和不同的返回類型進(jìn)行重載;
+ 更容易發(fā)現(xiàn)無用的創(chuàng)建代碼;
- 非標(biāo)準(zhǔn)化的創(chuàng)建方式,例如:不是統(tǒng)一用new的方式實(shí)例化。
11.1.(P275) 構(gòu)造函數(shù)鏈:把構(gòu)造函數(shù)鏈接起來,減少代碼的重復(fù)。特殊的構(gòu)造函數(shù)調(diào)用更通用的構(gòu)造函數(shù),直到全包含構(gòu)造函數(shù)(處理所有的構(gòu)造函數(shù)調(diào)用)。
當(dāng)構(gòu)造函數(shù)數(shù)量過多時(shí),可以使用Creation Method(6.1節(jié))重構(gòu)。
(二)、Factory的使用
6.3. (P60)用Factory封裝類:將同一個(gè)包里面、實(shí)現(xiàn)了相同接口的多個(gè)類的實(shí)例化交由工廠完成。
優(yōu)點(diǎn)與缺點(diǎn):
+ 通過Creation Method簡化不同種類實(shí)例的創(chuàng)建;
+ 通過隱藏類減少包結(jié)構(gòu)的“概念重量”;
+ 通過共享公共接口實(shí)踐“面向接口編程,而不要面向?qū)崿F(xiàn)編程”的理念;
- 增加新的種類的實(shí)例時(shí),必須修改或者增加新的Creation Method;
- 當(dāng)客戶只能獲得Factory的二進(jìn)制代碼而不是源代碼時(shí),對Factory的制定將受到限制。
6.4. (P67)用Factory Method引入多態(tài)創(chuàng)建:將同樣的超類,不同的創(chuàng)建過程統(tǒng)一交由工廠完成。
優(yōu)點(diǎn)與缺點(diǎn):
+ 減少因創(chuàng)建自定義對象而產(chǎn)生的重復(fù)代碼;
+ 有效地表達(dá)了對象創(chuàng)建發(fā)生的位置,以及如何重寫對象的創(chuàng)建;
+ 強(qiáng)制Factory Method使用的類必須實(shí)現(xiàn)統(tǒng)一的接口;
- 可能會(huì)向Factory Method的一些實(shí)現(xiàn)者傳遞不必要的參數(shù)。
算法重構(gòu)
Strategy
7.2.(P102) 用Strategy替換條件邏輯
優(yōu)點(diǎn)與缺點(diǎn):
+ 通過減少或者去除條件邏輯使算法變得清晰易懂;
+ 通過把算法的變體搬移到類層次中簡化了類;
+ 允許在運(yùn)行時(shí)用一種算法替換另一種算法;
- 增加設(shè)計(jì)的復(fù)雜度;
- 增加了算法獲取數(shù)據(jù)的復(fù)雜度。
Template Method
8.1. (P166)形成Template Method:通過將不同方法中相同的順序執(zhí)行步驟提取出來從而實(shí)現(xiàn)泛化。
優(yōu)點(diǎn)與缺點(diǎn):
+ 通過把不變行為搬移到超類,去除子類中的重復(fù)代碼;
+ 簡化并有效地表達(dá)了一個(gè)通用算法的步驟;
+ 允許子類很容易地定制一個(gè)算法;
- 當(dāng)為了生成算法導(dǎo)致子類必須實(shí)現(xiàn)很多方法的時(shí)候,就增加了設(shè)計(jì)的復(fù)雜度。
Composite
XMLBuilder的例子依據(jù)的有(7.5)TagNode,(6.5)TagBuilder,(10.1)TagNode,代碼中存在一些小問題,可以試著自己修改。
7.1. (P97)Composed Method(組合方法)模式:將代碼組合成名字簡單易懂的方法,再將這些方法組合成更大的方法,從而方便讀懂代碼。
優(yōu)點(diǎn)與缺點(diǎn):
+ 清晰地描述了一個(gè)方法所實(shí)現(xiàn)的功能以及如何實(shí)現(xiàn);
+ 把方法分解成命名良好的、處在細(xì)節(jié)的同一層面上的行為模塊,以此來簡化方法;
- 可能會(huì)產(chǎn)生過多的小方法;
- 可能會(huì)使調(diào)試變得困難,因?yàn)槌绦虻倪壿嫹稚⒃谠S多小方法中。
7.5. (P143)Composite模式:隱式地構(gòu)造出樹形結(jié)構(gòu)。
優(yōu)點(diǎn)與缺點(diǎn):
+ 封裝重復(fù)的指令,如格式化、添加或刪除結(jié)點(diǎn);
+ 提供了處理相似邏輯增長的一般性方法;
+ 簡化了客戶代碼的構(gòu)造職責(zé);
- 構(gòu)造的隱式的樹結(jié)構(gòu)越簡單,設(shè)計(jì)的時(shí)候就越復(fù)雜。
6.5. (P74)用Builder封裝Composite:因?yàn)闃?gòu)造Composite有許多重復(fù)工作,并且工作流程是復(fù)雜而且容易出錯(cuò)的,通過Builder來處理構(gòu)造細(xì)節(jié)來簡化構(gòu)造過程。
優(yōu)點(diǎn)與缺點(diǎn):
+ 簡化了構(gòu)造Composite的客戶代碼;
+ 減少了創(chuàng)建Composite的重復(fù)和易出錯(cuò)的問題;
+ 在客戶代碼和Composite之間實(shí)現(xiàn)了松耦合;
+ 允許對已經(jīng)封裝的Composite或復(fù)雜的對象創(chuàng)建不同的表示;
- 接口可能無法清楚地表達(dá)其本質(zhì)意圖。
10.1. Collecing Parameter(聚集參數(shù))模式:是把一個(gè)對象傳入到不同的方法中,從而在這些方法中收集信息。
經(jīng)常與Composed Method模式一起使用。
優(yōu)點(diǎn)與缺點(diǎn):
+ 可以把很大的方法轉(zhuǎn)換成更上的、更簡單的方法;
- 少量地影響結(jié)果代碼的運(yùn)行速度。
8.2.(P172) 提取Composite:將同一個(gè)超類下的多個(gè)子類實(shí)現(xiàn)的同樣的Composite提取到超類中。
優(yōu)點(diǎn)與缺點(diǎn):
+ 去除重復(fù)的類存儲邏輯和類處理邏輯;
+ 能夠有效地表達(dá)類處理邏輯的可繼承性。
8.3.(P180) 用Composite替換一個(gè)對象和多個(gè)對象的分別:用Composite實(shí)現(xiàn)了既可以處理一個(gè)對象,也可以處理多個(gè)對象的同樣的函數(shù)接口。
優(yōu)點(diǎn)與缺點(diǎn):
+ 去除了處理一個(gè)對象的方法和多個(gè)對象的方法中重復(fù)的代碼;
+ 提供了處理一個(gè)對象和多個(gè)對象的統(tǒng)一函數(shù)方法;
+ 提供了處理多個(gè)對象的更豐富的方法(例如:OR表達(dá)式);
- 可能會(huì)在Composite的構(gòu)造過程中要求類型安全的運(yùn)行時(shí)檢查。
Command
7.6.(P155)用Command替換條件調(diào)度程序:為動(dòng)作創(chuàng)建Command,將這些Command存儲在集合中,再通過集合取出對應(yīng)的Command執(zhí)行。
缺點(diǎn)與優(yōu)點(diǎn):
+ 提供了用統(tǒng)一方法執(zhí)行不同行為的簡單機(jī)制;
+ 允許在運(yùn)行時(shí)改變所處理的請求,以及如何處理請求;
+ 僅僅需要很少的代碼實(shí)現(xiàn);
- 會(huì)增加設(shè)計(jì)的復(fù)雜度,有時(shí)還不如條件調(diào)度程序簡單。
Adapter
8.6.(P208)提取Adapter:一個(gè)類適配了多個(gè)版本的組件、類庫、API或其他實(shí)體,可以通過提取Adapter來統(tǒng)一接口。
缺點(diǎn)與優(yōu)點(diǎn):
+ 隔離不同版本的組件、類庫或API之間的區(qū)別;
+ 使類只負(fù)責(zé)適配代碼的一個(gè)版本;
+ 避免了頻繁地修改代碼;
- 因?yàn)锳dapter帶來的限制,阻止了客戶調(diào)用Adapter沒有提供的功能。
8.5.(P99)通過Adapter統(tǒng)一接口:使用客戶代碼與多個(gè)類交互時(shí),遵循統(tǒng)一接口完成。
優(yōu)點(diǎn)與缺點(diǎn):
+ 客戶通過相同的接口與不同的類交互;
+ 客戶通過公共的接口與多個(gè)對象交互;
+ 客戶與不同的類交互方式相同;
- 當(dāng)類的接口可以改變的時(shí)候,增加了設(shè)計(jì)的復(fù)雜度。
Adapter模式與Facade模式的區(qū)別
Adapter用來適配對象;
Facade用來適配整個(gè)子系統(tǒng);通常用來與遺留系統(tǒng)進(jìn)行交互。
它們都可以使代碼易于使用,但是它們的應(yīng)用級別不同。
State
9.1.(P231)用類替換類型代碼:保護(hù)字段從而避免不正確或者不安全的賦值。
采用類替換類型代碼而不用枚舉,是因?yàn)轭惪梢钥紤]未來行為的擴(kuò)展,但是現(xiàn)在Java的枚舉功能更加強(qiáng)大了,所以可以根據(jù)自己的習(xí)慣來選擇。
當(dāng)重構(gòu)過程中產(chǎn)生的類需要擴(kuò)展包含更多行為時(shí),就可以考慮(7.4.State替換狀態(tài)改變條件語句)進(jìn)行重構(gòu)了。
缺點(diǎn)與優(yōu)點(diǎn):
+ 避免非法賦值和比較;
- 比使用不安全類型需要更多的代碼。
7.4. 用State替換狀態(tài)改變條件語句:簡化復(fù)雜的狀態(tài)轉(zhuǎn)換邏輯
優(yōu)點(diǎn)與缺點(diǎn):
+ 減少復(fù)雜的狀態(tài)轉(zhuǎn)換條件邏輯;
+ 簡化復(fù)雜的狀態(tài)改變邏輯;
+ 提供觀察狀態(tài)改變邏輯的角度;
- 增加設(shè)計(jì)的復(fù)雜度。
9.3. 引入Null Object:替換掉null的判斷邏輯,提供了對null的正確處理。
優(yōu)點(diǎn)與缺點(diǎn):
+ 不需要重復(fù)的null邏輯就可以避免null錯(cuò)誤;
+ 通過最小化null測試簡化了代碼;
- 當(dāng)系統(tǒng)不太需要null測試的時(shí)候,會(huì)增加設(shè)計(jì)的復(fù)雜度;
- 如果程序員不知道Null Object的存在,就會(huì)產(chǎn)生多余的null測試;
- 使維護(hù)變得復(fù)雜,擁有超類的Null Object必須重寫所有新繼承到的公共方法。
Singleton
6.6. 將Singleton內(nèi)聯(lián)化:把Singleton的功能搬移到需要它的類中,然后刪除Singleton。
優(yōu)點(diǎn)與缺點(diǎn):
+ 使對象的協(xié)作變得更加明顯和明確;
+ 保護(hù)了單一的實(shí)例,而且不需要特別的代碼;
- 當(dāng)在許多層次間傳遞對象實(shí)例比較困難的時(shí)候,增加了設(shè)計(jì)的復(fù)雜度。
9.2. 用Singleton限制實(shí)例化:減少內(nèi)存的占用,提高運(yùn)行的速度。
優(yōu)點(diǎn)與缺點(diǎn):
+ 改進(jìn)性能;
- 變成全局訪問;
- 當(dāng)對象含有不能共享的狀態(tài)時(shí),重構(gòu)無法進(jìn)行。
Observer
8.4.(P190)用Observer替換硬編碼的通知:
優(yōu)點(diǎn)與缺點(diǎn):
+ 使主題及其觀察者訪問松散耦合;
+ 支持一個(gè)或多個(gè)觀察者;
- 增加設(shè)計(jì)的復(fù)雜度;
- 面對串聯(lián)通知的時(shí)候,會(huì)進(jìn)一步增加設(shè)計(jì)的復(fù)雜度;
- 當(dāng)觀察者沒有從主題中刪除的時(shí)候,會(huì)造成內(nèi)存泄漏。
注:例子不熟悉,所以代碼沒太能領(lǐng)悟
Decorator
7.3. (P115)將裝飾功能搬移到Decorator:
優(yōu)點(diǎn)與缺點(diǎn):
+ 將裝飾功能從類中搬移去除可以簡化類;
+ 有效地將類的核心職責(zé)與裝飾功能區(qū)分開;
+ 可以去除幾個(gè)相關(guān)類中重復(fù)的裝飾邏輯;
- 改變了被裝飾對象的對象類型;
- 會(huì)使代碼變得難以理解與調(diào)試;
- 當(dāng)Decorator組合產(chǎn)生負(fù)責(zé)影響的時(shí)候會(huì)增加設(shè)計(jì)的復(fù)雜度。
注:這部分的代碼也不完備,理解起來有困難,建議大致了解作者思路就好了,Decorator應(yīng)該是個(gè)應(yīng)用比較廣泛的模式,以后自己可以通過實(shí)踐探索。
Decorator與Strategy的區(qū)別:
+ 都可以去除與特殊情況或選擇性行為相關(guān)聯(lián)的條件邏輯;
+ 都通過把條件邏輯搬移不對勁新的類中達(dá)到這一目的;
- Decorator主要把自己包裝在一個(gè)對象之外
- Strategy則用在一個(gè)對象之中。
11.2.(P278)統(tǒng)一接口:找出所有子類的公共方法,復(fù)制到超類中,在超類中執(zhí)行空行為。
11.3.(P280)提取參數(shù):通過客戶代碼提供的參數(shù)對字段進(jìn)行賦值。
注:這兩節(jié)都是輔助Decorator進(jìn)行重構(gòu)的。
6.2.(P51)將創(chuàng)建知識搬移到Factory:避免創(chuàng)建代碼到處蔓延。
優(yōu)點(diǎn)與缺點(diǎn):
+ 合并創(chuàng)建邏輯和實(shí)例化配置選項(xiàng);
+ 將客戶代碼與創(chuàng)建邏輯解耦;
- 如果可以直接實(shí)例化,用Factory就會(huì)增加設(shè)計(jì)復(fù)雜度。
注:可能作者認(rèn)為采用的是HTMLParser的原因,所以給出的代碼不全,不利于理解重構(gòu)的含義。
Visitor
10.2.(P259)將聚集操作搬移到Visitor:適用于從多個(gè)對象中聚集信息,適用于從不同的對象中聚集信息。使用起來難度較大,首選應(yīng)該是Collecting Parameter方法(P253,10.1.)。
優(yōu)點(diǎn)與缺點(diǎn):
+ 調(diào)節(jié)多個(gè)算法,使其余適用于不同的對象結(jié)構(gòu);
+ 訪問相同或不同繼承結(jié)構(gòu)中的類;
+ 調(diào)用不同類上的類型特定方法,無需類型轉(zhuǎn)換;
- 新的可訪問類需要新的接收方法,每個(gè)Visitor中需要新的訪問方法;
- 可能會(huì)破壞被訪問類的封裝性;
- 增加了代碼的復(fù)雜度。
注:
- 盡量使用通用接口把互不相同的類轉(zhuǎn)變成相似的類,而少用Visitor模式。
- Ralph Johnson:大多時(shí)候并不需要Visitor,有些時(shí)候則是必須要用,別無選擇。
- 可能作者認(rèn)為采用的是HTMLParser的原因,所以給出的代碼不全,不利于理解重構(gòu)的含義。
8.7.(P217)用Interpreter替換隱式語言
優(yōu)點(diǎn)與缺點(diǎn):
+ 比隱式語言更好的支持語言元素的組合;
+ 不需要新的代碼來支持語言元素的新組合;
+ 允許行為的運(yùn)行時(shí)配置;
- 會(huì)產(chǎn)生定義語法和修改客戶代碼的開銷;
- 如果語言很復(fù)雜,編程工作量較大;
- 如果語言很簡單,不需要考慮使用這個(gè)模式,否則會(huì)增加設(shè)計(jì)的復(fù)雜度。
2019.1.2.晨,結(jié)束閱讀。學(xué)習(xí)代碼下載
注:這種讀書筆記對他人幫助不大,因?yàn)檫@個(gè)只是為了進(jìn)一步理解作者的意圖而進(jìn)行的工作,通過輸入關(guān)鍵點(diǎn)來確認(rèn)自己是否真正領(lǐng)悟了作者想表達(dá)的意思。
當(dāng)輸入這些優(yōu)點(diǎn)與缺點(diǎn)時(shí)并沒有領(lǐng)悟,然后再通過輸入代碼來理解作者如何從一段不好的代碼重構(gòu)成一段好代碼的,重構(gòu)的過程中得到了什么、失去了什么,自己未來編寫程序中應(yīng)該如何平衡。
補(bǔ)充1:不要在意輸入代碼中出現(xiàn)的錯(cuò)誤,那是因?yàn)榇a不完備,許多類或者函數(shù)沒有提供源代碼。只要輸入的代碼可以排版和使用自動(dòng)提示工具,就說明代碼符合Java的語法規(guī)范,那么在閱讀全書的過程中可以一點(diǎn)點(diǎn)把代碼補(bǔ)全,或者根據(jù)自己的理解補(bǔ)全(這也是個(gè)理解重構(gòu)和提高編碼能力的機(jī)會(huì))。
補(bǔ)充2:如果需要下載HTMLParser,請注意是1.3的版本才是作者使用的。但是與HTMLParser相關(guān)的例子與原代碼并不完全相同,而作者并沒有在自己的例子中把邏輯構(gòu)造完整,因此建議跟HTMLParser相關(guān)的例子還是跳過吧,否則為了調(diào)試通過消耗時(shí)間太多。(我現(xiàn)在開始渴望直接看《重構(gòu)》那本書了。)
代碼目錄:
貸款風(fēng)險(xiǎn)估算程序(6.1,7.2,8.1,11.1)
HTMLParser(6.2,7.3,8.2,10.1,10.2,11.2,11.3)
對象-關(guān)系數(shù)據(jù)映射(6.3)
XMLBuilder(6.4,6.5,7.5,8.5)
二十一點(diǎn)游戲(6.6)
集合類庫(7.1)
SystemPermission(7.4,9.1,9.2)
Product(8.3,8.7)
CatalogApp(7.6)
JUnit(8.4)
數(shù)據(jù)庫查詢(8.6)
Applet(9.2)