之前寫了個函數的重構,這裡寫的是函數的調用的重構,不同哦,是為了寫出讓別人好調用的函數(或接口)。
1、函數改名
修改點:函數的名稱未能提示函數的用途。
做法:修改函數的名稱
如果你想給函數寫上一句什麼注釋,那麼你就把這個注釋想辦法作為名稱寫上好了。
Martin原話:
你可能無法第一次就取一個好名字,這個時候你就會想,就這麼將就著吧,畢竟這只是一個名稱。
當心,這是惡魔的召喚,是通向混亂之路,千萬不要被它誘惑。
(我就無數次被誘惑,然後取了很多渣名,因為想一個好名字真是太難了,除非我把函數名寫很長)
2、添加參數
修改點:某個函數需要從調用端得到更多信息
做法:為此函數添加一個對象參數,讓該對象帶進函數所有信息。
3、移除參數(好吧,相比第二點,很多人可能會嫌麻煩不去搞這個,惡魔的誘惑哦)
修改點:函數本體不再需要某個參數
做法:將該參數去除
4、將查詢函數和修改函數分離(所以直接用屬性就好了啊)
修改點:某個函數既返回對象狀態值,又修改對象狀態
做法:建立兩個不同的函數,其中一個負責查詢,又負責修改
存在例外哦,就是並發場景下同時查詢與修改的操作,那麼你仍應該分離,但是單獨寫一個函數去同時進行這兩個事情。
5、令函數攜帶參數
修改點:若干函數做了類似工作,但在函數本體中卻包含不同的值
做法:建立單一函數,以參數表達那些不同的值
簡單來說,就是兩個函數有很多相同部分,就幾個值不同,你把這幾個值作為函數參數,那麼就可以把兩個函數合二為一
6、以明確函數替代參數(與5相反哦)
修改點:你有一個函數,其中完全取決於參數值而采用不同行為(看好是完全)
做法:針對該參數的每一個可能值,建立一個獨立函數
意思就是說你根據參數的判斷而采取不同的行為,那麼你完全可以分成幾個函數來實現。
而如果影響並不是很大,用5就好了,如果確實需要條件判斷,那麼可以考慮使用多態來消除條件判斷
7、保持對象完整
修改點:你從某個對象中去除若干值,將它們作為某一次函數調用時的參數
做法:改為傳遞整個對象
動機:萬一將來函數需要新的數據項,你就必須查找並修改對此函數的所有調用。而且這樣也能提高整個代碼的可讀性。
但是也有例外:如果你穿的是數值,那麼函數就僅僅依賴於數值,但是如果是對象,那麼依賴的就是整個對象,這有可能會造成你的結構惡化,所以你得具體情況具體分析。
不過如果一個函數使用了另外一個對象很多的值,那麼你可能需要考慮是不是需要把這個函數放在那個對象所屬的類裡面了。
8、以函數取代參數
修改點:對象調用某個函數,並將所有結果作為參數,傳遞給另外一個函數
做法:讓參數接受者去除該項參數,並直接調用前一個函數
動機:如果函數可以通過其它途徑獲取參數值,那麼它就不應該通過參數取得該值。過長的參數列會增加程序員的理解難度,因此應該盡可能縮短參數列的長度。
9、引入參數對象
修改點:某些參數總是很自然地同時出現
做法:以一個對象取代這些參數
之前在何處重構裡已經講過了,其它的章節貌似也涉及到了。這種類其實也有其他的好處,比如你建立了這樣一個類,說不定有些函數就可以放到這個類裡,讓代碼結構更清晰。
10、移除設置函數
修改點:類中的某個字段應該在對象創建時被設值,然後就不再改變
做法:去掉該字段的所有設置函數
意思就是說如果初始化的時候就不需要改變某個字段,那麼你就不要去添加設置函數,這樣會讓意圖不明確。
11、隱藏函數
修改點:有一個函數,從來沒有被其他任何類用到
做法:將這個函數修改為private
還是這個意思,讓調用者看到他們應該看到的,和想看到的,不要暴露給他們過多的信息,越簡單越好,他們用起來越方便,也有利於安全性。
12、以工廠函數替代構造函數
修改點:你希望在創建對象時不僅僅是做簡單的建構工作。
做法:將構造函數替換為工廠函數
很簡單,在數據的重構裡第14點我的例子就是這樣的。
public class Room { public Room() { } public static Room CreateRoom() { return new Room(); } }
這個東西用來多態去除類型碼,去除過多的條件表達式特別有效
也就是在CreateRoom裡用來判斷Room類型來創建Room的不同子類,當然在類型較少的情況下,你也可以加多個工廠函數,分別創建不同的子類
13、封裝向下轉型
修改點:某個函數返回的對象,需要由函數調用者執行向下轉型。
做法:將向下轉型動作移到函數中
簡單例子:
public class Room { public Room() { } public Object GetRandRoom() { return RoomList.GetRandRoom(new Random().Next()); } }
應該轉為
public class Room { public Room() { } public Room GetRandRoom() { return (Room)RoomList.GetRandRoom(new Random().Next()); } }
也就是說有些系統函數或者別人寫的函數經過處理後返回一個object對象,但是你知道這個object對象是Room類型,那麼請把它轉化為Room類型,也就是說盡量在更底層就轉型,而不是在調用端轉型,這個增加調用者的理解難度。
這種情況可能會在返回迭代器或者集合的函數身上發生
不過貌似我們.NET幾乎不會出現這種情況,起碼我遇到的代碼就沒見過,是我見過的代碼太少呢,還是說大家都很厲害的樣子,避開了這個坑。
14、以異常取代錯誤碼
修改點:某個函數返回一個特定的代碼,用以表示某種錯誤情況
做法:改為拋異常
好吧這個應該不用我來說了吧,不過很多人(包括我)都習慣返回錯誤碼,因為簡單,大家共勉,以圖改進吧。
順便提一點,對.NET的錯誤機制,也就是try catch finally throw這幾個東西用到時候要小心點,這個東西存在很多的爭議,就是你拋出的異常應該是可控的異常,而不應該是不知道是什麼鬼的異常,如果是不知道是什麼鬼的異常就應該立馬解決了,而不是吞掉。
15、以條件判斷取代異常
修改點:面對一個調用者可以預先檢查的條件,你拋出一個異常
做法:修改調用者,使它在調用函數之前先做檢查。
好吧,這個東西也就是消除異常,也就是說比如這個異常你可以提前預料到,你就應該去直接用個某種手段去檢查,檢查不通過就返回你catch後執行的操作,而不是不去檢查,直接try catch。坦白說,其實try catch裡的代碼越少越好,因為找到異常後定位異常也需要資源。