From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Tue Apr 16, 2002 3:31 pm
Subject: Refactoring筆記-1
重整的定義
重整的定義:重整是改變軟體的系統的一種過程,它的方式是不改變程式碼的外
在行為而是改善程式碼的內部結構。這是一種有規律(discipline)的方式清理程
式碼以降低產生臭蟲的機會。本質上當你重整的時候你是改善已撰寫的程式碼的
設計。
一般的開發流程是希望先有一個好的設計,才能有好的程式碼。但是隨著系統的
成長因為需求的改變,以及程式碼會被修改,使得其原有的設計無法配合系統的
需求,設計與程式碼兩者之間便逐漸脫節,加上一般系統開發受限於時間及人
力,無法維持設計文件與程式碼同步,在後續開發或維護時原始的設計文件便變
成無用武之地。由於變動的關係,原來認為好的設計才有好的程式碼這個命題變
得沒有意義。因此如何在撰寫程式碼後仍能維持這些程式碼仍然是一個好的設計
變得非常重要。重整便是從這種兩難當中逐漸發展除來的一種技術。而既使你原
有的設計不是哪麼好,相對的程式碼就沒辦法寫得很具有結構性。此時採用重整
的技術你也可以讓程式碼變的非常具有結構性。
重整的每一個步驟都是非成容易的。你只是在類別中移動一些欄位,從方法中拉
出一些程式碼成唯一個新的方法,或者將程式碼在繼承的層級架構中向上或向下
移動。僅僅這些簡單的步驟便可以根本的改變程式碼的設計。
經有重整對於傳統的開發方式也產生了重大的變革,你不再需要在一開始便需要
有完整的系統設計,而是在開發過程當中不斷的發生設計的動作。這樣可以讓你
的系統不斷的開發也不斷的改善。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Thu Apr 18, 2002 10:38 am
Subject: Refactoring筆記-2
一個設計不良的系統最主要的是不容易變動。困難的原因是你無法指出哪些是需
要改變的。當你不知道哪些是需要變動的,有很大的機會這個程式將隱藏錯誤及
導致臭蟲。比如說你的輸出是一般的文件檔,當你同時需要輸出HTML格式時,你
的系統若沒有這個彈性,加入HTML格式將會導致你的系統重大的變動,因此隨之
而來的就是臭蟲。或者你是使用剪貼的方式修改程式碼,就長期而言你的程式碼
被改變的機會大增。因為既使你使用剪貼的方式加入新的功能,當你的企業規則
一經變動,你要便有許多地方要修改,但由於系統的複雜性讓你無法全面性的顧
及,你不知道哪些地方需要修改。所以當你變動的時候你希望你修改的地方愈少
愈好,這樣才不致於產生臭蟲。由於使用者總是有些新的想法可能導致系統的變
動,因此你的程式應該是容易修改的,這也就是為什麼需要重整了。
TIP:當你需要加入新的功能時,卻發現這個程式碼的結構很難讓你去修改以加
入新的功能時,你就需要先將這個程式重整以使得容易加入新的功能,然後才去
加入新的功能。
重整的第一個步驟,請先確定你已有一系列的測試保護。否則別輕易作重整。因
為沒有測試你不知道你所做的是否改變程式原有的行為。
TIP:重整前請確認你已有一個完整的測試。而且這個測試必須是可以自動執行
的。
重整的首要目標是將過大的程式區塊(如大的方法)分解成小的區塊,因為比較
小的區塊程式碼比較容易管理,容易操作或移動。同時重整的步驟不宜太大,每
一次只作一小小步驟的改變,然後執行測試以確認你的變動是否破壞了甚麼東
西。
TIP:重整是以極小的步驟改變程式,因為如果你犯了任何錯誤才能夠很容易的
找到臭蟲。
好的程式碼應該很容易的傳達它的意圖,而變數名稱是乾淨的程式碼的一個關
鍵,不要害怕改變事物的名稱以讓它更清楚的表達它的意圖。程式碼的溝通性是
非常重要的,重整最重要的工作就是提升程式碼的溝通性。程式碼的溝通性不止
是讓別人瞭解你所寫的程式碼,同時也是你對程式碼的瞭解有多少。
TIP:再愚笨的人都可以撰寫出讓電腦可以瞭解的程式碼,好的程式設計師所撰
寫的程式碼是讓人可以很容易瞭解的。
重整的結果可以是將責任的分配更佳,而讓程式碼更容易維護。這樣的程式碼與
程序性的程式碼有相當大的差異性。程序性的方式是我們習以為常的,但是只要
你常作重整你會逐漸習慣使用物件導向的觀念而且不會再走回頭路撰寫程序性的
程式碼。當你開始作重整時,你要掌握重整重要的節奏:測試,小小的改變,測
試,小小的改變,測試,小小的改變。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Mon Apr 22, 2002 8:13 am
Subject: Refactoring筆記-3-1
重整的定義
以名詞的定義方式:Refactoring在軟體內部結構所做的改變,以讓軟體更容易
瞭解及使維護的成本降低,而不至於改變外在的行為。
以動詞的定義方式:Refactor應用一系列的重整(refactoring)重組軟體結構
(restructure),而沒有改變其外在的行為。
重整不只是讓程式碼更乾淨,它更進一步的意義是提供一種技術以更有效且可以
控制的方式清理程式碼。因為你將知道應該採用哪一種重整及如何使用這種重整
以避免導致臭蟲,而且隨時測試。重整的首要目標是讓程式更容易瞭解及維護。
(另一種相對的作法是調整程式的執行速度。)重整的另一個特性是不改變軟體
的外在行為。
軟體開發時有另一種考量,稱為兩頂帽子。一頂帽子是重整,另一頂帽子是增加
新功能。你不可應該同時戴兩頂帽子。亦即重整的時後不要新增功能,新增功能
的時候不要重整。這兩個工作不要同時執行。因為新增功能意味著改變軟體的外
在行為,而重整時是以不改變外在行為為前提。因此若你同時做這兩項工作容易
讓你搞混而產生意外的臭蟲。當你開發軟體時;一開始你加入新的功能,接這你
重整你的程式碼以便讓你後續的工作更容易進行,如此反覆進行。所以你的開發
是在這兩頂帽子之間頻繁切換,這兩者每次所花的時間約是10幾分鐘。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Wed Apr 24, 2002 11:02 am
Subject: Refactoring筆記-3-2
為什麼你需要重整?
重整改善軟體的設計:
在軟體開發過程當中,程式設計師往往會去修改程式碼。修改的原因可能是因為
短期的目的或者是因為沒有完整瞭解程式碼原有的設計,因此導致程式碼失去結
構化,也因此不容易經由閱讀程式碼瞭解其結構。程式碼失去結構的情況有累積
的效應。越是缺乏結構性的程式碼越是難以維護,而越是難以維護越是沒有結構
性。不良設計的程式碼往往包含許多重複的程式碼,這些重複的程式碼在不同的
地方做相同的事情。因此消除重複的程式碼是改善設計最重要的事情。這是為了
往後維護工作提供簡便性。降低這些重複的程式碼無關於執行速度,因為這些執
行的動作還是存在。程式碼愈多則維護工作愈困難,因為相同的程式碼散佈四處
程式設計師無法往往無法完全掌握。經由消除重複的程式碼以確保程式碼所說的
任何事情只說一次且是唯一的一次(code says everything once and only
once),這才是良好設計的保證。
重整讓軟體易於瞭解:
程式碼主要是寫給電腦看的,但別忘了當有人要去維護這些程式碼時這些程式碼
還是要給人看的。所以往往程式設計師在撰寫程式碼的時候只是顧及到可以執行
而忽略了這些程式碼在未來還是要給人看的。 重整可以提升程式碼的可讀性。
[問題:甚麼樣的程式碼才具有可讀性?]而往往最需要取維護程式碼的就是撰寫
這些程式碼的程式設計師。程式設計師無法記住所有他寫的程式碼,所以當他維
護時需要先檢視程式碼。因此所有的東西都是要放在程式碼當中。[問題:寫註
解如何?]你可以從重整當中瞭解你不熟悉的(unfamiliar)程式碼,從重整當中
你不停的腦力激盪,然後改變程式碼以回應你所瞭解的結果。最後你再測試你瞭
解的及改變的結果看看是否仍然可以執行。經過重整後你可以看到以前你所看不
到的細節。
重整幫助你除蟲:
瞭解程式碼也幫助你防止臭蟲。或許有些人可以檢視一大堆的程式碼然後找出臭
蟲,但大部分的人沒有辦法。重整可以經由對程式碼的瞭解及程式碼結構的釐清
以降低臭蟲的機會。所以你不必要是一個偉大程式設計師,只要你擁有好的習慣
便可以是一個稱職的程式設計師,而重整就是你需要的一個好習慣。
重整可以讓加快你程式開發的速度
好的設計是開發速度的重要因素。沒有好的設計一開始你還是可以很快,但是過
不了多久速度會逐漸遞減。因為你花更多時間除蟲而不是增加新功能。你對已完
成的程式碼作任何的改變讓你耗費許多時間在瞭解系統及找尋重複的程式碼。為
了加入新功能迫使你在舊有程式碼基礎中補綴漏洞。好的設計也是維護程式碼速
度的重要因素。重整可以避免系統的設計走樣甚至改善設計,所以可以提升開發
的速度。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Fri Apr 26, 2002 10:00 am
Subject: Refactoring筆記-3-3
何時需要重整?
當你安排進度時,重整應安排多少時間?其實重整不應該預先留出時間,重整應
該是隨時在做的。你不用決定何時重整,而是當你需要作其它的事時自然會需要
重整來幫你。
三階段的規則
Don Roberts提出所謂的三階段規則:當你第一次做一件事時你只是去做。當你
第二次做類似的事時對於重複的事情你會猶豫,但你還是做重複的事。第三次做
類似的事時,你便重整。
TIP:碰到三次,你就要重整了。
當你新增功能時你便要重整
當你新增功能時,你要瞭解你必須修改的程式碼所以你要先重整。這些程式碼可
能是你或別人所撰寫的,當你想進一步瞭解這些程式碼是做甚麼事情,你便考慮
是否重整以讓程式碼更明顯。若是你便可以重整。
另一種情形是當原有的設計不是那麼容易讓你加入新的功能時。你不用苦惱於之
前的偏差,你只是重整以符合新增功能的需求。這不止是提供易於加入新功能同
時這也是一種比較快且平緩的方式。
當你需要除蟲時你便要重整
同樣的為了除蟲你要更瞭解程式碼,重整同時也可以提供幫助。當你知道程式中
有臭蟲,代表程式碼不夠清晰,所以重整也可以幫助你找到臭蟲。
當你審查(review)程式碼時你便要重整
有些組織實施程式碼審查,但成效不見得很好。程式碼審查可以幫助知識的擴
散,讓更多的人瞭解大型系統的更多觀念。而其中最重要的事就是程式碼必須乾
淨[問題:甚麼是乾淨的程式碼?]。對我自己寫的程式碼可能是很清楚,但對團
隊的其他人可就不一定,這是很普遍的。程式碼審查也提供所有人提出自己的意
見的機會。
重整可以幫助你審查別人的程式碼。當你有了一個想法某些地方可以更簡單的實
作時,當場你就可以做重整。當你做重整你會對程式碼愈清楚瞭解,而你所做的
重整也愈有用處。你不用想像應該如何做,重整的時候你馬上可以看到你所想
的。同時也能更深一層瞭解,這一層次的瞭解沒有重整是無法達到的。
重整同時幫助程式碼審查達到具體的結果。不只是概念,具體的實作當場就可以
實現。你可以從實作當中累積你的知識。
程式碼審查需要一個小組共同完成,最好是由一個審查人員加上原有的程式設計
師。由審查人員建議變動的方向,大家共同決定如何可以簡單的重整,然後去完
成。
較大的設計審查(design reviews)較容易在大型的團隊中獲得許多意見。展示程
式碼並不是最佳的方式。使用UML圖及以CRC卡推演整個劇情。因此設計審查採用
團隊方式,而程式碼審查採用個別的審查人員。
在XP中採用雙人組程式設計將審查的工作執行到極限。所有的開發工作都是兩個
人在一步機器中完成。事實上這就是持續性的程式碼審查,當然重整在整個過程
中不斷的在進行。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Fri Apr 26, 2002 2:55 pm
Subject: Refactoring筆記-3-4
為什麼重整是有用的--Kent Beck
程式有兩種價值,一個是今天它可以為你所做的,另一個是明天(未來)它可以
為你所做的。當我們撰寫程式時我們只考慮今天的價值。而當我們除蟲或加入新
功能時,我們是提升今日程式的能力而讓其更有價值。你必須瞭解系統今日所做
的只是故事的一部份。如果你能今日就把今日的事完成,但如果你做的方式是讓
明天的工作無法在明天完成,那你就會有漏失。請特別注意;既使你知道今天應
該做的事,但對於明日你是無法確認的。明天有任何的可能性。由於猜測未來的
變動性非常困難,對於專案也沒有好處,因此掌握現在的需求,把未來可能的變
動留待面臨的時候才來解決。重整就是這個解決問題的手段。當你發現昨日的決
定在今天是不當的,你可以改變這個決定,然後你面對的問題是今日的問題。同
樣的當明天你發現今日的決定是不當的,你也可以改變今日的決定。
甚麼是讓程式碼難以處理的原因:
?程式碼難以閱讀者是難以維護。
?程式碼有重複的邏輯者是難以維護。
?程式碼需要額外的行為,這些額外的行為需要你改變運行中(running)的程式
碼是難以維護的。
?程式碼中有複雜的條件邏輯者是難以維護的。
因此我們要讓程式碼容易閱讀,所有邏輯每一個都只存在一個地方且是唯一的地
方,不讓任何變動危及現有的行為,及讓條件邏輯表達的方式盡可能的簡單。重
整就是處理運行中的程式碼並增加其價值的一種程序,不改變其行為而提升其品
質且讓我們快速的持續開發。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Tue Apr 30, 2002 10:56 am
Subject: Refactoring筆記-3-5
你應告訴你的管理者哪些事情?
如果你的管理者是技術背景,應該是很簡單。如果你的管理者是品質導向,可以
從品質的角度切入。在程式碼審查中使用重整是最好的方式。因為技術審查最重
要的工作便是減少臭蟲及提升開發速度。如果有管理者聲稱他們是品質導向但更
注意進度,此時你最好『別說』!這時候你最好直接去做就好了,讓事實證明一
切。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Tue Apr 30, 2002 10:57 am
Subject: Refactoring筆記-3-6
迂迴及重整--Kent Beck
電腦科學這個學科相信所有的問題可以透過某種程度的間接方式解決。--
Dennis DeBruler
重整偏向於將大的物件分解成許多較小的物件,並將大的方法分解成許多較小的
方法。間接是一種兩面刃,你將一件事物分解成兩個意味者你有更多的事物要管
理,而當一個物件委託另一個物件會讓程式設計師更難以閱讀程式碼。因此會想
要降低間接性。別這麼快下結論,間接性會有所補償,下面是幾種方式:
讓邏輯共享成為可能:例如一個次方法(submethod)可以在不同的地方被呼叫,
或者是父類別中的方法可以在其所有子類別中共享。
區隔意圖的表達及實作的表達:為每一個類別及方法選擇有意義的名稱以表達你
的意圖。類別及方法的內含表達你對意圖的瞭解。如果你的內含就意圖而言是更
小的片段來撰寫,你可以撰寫程式碼溝通大部分關於其本身結構重要的資訊。
[註釋:不是直接實作程式碼來回映你的意圖,而是先使用有意義的名稱表達你
的意圖在間接以程式碼實作。]
隔絕變動:我在兩個不同的地方使用同一個物件,而我想要在這兩種狀況中之一
改變其行為。如果我改變物件,我冒著同時改變兩者的風險。所以我將這兩個物
件做一個父類別而改變其中之一的行為。
將條件邏輯編碼(encode):應用多型的機制彈性且清楚的表達條件邏輯,將條件
式改變成訊息(messages)可以同時降低重複的程式碼,提升清晰度及彈性。
這是所謂的重整的技巧(game):維護系統目前的行為,你如何可以讓你的系統更
有價值,包括提升品質或降低成本?透過這個技巧最通常的變化就是檢視你的程
式。找出一個或多個產生『間接的利益』的地方,然後將間接的特性放入而不改
變程式現有的行為。此時你的程式具有在未來可資利用的價值,因為它具有更好
的品質。
其次在重整技巧中不常發生的,找出沒有價值的間接部分並將之取出,一般這是
採用中間層(intermediate)方法的形式,這個方法只是用於某種意圖但這個意圖
並不是長久存在。或者可能是一個元件,這個元件是你期望共享或多型但最後證
明只用在一個地方。當你發現類似此種寄生在間接性的事物,把它剔除。同樣的
你會獲得一個更有價值的程式,並不因為只是具有前述四種品質之一或更多,而
是因為使用更少的間接性而從品質獲得相同價值。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Thu May 2, 2002 11:36 am
Subject: Refactoring讀書筆記3-7
重整的問題
當你學習一種新的技術,這個技術可能可以大大提升你的能力。但往往你也會忽略掉這個技術隱藏的危機。任何技術都有其限制。
資料庫
使用資料庫的程式重整時會有問題。因為使資料庫的程式其程式碼與資料結構(schema)緊密結合。另一個原因是資料遷移(migration)。既使你讓你的系統的資料結構與物件模式之間的關連性很小,改變資料結構[註釋:重整時改變資料結構]還是會迫使你遷移資料,這可是一個大工程。使用非物件化資料庫處理這種問題的方式是在你的物件模組及資料庫模組之間放入一層軟體區隔兩者,這種方式可以幫助你區隔兩者個別的變動不致互相牽動。當你更新一邊你不用同時更新另一邊,你只要同時更新中間層。中間層提供彈性但也增加複雜性。既使你不為重整考量,維持一個中間層在許多情況下如多個資料庫或複雜資料庫模組的時候中間層還是需要的。一開始你不用建立中間層,只要一旦發現你的物件模組是易變的時候才加入中間層。
物件化資料庫有利也有弊。有些物件導向資料庫提供自動化的依據物件作資料遷移。這提供省力但卻是耗時的工作。若資料遷移不識自動化,你必須手動遷移資料更是耗時耗力,而且必須同時分心於資料結構與類別結構及欄位等的變遷。
改變介面
物件有一個重要的特性就是允許你改變實作而不改變其介面。因為既使你改變內部實作對於使用這個物件的客戶端仍然不至於受到影響。但是改變物件的介面可能導致的問題就多了。重整時如使用改名(Rename)的方法便牽涉到變動介面的議題。變動方法的名稱時,如果你可以接觸到所有呼叫這個方法的程式碼那沒問題,你可以去改變呼叫到這個方法的程式碼。問題出在呼叫這個方法的程式碼是你無法找到或變動的。此時這個方法的介面稱之為『發行的介面(published interface)』(更開放的公開(public)介面)。這時候你要作的必須更複雜。
處理的方式是你要同時保有原有的介面而不只是改名,除非等到你的使用端都能隨之改名為止。還好這並不是太棘手。只要讓原有的介面(保留名稱的方法)呼叫新的介面即可。這樣可以讓兩個介面並存。絕對不要複製舊介面修改成新的介面,這樣便有了重複的程式碼。在Java你也可以使用其『反對設施(deprecation facility)』標示這個舊有的方法是不建議使用的,以讓你的使用者知道這個方法將不再適用。
保留介面一般是可行的,但有些痛苦。你必須維持額外的方法一段時間,也會讓介面複雜化。另一種方法就是別發行你的介面。這不意味著你不可以這樣做尤其是你開發的系統是供外部使用的。這裡強調的是開發團隊中所有的開發者彼此將介面發行給別人使用。尤其是在組織中這些程式碼是個別管理的。當然你也可以像XP一樣所有程式碼都是共有的及雙人組程式設計的方式,就可以避開這個問題。
TIP:別貿然發行你的介面。修改你的程式碼擁有規則以讓重整易於進行。
在Java中改變介面有一個特別的問題:在例外的丟出字句中加入一個例外。這並未改變識別(signature),因此你無法使用委託(delegation)來處理,因為無法編譯。你可以為新方法選擇一個新名稱而讓舊方法呼叫新方法,並將通過(checked)的例外轉成捕捉(unchackd)的例外。你也可以丟出一個捕捉的例外,但你也失去檢查(checking)的能力。當你這麼做的時候,你可以通知你的呼叫者這個例外在未來的日子裡變成一個通過的例外,他們有時便可以將處理者(handlers)放入他們的程式碼中。因此比較好的方式是在包裹(package)中定義一個父類別以確保公開的方法在他們的丟出字句中只宣告這個例外。這種方式你可以定義你所想要的例外次類別。但這種方式對於只知道一般性例外的呼叫者是無效的。[問題:問題在哪裡?]
設計改變的是不容易重整的(Design Changes That Are Difficult to Refactor)
你重整的方式能夠脫離任何設計的錯誤(Can you refactor your way out of any design mistake),或者某些設計決策是極為核心以致於你無法依賴重整在隨後改變你的心意?在這部分沒有很多資料可以佐證。在實務上重整有時很容易有時則否,有各種的情況。
此時你可以用想像的重整。當你考慮設計的各種替代方案,你可以考慮從一種設計方案重整成另一種時的困難度。如果看起來不是很難,那不用擔心如何選擇,只要選擇比較簡單的設計方案,既使它並未涵蓋所有潛在的需求。但是如果你沒辦法找到較簡單的重整方式,你便需要更用心作設計囉。不過這種情形似乎不多。
你何時不再重整?
當你覺得應該從頭開始重寫的時候。當你覺得重寫比重整容易時。這個決定有時並不容易,但也沒有很好的指引可以告訴你。有一個比較明顯的徵兆是當目前的程式碼沒辦法跑。此時不管你如何嘗試測試還是有許多臭蟲。當然重整是在你的程式碼可以正確的執行後才有用的。不能跑的程式作重整是沒有意義的。另一種路徑是將將一大塊的程式封裝成元件(component),此時你可以使用重整加上重建(refactor-versus-rebuild)的方式一次處理一個元件。這是一種可行的方式,但是並沒有詳盡的規則可循。但對於一個遺產(legacy)系統,這可能是一種方向。
另一種情況是,當你已面臨交件期限的時候。此時重整的生產力不如在交件之後。除了面臨期限你不應該推辭重整。證據顯示重整可以提升生產力,沒有時間往往代表你需要重整。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Mon May 6, 2002 9:22 am
Subject: Refactoring讀書筆記3-8
重整與執行速度
重整的結果可能導致執行速度的降低,但也對調整執行速度較具啟發性。所以最好的方式是先撰寫容易調適的程式再來調適使之符合要求的執行速度。撰寫速度快的程式有三種方式:
最嚴謹的方式是使用『時間預算(time budgeting)』,這往往是使用在嚴酷的及時系統。這種方式是將設計切割成一個個的元件,每一個元件都給定一定的資源預算--時間及空間。這些元件不得超越給定的預算,但是相互交換時間預算是允許的。這種方式嚴格的控制執行時間。
第二種方式是持續監視的方式。這種方式是每一個程式設計師隨時盡可能的維持其高執行速度。這是一般的方式且是直覺性的。但實施的效果不是很好。因為為執行速度的改變往往使得程式碼難以再處理反而降低開發的速度。那些改善執行速度的程式碼散佈在系統的各處,而每一個改善執行速度的程式碼只是為程式的行為狹隘的意圖。其實真正與有關執行速度有關的程式碼只是非常少部分的,若是你改善所有的程式碼其中有百分之九十是浪費的,因為你改善的大部分並不是執行太頻繁的。你愈是花時間改善,愈是浪費時間,因為缺乏程式的清晰度。
第三種方式是改善那些可以獲得百分之九十執行速度的程式碼。這種方式是以良好的分解方法(well-factored manner)建構你的程式而不要在意執行速度直到調整執行速度階段,一般是在開發的後段。在執行速度最佳化階段,你可以依循特定的步驟調整程式的執行速度。一開始你在一個背景(profiler)下執行程式,這個背景監視程式並告訴你程式使用的時間及空間。以這種方式你可以找到讓程式執行慢的小部分。再依據你以往改善執行速度的方式針對這點改進。因為這種方式只針對小部分作改善你可以節省許多時間。當你完成並編譯、測試並且在相同的背景下執行看看執行速度改善的結果,若否回復改變的結果重新嘗試其他的方式直到改善結果是你滿意的。良好分解的程式對於此種方式有兩個助益:1.讓你有時間調整執行速度,因為你有良好分解的程式碼,你可以快速的增加功能,而讓你有更多的時間聚焦於執行速度。(使用背景可以讓你將時間放在在正確的地方)2.讓你有更細緻的區塊作為執行速度的分析。你的背景引導你到有問題的小部分,而讓你容易的調整。而清晰的程式碼你能更容易瞭解你的程式碼可能改善的可能方向及哪一類調整是比較可行的。
重整確實有可能降低程式的執行速度,但你若還是持續重整,最後可以幫助你調整執行的速度。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Thu May 9, 2002 8:13 am
Subject: Refactoring讀書筆記4-1
程式碼中的臭味
--------------------------------------------------------------------------------
本章告訴我們如何寫一個好的物件導向程式。
你除了知道重整的用處及如何重整,最重要的是你要知道何時要重整及何時重整才算完成。Martin及Kent兩人認為程式碼是否需要重整是依據所謂的『程式碼的味道』來決定。之所謂稱為『味道』是因為要具體規範是很困難的,目前可以規範的有許多模糊的空間。所以這些規範有許多是依靠人的直覺。重整與待解決的問題之間往往是間接性的。所以學習重整需要你培養你的感覺如多少個實體變數是太多的,多少行程式碼是太多的等等。然後使用該書內頁所列舉項目的配合書中章節的說明體會你的程式碼有哪些『臭味』。
--------------------------------------------------------------------------------
重複程式碼(Duplicated Code)
重複程式碼是程式的臭味中排名第一需要重整的。只要你在兩個以上的地方發現相同的程式碼結構你就需要重整了。其中最單純的是在一個類別中的兩個方法有相同的表示式。你可以使用『淬取方法』讓兩個地方呼叫相同的程式碼。另一種常見的情況是在兩個兄弟次類別中有相同的表示式,你可以使用『淬取方法』或『欄位上移』的方式降低重複的程式碼。如果這些程式碼只是類似而非完全相同,你可以使用『淬取方法』區隔相同的部分與不同的部分。此時你發現可以使用『形成模版方法』。如果這些方法作的是相同的工作,但卻有著不同的演算法,你可以選擇其中之一比較清晰的演算法並使用『替代演算法』。
如果在兩個不相關的類別中有重複的程式碼,考慮在其中一個類別使用『淬取類別』,並將這個類別當作一個元件在另一個類別中使用。另一種可能性是方法只屬於這些類別中之一而由其他類別呼叫,或者是方法屬於第三個類別而由原來這兩個類別參照,你就需要決定這個方法在哪裡是比較有意義並且確保只有在那裡出現。
[思考:重複程式碼對於系統的維護有相當大的障礙,當你需要新增功能或除蟲時,一旦牽涉到這些程式碼你必須在程式當中找到所有相同的部分一一修改,一旦有一些沒有找到便形成一個臭蟲。同時消除重複的程式碼有利於程式的瘦身,同時因為功能職責的明確有利於程式碼的閱讀。]
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Fri May 10, 2002 1:28 pm
Subject: Refactoring讀書筆記4-2
長方法(Long Method)
[思考:長方法意味著這個方法負擔的責任太大,其中有太多的操作。因此長方法不易閱讀更別說維護了。同時將許多操作聚集在一個方法中這些操作無法分享出來給其他物件使用間接導致重複的程式碼。因此長方法需要將其個別可以獨立的操作程序分割出來成為個別的方法以便共享也因為簡短易懂便於維護。]
物件導向程式設計最重要的特性是『簡短的方法』。初次接觸OO的人往往覺得程式好像都每有在做甚麼事,看到的是一系列無盡的委託。這就是前述的間接的利益』--表達(explanation)、共享(sharing)及選擇(choosing)--這都是由小方法所支援。因為長方法不容易瞭解,因此舊有的程式語言使用『副程式(subroutine)』或函數,但仍不容易形成小的方法。OO可以降低這種程序性的呼叫。但對於程式碼的讀者來說還是很困擾,因為他需要隨著『次程序(subprocdure)』變換背景(context)才能瞭解這個次程序到底在做甚麼事。現在的開發環境允許你同時檢視兩個方法,有助於閱讀,但最重要的關鍵是這些小的方法有一個好的命名規則,如果你命名的好你不需要去看方法的實體(也就是實作的程式碼)。
採用小的方法有一個淨效應(net effect)就是你必須積極的分解(decomposing)方法。我們嘗試的方式是當我們需要註解某些事項時,我們便寫一個方法來替代。這個方法中包含的程式碼被註解,但方法是以程式碼的意圖來命名而不是它如何做來命名。這個方法所取代的可以是一群程式碼甚至只是一行程式碼,更甚者這個方法的呼叫(方法名稱)比它所取代的程式碼更長。這種方式可以讓方法名稱表達程式碼的意圖。這裡的關鍵不是方法的長度而是方法做甚麼與如何做之間根本的差異。
要縮短方法的99%使用『淬取方法』。找到方法中似乎是結合比較好的部分另成一個新方法。如果你有一個方法其中有許多參數及臨時變數(temporary variables)便需要『淬取方法』讓這個方法更容易閱讀。你也可以使用『使用查詢替代臨時變數』以降低臨時變數的使用。一長串的參數可以透過『帶入參數物件』及『保存完整的物件』來減肥。如果這些你都嘗試過仍然沒有辦法降低參數及臨時變數的數量,你可以採用最後的必殺絕招『以方法物件取代方法』。
你如何決定那一塊程式碼需要淬取,有一種技術就是找尋註解,這是某一類重要的語意學上的信號。標示有註解的某一區段程式碼告訴你它所做的可以以一個方法取代而這個方法的名稱是以這個註解為基礎。既使只是一行的程式碼也值得淬取出來,因為註解代表者需要表達一些意圖。[註釋:這意味著程式碼沒有需要註解或說明文件]。條件表示式及迴圈也是需要淬取的重要信號。使用『分解條件』處理條件表示式。而處理迴圈的方式是將迴圈及迴圈中的程式碼淬取成其本身的方法。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Mon May 13, 2002 2:30 pm
Subject: Refactoring讀書筆記4-3
大類別(Large Class)
[思考:大的類別表示其擁有太多的方法或實例變數,容易產生重複的程式碼。因此需要將大類別依職責分割成小類別。]
如果一個類別所做的事情太多,顯示出來的是太多的實例變數(instance variables)。如果一個類別有太多的實例變數,它的背後就是重複的程式碼。[問題:為什麼有太多的實例變數會導致重複的程式碼?多大的類別或包含哪些元素的類別才是大類別。]
你可以使用『淬取類別』包裹數個變數。挑選彼此有意義的變數集合成一個元件。其次在一個類別中某些變數的共通的固定設備(prefixes)[註釋:各類別皆須使用到的變數或環境狀態等。]或設備後設(suffixes)[註釋:對於固定設備使用後的處理。]可以考慮包裹成一個元件。如果這個元件可以當作次類別,你可以使用『淬取子類別』來處理。有時候一個類別並不是一直使用其所有的實例變數,你可以多次使用『淬取類別』及『淬取子類別』。
同樣的有太多程式碼的類別也是造成重複程式碼的元兇。因此把一個大的方法重整成數個小的方法是解決的方式。另一個訣竅便是依據使用端如何使用這個類別並為每一個使用端使用『淬取介面』,這可以提供你後續如何分解這個類別。
如果你的大類別是使用者介面(GUI),你可以將資料及行為移至個別的領域物件(domain object)。這種方式可能要維持某些重複的資料在兩個地方並且維護其同步。『重複觀察資料』可以處理這種狀況。此時,尤其是你使用較舊的抽象視窗工具組(Abstract Windows Toolkit AWT)元件,你可以依循這個方法移除GUI類別並以Swing元件取代。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Wed May 15, 2002 8:10 am
Subject: Refactoring讀書筆記4-4
長參數列(Long Parameter List)
[思考:有長參數列的方法使用時必須瞭解各個參數所代表的意義,因此不易瞭解及閱讀。使用廣域變數的做法雖可以改善,但是容易造成變數被其他物件所變動而不知有其風險。因為物件可以攜帶多個資料,利用物件傳遞資料可以簡化參數列。同時方法可以依其需要從提供資料的物件獲取需要的資料,這些資料可以受到提供資料物件的保護。]
傳統程式常利用傳遞參數的方式提供程序或函數所需要的資料。這包括使用廣域變數,而廣域變數在使用上有許多風險。物件導向的程式使用另一種機制,當你需要某些資料時可以要求其他物件來提供。因此你不是依據方法的需求傳進所需要的參數,而是傳進足夠的物件讓方法自行取得起所需的資料。而一個方法所需要的在其所在的類別(host class)都可以提供。因此OO程式的參數遠少於傳統的程式。參數太多的缺點是:難以瞭解、易變以致於難以使用,這是因為當你需要更多資料時往往需要改變。這些改變可以透過物件降低。
當你可以從一個已知的物件獲得參數的資料時可使用『以方法取代參數』。這個物件可能是一個欄位或另一個參數。使用『保存完整的物件』從一個物件取得一批資料並以物件本身取代這些參數。如果你所需的資料不是一個物件可以提供的,使用『帶入參數物件』。
這裡有一個例外,當你很明確的不希望在被呼叫的物件與較大的物件之間產生相依關係。此時將資料解開並單獨的以參數方式傳送也是合理的,但你需要特別小心。如果參數列太長而且太頻繁的改變,你要重新思考你的相依結構。[思考:物件相依關係也就是委託關係,太複雜可能導致哪些後果。也就是說關係太密切是不是會使得程式的彈性降低。當你想變動一個物件的時候因為相依關係使得變動很困難。但是委託關係在OO中是物件合作的基礎,在設計樣式(design patterns)中有太多的範例。]
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Wed May 15, 2002 9:11 pm
Subject: Refactoring讀書筆記4-5
分歧的改變((Divergent Change)
[思考:分歧的改變是因為因應不同的外部需求程式必須提供的彈性。如輸出可能有多種不同的格式(如純文字檔、HTML檔或者資料庫等),而設計時只針對其中一種設計,但預期可能會有其他種可能性,因此程式必須預留可能性,或者在增加新功能時使用重整使之具有這種彈性。]
我們使我們的程式結構化以便於變動。當我們變動程式時我們希望只是跳到程式中的某一點做改變而不至於影響其他的部分。當你的類別的變動是因為不同的理由;如:每當資料庫變動你便需要變動某三個方法或者因新的財務需求便需要變動某幾個方法。因此每次需求的改變都會導致某些類別跟著變動,而且新加入的類別應該表達這些變動。要明確這種方式你必須界定特定情況所有可能的改變並使用『淬取類別』匯集這些情況。[註釋:建立一些『介面』,以便依據變動更換相對應的物件。]
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Fri May 17, 2002 9:03 am
Subject: Refactoring讀書筆記4-6
霰彈式的外科手術(Shotgun Surgery)
[思考:當你對程式做一個變動的時發現需要同時變動到程式的許多地方,表示這些需要變動的地方有其相關性,因此將這些具有相關性的地方利用重整將之集合在一個地方可以方便程式碼的變動,同時提供程式更好的結構。]
與分歧的改變類似但相對的。當你每次做某種改變皆需要在許多不同的類別中做小改變。當改變是散佈在四處你很不容易找尋要變動的地方而忽略了某個重要的地方。
此時你可以使用『移動方法』及『移動欄位』把所有的改變放在一個單一類別中。若目前沒有一個類別適合的話便建立一個吧。一般你可以使用『行內類別』把一整堆的行為放在一起。
分歧的改變是在一個類別中有許多不同的變動,而霰彈式的外科手術則是一個改變會動到許多類別。兩種方式你都想要讓改變與類別是 一對一的關係。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Fri May 17, 2002 9:06 am
Subject: Refactoring讀書筆記4-7
特性的羨慕(Feature Envy)
[思考:當你設計一個方法,而這個方法需要其他物件來提供大部分的資料,此時可以考慮將這個方法移到提供資料的物件當中直接存取此物件的資料。這樣做的好處是降低程式碼的複雜度。也比較符合OO的原則:資料及處理資料的程序包裹在一個物件之中。]
OO技術的重點是將資料與處理資料的程序包裹起來的技術。有一種典型的味道:一個方法似乎對其他的類別較有興趣而非這個方法的宿主類別。而其最通常期盼的是聚焦於資料。大多時候一個方法都是啟動其他物件的get方法獲得某些值以計算所要的結果值。解決方式很明顯,這個方法顯然的應該在那個地方,你可以使用『移動方法』把它移到它應該存在的地方。[註釋:這個方法應該移到它最常呼叫獲取資料的物件中。]有時只有部分的方法有這種特性,你可以先使用『淬取方法』在這些方法上,然後在以『移動方法』將它以到它應該存在的地方。
當然並不是所有的方法都是如此。往往一個方法會使用許多其他類別的功能,那它應該存在哪一個類別中呢?一般看哪一個類別資料最多便是這個方法的宿主。或者使用『淬取方法』將方法分解分別放到個別適當的類別中。
有些樣式會打破這個規則。在GoF『設計樣式』一書中的策略(Strategy)樣式及訪問者(Vistor)樣式即是。Kent Beck的自我委託(Self Delegation)亦是。 不過最重要的是要把會變動的部分放在一起。資料及參考這些資料的行為一般是放在一起,但有例外。例外發生時,我們把會共同產生變動的行為放在一起。策略樣式及訪問者樣式讓你方便的改變行為,因為他們將會被改寫(overriden)的行為分離出來,間接的降低了未來變動的成本。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Mon May 20, 2002 1:18 pm
Subject: Refactoring讀書筆記4-8
資料叢集(Data Clumps)
[思考:物件包裹資料,因此其包裹的資料應該是具有相關性(處理資料的相關性),因此把具有相關性的資料群集在一個物件當中有利類別清晰的表達其意義。]
資料項目往往就很容易群集在一起。你會常常發現三四個資料項目群集在許多地方,如:一些類別中的資料項目及在許多方法中的參數。一串資料事實上是應該群聚於他們自己的物件當中,所以先找找看這些資料欄位如何群聚,使用『淬取類別』將他們存放在一個物件中。然後注意方法的宣告識別,使用『帶入參數物件』或『保存完整的物件』幫忙減肥以減少參數列及簡化呼叫。別擔心只用到新物件中部分的欄位,只要以新物件取代這些欄位你就賺到了。[思考:當所有存取這些資料的人都是使用這個物件時,其對於資料的變動都會反應在其他人的資料上。這應該是以物件取代資料欄位的重大利益。]
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Wed May 22, 2002 10:06 am
Subject: Refactoring讀書筆記4-9
原始型別的著迷(Primitive Obsession)
[思考:使用語言所提供的原始型別的資料是傳統程序性語言的方式。但物件導向的物件除了資料以外尚包括處理的程序,因此我們應該使用OO的優點將原始型別的資料包裹在物件中使用,任何對資料的操作皆透過物件的方法去執行。]
多數的語言中有兩類資料:紀錄(record)型別及構成紀錄的原始型別。使用物件可以模糊化這些資料的這些語言內建原始型別與大類別間的界線[註釋:紀錄本身就是這個目的,但使用物件更好用。但一般物件新手不善使用這個優點。]。[思考:原始型別的資料沒有操作的能力,以物件取代可以增加資料處理能力。同時物件比較能代表實際世界的『物件』。]如money類別可以包含數量及金額等。你可以在個別的資料值使用『以物件取代資料值』。如果這些資料值是標誌碼(type code)且其值不影響行為使用『以類別取代標誌碼』,如果你有條件式是依據這些標誌碼使用『以子類別取代標誌碼』或『以狀態/策略樣式取代標誌碼』。如果你有一群欄位要放在一起使用『淬取類別』。如果你發現參數列中是原始型別使用『帶入參數物件』。如果你
Areca Chen
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Wed May 22, 2002 10:09 am
Subject: Refactoring讀書筆記4-10
切換的陳述(Switch Statement)
[思考:使用切換陳述的缺點是因為每一個Case的選項中程式碼往往是類似的程式碼;只有少部分的差異,這就是重複的程式碼。只要使用多型的機制便很容易取代切換的陳述。]
OO最明顯的特徵是比較少用切換陳述(如case的子句),因為切換陳述是重複程式碼的根源。你會發現相同的切換陳述散佈在程式的不同地方。當你在一個切換陳述中加入一個子句,你必須同時更新所有的切換陳述。OO多型的概念提供精緻的方式處理這個問題。
每當你想用切換陳述時你要考慮一下多型。一般切換陳述是在標誌碼之間切換,使用方法或類別處理標誌碼,先使用『淬取方法』淬取切換陳述然後使用移動方法』放到使用多型的類別當中,然後你必須決定到底要用以子類別取代標誌碼』或以狀態/策略樣式取代標誌碼』。
如果你只是少部分使用到切換陳述,使用多型太麻煩而不想做改變,使用『以明確的方法取代參數』是個不錯的選擇。如果你的case條件中有一個是空的(null)試試『帶入空物件』。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Thu May 23, 2002 8:08 am
Subject: Refactoring讀書筆記4-11
平行繼承層級架構(Parallel Inheritance Hierarchies)
[思考:當有兩個類別層級架構是相依,每當為其中之一增加子類別時必須無時也為另一個類別相對應的增加一個子類別。這種類別層級架構容易造成耦合性不利維護。]
平行繼承層級架構是霰彈式的外科手術的一個特例。當你從一個類別繼承產生一個子類別時必須同時也在另一個類別繼承產生一個資類別。你可以使用『移動方法』及『移動欄位』去除這種狀況。[問題:是甚麼情況會造成兩個類別的繼承層級架構相依?反覆樣式(Iterator)視一種情況嗎]
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Thu May 23, 2002 8:11 am
Subject: Refactoring讀書筆記4-12
懶惰的類別(Lazy Class)
[思考:怎麼樣的類別算是沒有價值的類別?]
一個類別所做的事情太少(或沒有價值(pay for itself))便應該去除。如果這個類別是子類別可以使用『瓦解層級架構』將他排除。近乎無用的元件應改使用『內包類別』處理掉。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Mon May 27, 2002 10:44 am
Subject: Refactoring讀書筆記4-13
純理論的普遍性(Speculative Generality)
[思考:XP強調不要為明天作準備。所以設計類別架構時不要考慮到要先有父類別,有需要的時候才去產生父類別。委託的設計也不用過早,需要時才做。]
當有人要為未來可能需要的某些功能預做準備,此時你就可以聞到這個味道。這種情況往往難以瞭解及維護。如果你有一個抽象類別並未做太多事使用『瓦解層級架構』。不必要的委託可以使用『內嵌類別』處理。方法中不必要的參數可以使用『移除參數』去除。方法名稱含有舊有的抽象名稱應該使用『方法改名』處理。
純理論的普遍性可以透過方法的使用端或測試案例的類別發掘出來。如果你發現這種情況便將他移除並用測試檢驗。如果有一個方法或類別是用來輔助測試案例提供檢驗合法性的功能,你還是要保留這個方法或類別。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Mon May 27, 2002 11:11 am
Subject: Refactoring讀書筆記4-14
臨時欄位(Temporary Field)
[思考:傳統程序性語言為了處理資料的需要會有許多暫存變數,這些變數對維護人員(不管是否是原撰寫人)要瞭解其作用非常的困難,這包括變數所儲存的資料代表甚麼以及這個變數會經過哪些運算,甚至有些人在程式碼中重複使用這個變數儲存不同的資料。處理方式首先盡可能降低這些實體變數的使用,其次將這些變數及處理的程式碼集中管理。]
物件中有些特定用途的實體變數只是在特定情況下使用。此時這個程式碼是難以瞭解的,要瞭解這些變數代表甚麼意義就很困難。
使用『淬取類別』建立一個空間儲存這些變數並將相關的程式碼放到這個元件中。使用『帶入空物件』降低條件表示式的使用以減少變數的使用。
臨時欄位產生的一般使用情況是當你的演算法很複雜時便需要一些儲存中間暫時資料,而且一般不想使用長參數列傳遞資料便需要把資料放到變數中。而這些 暫存變數只在這個演算法中有用,離開了演算法部分便毫無意義。你可以使用『淬取類別』將相關的變數及方法集中起來,這個新的物件稱為『方法物件(method object)』[Beck]。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Tue May 28, 2002 8:38 am
Subject: Refactoring讀書筆記4-15
訊息鏈(Message Chains)
[思考:這是由於物件導向特性(間接性或委託)的濫用,訊息鏈的情況對於閱讀程式碼有相當的阻力。當發現中間的物件並沒有做任何處理而只是呼叫另一個物件來處理,我們就必須思考是否直接呼叫可以處理這個訊息的物件。訊息鏈特別指出的是一條單獨的鏈,這個鏈並不屬於特定繼承結構的一支。(我們可以想像例外的舉發猶如一條訊息鏈當一個例外處理沒辦法處理時便向上拋出直到有一個例外處理可以處理這個例外,其實這當中每一個例外處理機置還是需要做判斷是否處理。)]
訊息鏈是當一個客戶端向一個物件要求某個服務時,這個物件還必須要向另一個物件請求服務,甚至第二個物件還必須要向第三個物件要求服務,如此一直下去。你可以看到一長串的get方法。這種狀況使得客戶端與此種巡航(navigation)結構緊密結合在一起。要改變這種間接的關係便需要改變客戶端。
使用『隱藏委託』,處理的方式有很多種。理論上你可以在鏈上的每一個物件動刀,但這可能讓每一個中介物件變成中間人(Middle Man)。最好看看最終被使用的物件是哪一個,再嚐試使用『淬取方法』取出使用到它的程式碼片段形成一個方法,然後使用『移動方法』將這個方法放到鏈的最底端(註釋:原始呼叫者)。但如果這個鏈中某一物件的許多使用端需要繼續巡航剩其餘的鏈,加一個方法來執行。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Wed May 29, 2002 8:46 am
Subject: Refactoring讀書筆記4-16
中間人(Middle Man)
[思考:委託主要的意旨在於物件的責任;將相關或類似的責任集中在一個物件中。其他物件需要這些功能時便向這個物件請求執行。任意物件中每一個方法應該有其本身的職責,透過委託應該只是其執行其職責時呼叫其他物件來幫忙,而非將整個職責直接委託其他物件的方法,否則這個方法基本上是沒有意義的。你只要直接呼叫這個擁有適當職責的物件即可。]
伴隨這物件的封裝就是需要委託。中間人就是受委託者。若你發現一個類別發現它半數的方法都是委託其他類別,你便可以使用『移除中間人』並直接與知道如何執行工作的物件溝通。如果只有少數方法是如此,使用『內嵌方法』將這些含入(inline)呼叫物件中。如果有額外的行為,你可以使用『以層級架構取代委託』將中間人轉換成實際物件的子類別,這樣可以讓你擴充行為而無須追逐(chasing)所有的委託。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Wed May 29, 2002 8:47 am
Subject: Refactoring讀書筆記4-17
不當的親密關係(Inappropriate Intimacy)
[思考:物件存取其他物件的內部私有部分有害物件的封裝性。直接將這兩個物件共同繼承一個父類別是一種方式。或者將共同的部分獨立出成為一個物件也是一種處理方式。]
當類別過度探索其他類別的私有部分,代表這個物件有不當的親密關係。使用『移動方法』及『移動欄位』來降低親密關係。看看你是否可以使用『改變雙向的結合為單向』修改配置。如果這些類別有相同的興趣,使用『淬取類別』將共同的部分放在一個安全的地方使之成為最誠實(honest)的類別。或者使用『隱藏委託』讓其他物件當作媒介(go-between)。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Thu May 30, 2002 8:25 am
Subject: Refactoring讀書筆記4-18
有不同介面可選擇的類別(Alternative Classes with Different InterFace)
[思考:把相同職責的方法集中放在一個物件中不止符合物件的原理--以物件職責為中心,同時便於維護工作。]
有些方法執行相同的工作只是其宣告方式不同,使用『方法改名』改變其名稱。但這一般是不足的,此時表示這些類別所做的還不足。持續使用『移動方法』這些行為到類別中直到其協定(protocols)一致。若你覺得使用『移動方法』太冗長也可以使用『淬取父類別』來輔助。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Thu May 30, 2002 8:27 am
Subject: Refactoring讀書筆記4-19
不完整的程式庫類別(Incomplete Library Class)
[思考:當你的程式庫類別所提供的功能不能滿足你的需求時,你要如何擴充你的程式庫類別?這裡有提供兩個方法。]
再使用(reuse)是OO的目標。而再使用的技巧一般是應用程式庫類別。但建立程式庫類別往往不是很周延,往往直到我們真正建構的時候才知道我們需要的是甚麼。所以建立程式庫類別不是一件很容易的事。一般程式庫類別的格式不是很好有時很難以修改成你所想要它所做的。此時使用試真法(tryied-and-true)的『移動方法』是無法改善的。
針對這個臭味有幾種特定的工具:如果你只是在程式庫類別中提供一些你想要的方法,使用『帶入外部方法』。如果是一整個完整的額外行為你需要『帶入區域繼承』。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Fri May 31, 2002 8:08 am
Subject: Refactoring讀書筆記4-20
資料類別(Data Class)
[思考:資料類別本身不是甚麼臭味,其臭味是來自其內部的不當設計如資料隱藏或資料保護。資料類別看似沒有特殊作用,但在物件成長的過程中還是負擔一部份的資料管理的責任。]
資料類別中除了資料欄位及存取這些資料的Set及Get方法外別無他物。這種類別一般多是為其他物件操作。(詳長參數列)早期這些類別有一些公開的欄位,在做其他任何動作前先使用『封裝欄位』把資料隱藏起來。如果其中有集合欄位(collection fields)檢查看看是否適當的封裝若否使用『封裝集合』。對於不得改變的欄位使用『移除設定的方法』。
找找看是否有其他類別中常常使用到Set及Get方法,試著將這些行為以『移動方法』移到資料類別中。如果你無法移動一個完整的方法,使用『淬取方法』建立一個可以移動的方法。最後你可以使用『隱藏方法』於這些Set及Get方法。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Mon Jun 3, 2002 9:03 am
Subject: Refactoring讀書筆記4-21
拒絕遺產(Refused Bequest)
[思考:子類別沒有使用到父類別中所有的內含是非常普遍的,所以這個問題不是很大,除非自類別只想使用父類別實作而不想實踐父類別的介面時這才是真正的大問題。]
子類別從父類別中繼承所有的資料及方法。但如果他不想也不需要呢?那他只取其所需。傳統的說法這意味著繼承的層級架構是錯誤的。你需要建立一個新的兄弟(Sibling)類別並使用『方法下移』及『欄位下移』將所有用不到的方法移至兄弟類別。這種方式父類別所擁有的只是共通的部分。所以你常常會聽到「所有的超類別應該是抽象的」的建議。拒絕遺產這個臭味並不是很強烈,並不意味著你一直要很重視這個臭味。只是說如過拒絕遺產這個問題造成困擾或問題時便遵循傳統的建議做重整。
這個臭味造成最大的問題是當子類別繼承行為但不願意支援父類別的介面時。亦即不拒絕實作但拒絕介面。此時別亂動繼承的層級架構,你需要使用『以委託取代層級架構』將他取出。
--------------------------------------------
From: "areca_chen.tw" <areca_chen.tw@y...>
Date: Mon Jun 3, 2002 9:06 am
Subject: Refactoring讀書筆記4-22
註解(Comments)
[思考:註解不是臭味,只是註解指出臭味所在。最重要的是方法的名稱便可以說明這個方法在做甚麼事。也因此你的方法不能太大以致名稱無法充分表達他的意涵。]
這個臭味並不是說你不要寫註解。註解往往是指引出不好的程式碼。當你重整這些不好的程式碼後你會發現註解根本是多餘的。當你想要使用註解說明程式碼做些甚麼事時,試試『淬取方法』。如果淬取後還需要註解說明程式碼做些甚麼事時使用『方法改名』。如果你需要提示一些關於系統所需的狀態時使用『帶入評斷』。
當你覺得需要註解的時候,先嘗試重整程式碼讓任何註解變成多餘的。
使用註解的時機是當你除了註解以外不知道該做甚麼了。額外描述到底發生了甚麼事,註解可以說明哪些是你不確定的。註解應該說明為什麼你這樣做,這個資訊對於往後的維護很有幫助,尤其是易忘的部分。