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

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