很好,我們終于邁出了重構的第一步,而這第一步我們瞄準了代碼問題的重災區——超級大函數。超級大函數之所以是代碼問題的重災區,就是因為它們往往難于閱讀、難于維護。面對大函數我們采取的辦法是拆分,以功能為核心將其拆分成一個一個獨立的函數。拆分后的程序變得易于閱讀了,因為要讀懂程序你不再需要讀完所有代碼,選擇性的讀取那些頂級函數,只需了了數行代碼,你就可以明白整個程序。
但是,當我們將數千行的大函數分解成數十個小函數時,另一個問題出現了。想象一下,數十個函數被雜亂無章地堆放在一個對象中,看看就讓人頭疼。實際上,我們是不會這樣做的。當我們開始了對大函數的分解時,隨之而來的就是對大對象的分解。大對象,就是指的那些包含數十個甚至上百個方法或者函數,功能無所不包的超級對象。在很多遺留系統中,總有那么幾個超級對象,系統幾乎所有的功能都在它的里面有對應方法。這樣的對象,密密麻麻的方法讓人困惑,更關鍵的是,各種各樣的功能被耦合在一起,稍有修改就會影響到許多功能,甚至讓那些毫不相干的功能產生BUG。因此,我們應當合理地拆分我們的大對象。
與大函數一樣,很多時候遺留系統中的大對象,也都是伴隨系統業務復雜度的逐漸增長而出現的,我們來看看它的演進過程吧。我們說軟件實際上是對現實世界的模擬,通過這種模擬,實現信息化的管理,來提高我們的生產效率。但是,現實世界是復雜的,各種事物之間存在著各種各樣紛繁復雜的聯系,因此我們不可能完全模擬現實世界的所有,只可能是現實世界的一部分,客戶急需要模擬的那一部分。
人的大腦認識事物總是一個由簡單到復雜的過程,這是我們的客觀規律。因此,我們的軟件模擬真實世界也是一個由簡單到復雜的過程。最開初我們的想法總是非常簡單而單純的,就是讓軟件做一件非常簡單而明確的事情。由于這時候業務非常簡單,我們不需要太多的類和方法就可以實現業務操作。比如開票業務,就是將已開具的發票信息讀取出來,保存。這樣一個簡單操作,設計成一個簡單的開票業務類合情合理。
但是,隨著軟件模擬真實世界的不斷發展,業務變得越來越復雜。比如這個開票業務,我們隨后的業務開始變更,要檢查購方是否存在、開票人是否有權限、庫存中是否還有發票,等等。起初只有一種開票方式,但隨著非正常開票業務的增加,許多相關的業務也隨之變化……隨著業務的不斷增加,軟件代碼的規模也在發生著質的變化(如圖6.1所示)。
過去開票業務類只有百來行代碼,現在被膨脹到數千行代碼。各種條件語句層層嵌套,各種臨時變量穿插跑位,程序變得難于理解。由于讀不懂代碼,修改代碼的程序員開始在走鋼絲,一不小心改動了某個關鍵程序就可能引入重大BUG。為了避免重大BUG的出現,測試人員耗費巨大精力進行嚴格的測試。毫無疑問,軟件開始進入一種惡性循環,軟件退化開始一步步加深。
面對這種軟件規模增大而帶來的惡性循環,我們必須做出改變。面對問題我們不能病急亂投醫,而是應當對癥下藥。這個正確的藥方就是以職責驅動設計思想為核心,調整我們的程序結構,構建高內聚、低耦合的軟件系統。職責驅動設計,就是要求我們設計的所有類和接口,都要有自己的職責定義。而每個類和接口內部的所有方法和屬性都是圍繞著該職責來進行的,它們都是高度相關的。每個類和接口決不去做跟自己職責無關的事情,所有與自己職責無關的事情,都應當交給其它擁有該職責的類來完成,而自己僅僅是去調用。這就是職責驅動設計的思想,而每個類其內部包含的功能所達到的高度相關的程度,我們稱之為“內聚”。
概念似乎有一些抽象,我們來舉例說明吧。對于開票業務,我們設計了開票業務類來處理它,因此開票業務類的職責就是完成開票操作,這似乎毫無問題。但是,我們仔細審視開票操作,就會發現它包含了好幾個部分:首先,我們讀取客戶、開票人、發票庫存等信息進行相關的校驗,然后保存這些發票到數據庫中,最后統計當月的票量及金額。通過這樣的分析,它們似乎不再那么功能相關了,讀取和校驗客戶、開票人、發票庫存等信息是客戶、開票人、發票庫存實體類的職責,讀取和保存發票似乎是發票類的職責,而統計當月票量與金額似乎是財會統計類的職責。分與不分,完全取決于軟件代碼的復雜程度。如果總共也就幾十行代碼,我們寫成一個類中的幾個方法就可以了;但隨著功能復雜度的加深,那么我們必須得拆分,分配到不同的類中配合完成我們的功能。
隨著軟件業務的不斷變化,我們的軟件在發生著質的變化。發票保存前我們必須要進行一系列的校驗工作:檢查購方是否存在、開票人是否有權限、是否還有發票庫存,等等。不同的校驗,讀取的是不同的數據,它們的順序可能變化,校驗的個數也可能在調整。隨著需求的變化,一些校驗被增加進來而另一些則被剔除。我們判斷功能是否相關的一個非常重要的原則,就是是否是軟件變更的同一個原因。比如,“檢查購方是否存在”與“開票人是否有權限”,不是軟件變更的同一個原因:“檢查購方是否存在”是與客戶信息管理直接相關,而“開票人是否有權限”則是與用戶權限定義密切相關,因此它們不能放在同一個類中。為什么呢?因為“檢查購方是否存在”的業務邏輯變更時,不應當影響到“檢查開票人是否有權限”的功能。最好的辦法就是,將它們各自封裝在各種相關的業務類中(如圖6.2所示)。
經過以上分析我們發現,開票操作隨著業務邏輯的不斷發展,應當在原有的程序結構上,將開票業務類拆分成幾個部分:各種校驗類、發票業務類與財會統計類。這樣的拆分使得開票業務類最終由一個什么都干的多面手,變成了一個管理者。它不再參與那些具體的工作,而是將工作分配給不同的人,成為一個組織協調者。經過這樣的調整,我們的程序將變得更加易于閱讀、維護、變更。
大話重構連載首頁: http://fangang.iteye.com/blog/2081995
特別說明:希望網友們在轉載本文時,應當注明作者或出處,以示對作者的尊重,謝謝!
但是,當我們將數千行的大函數分解成數十個小函數時,另一個問題出現了。想象一下,數十個函數被雜亂無章地堆放在一個對象中,看看就讓人頭疼。實際上,我們是不會這樣做的。當我們開始了對大函數的分解時,隨之而來的就是對大對象的分解。大對象,就是指的那些包含數十個甚至上百個方法或者函數,功能無所不包的超級對象。在很多遺留系統中,總有那么幾個超級對象,系統幾乎所有的功能都在它的里面有對應方法。這樣的對象,密密麻麻的方法讓人困惑,更關鍵的是,各種各樣的功能被耦合在一起,稍有修改就會影響到許多功能,甚至讓那些毫不相干的功能產生BUG。因此,我們應當合理地拆分我們的大對象。
與大函數一樣,很多時候遺留系統中的大對象,也都是伴隨系統業務復雜度的逐漸增長而出現的,我們來看看它的演進過程吧。我們說軟件實際上是對現實世界的模擬,通過這種模擬,實現信息化的管理,來提高我們的生產效率。但是,現實世界是復雜的,各種事物之間存在著各種各樣紛繁復雜的聯系,因此我們不可能完全模擬現實世界的所有,只可能是現實世界的一部分,客戶急需要模擬的那一部分。
人的大腦認識事物總是一個由簡單到復雜的過程,這是我們的客觀規律。因此,我們的軟件模擬真實世界也是一個由簡單到復雜的過程。最開初我們的想法總是非常簡單而單純的,就是讓軟件做一件非常簡單而明確的事情。由于這時候業務非常簡單,我們不需要太多的類和方法就可以實現業務操作。比如開票業務,就是將已開具的發票信息讀取出來,保存。這樣一個簡單操作,設計成一個簡單的開票業務類合情合理。
但是,隨著軟件模擬真實世界的不斷發展,業務變得越來越復雜。比如這個開票業務,我們隨后的業務開始變更,要檢查購方是否存在、開票人是否有權限、庫存中是否還有發票,等等。起初只有一種開票方式,但隨著非正常開票業務的增加,許多相關的業務也隨之變化……隨著業務的不斷增加,軟件代碼的規模也在發生著質的變化(如圖6.1所示)。
圖6.1 開票業務的演化過程
過去開票業務類只有百來行代碼,現在被膨脹到數千行代碼。各種條件語句層層嵌套,各種臨時變量穿插跑位,程序變得難于理解。由于讀不懂代碼,修改代碼的程序員開始在走鋼絲,一不小心改動了某個關鍵程序就可能引入重大BUG。為了避免重大BUG的出現,測試人員耗費巨大精力進行嚴格的測試。毫無疑問,軟件開始進入一種惡性循環,軟件退化開始一步步加深。
面對這種軟件規模增大而帶來的惡性循環,我們必須做出改變。面對問題我們不能病急亂投醫,而是應當對癥下藥。這個正確的藥方就是以職責驅動設計思想為核心,調整我們的程序結構,構建高內聚、低耦合的軟件系統。職責驅動設計,就是要求我們設計的所有類和接口,都要有自己的職責定義。而每個類和接口內部的所有方法和屬性都是圍繞著該職責來進行的,它們都是高度相關的。每個類和接口決不去做跟自己職責無關的事情,所有與自己職責無關的事情,都應當交給其它擁有該職責的類來完成,而自己僅僅是去調用。這就是職責驅動設計的思想,而每個類其內部包含的功能所達到的高度相關的程度,我們稱之為“內聚”。
概念似乎有一些抽象,我們來舉例說明吧。對于開票業務,我們設計了開票業務類來處理它,因此開票業務類的職責就是完成開票操作,這似乎毫無問題。但是,我們仔細審視開票操作,就會發現它包含了好幾個部分:首先,我們讀取客戶、開票人、發票庫存等信息進行相關的校驗,然后保存這些發票到數據庫中,最后統計當月的票量及金額。通過這樣的分析,它們似乎不再那么功能相關了,讀取和校驗客戶、開票人、發票庫存等信息是客戶、開票人、發票庫存實體類的職責,讀取和保存發票似乎是發票類的職責,而統計當月票量與金額似乎是財會統計類的職責。分與不分,完全取決于軟件代碼的復雜程度。如果總共也就幾十行代碼,我們寫成一個類中的幾個方法就可以了;但隨著功能復雜度的加深,那么我們必須得拆分,分配到不同的類中配合完成我們的功能。
隨著軟件業務的不斷變化,我們的軟件在發生著質的變化。發票保存前我們必須要進行一系列的校驗工作:檢查購方是否存在、開票人是否有權限、是否還有發票庫存,等等。不同的校驗,讀取的是不同的數據,它們的順序可能變化,校驗的個數也可能在調整。隨著需求的變化,一些校驗被增加進來而另一些則被剔除。我們判斷功能是否相關的一個非常重要的原則,就是是否是軟件變更的同一個原因。比如,“檢查購方是否存在”與“開票人是否有權限”,不是軟件變更的同一個原因:“檢查購方是否存在”是與客戶信息管理直接相關,而“開票人是否有權限”則是與用戶權限定義密切相關,因此它們不能放在同一個類中。為什么呢?因為“檢查購方是否存在”的業務邏輯變更時,不應當影響到“檢查開票人是否有權限”的功能。最好的辦法就是,將它們各自封裝在各種相關的業務類中(如圖6.2所示)。
圖6.2 開票業務的拆分
經過以上分析我們發現,開票操作隨著業務邏輯的不斷發展,應當在原有的程序結構上,將開票業務類拆分成幾個部分:各種校驗類、發票業務類與財會統計類。這樣的拆分使得開票業務類最終由一個什么都干的多面手,變成了一個管理者。它不再參與那些具體的工作,而是將工作分配給不同的人,成為一個組織協調者。經過這樣的調整,我們的程序將變得更加易于閱讀、維護、變更。
大話重構連載首頁: http://fangang.iteye.com/blog/2081995
特別說明:希望網友們在轉載本文時,應當注明作者或出處,以示對作者的尊重,謝謝!
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
