軟件開發(fā)關(guān)于軟件重構(gòu)的靈魂四問(什么是軟件重構(gòu))

      網(wǎng)友投稿 910 2022-05-30

      1. 先添加新功能還是先進行重構(gòu)?

      問題:

      官方資料,重構(gòu)分析1.0版中。

      有兩頂帽子,一個是添加新功能,一個是重構(gòu)

      添加新功能時,你不應(yīng)該修改既有代碼,只管添加新功能,重構(gòu)時你就不能再添加功能,只管改進程序結(jié)構(gòu)。

      一次只做一件事情。

      這兩個是否有矛盾,以哪個為準?前面有些可信材料版本不一,有的還要互相打架,是否可以統(tǒng)一一下?

      回復(fù):

      關(guān)于添加新功能和重構(gòu)是否矛盾的問題,是先添加新功能還是先進行重構(gòu)?

      我們要做的是觀察這兩個事情哪個更容易一些,我們要做更容易的那一個。

      就是你不能一下子同時做這兩件事情。因為同時做兩件事情,會導(dǎo)致你工作的復(fù)雜度提升,容易出錯。

      一般而言,重構(gòu)會改變程序的設(shè)計結(jié)構(gòu)改動相對來說比較大。但是因為沒有功能方面的添加,所以對應(yīng)的測試案例我們不需要進行修改,那對我們來說,只要能夠使得現(xiàn)有的重構(gòu)修改能夠滿足我們的業(yè)務(wù)測試案例就可以了。

      添加新功能意味著我們要添加對應(yīng)的測試案例,以保證我們新的功能是可測的。這部分的修改一般會依托現(xiàn)有的程序結(jié)構(gòu),改動起來相對比較少,并且修改容易鑒別。

      在絕大多數(shù)正常情況下,我們一般是先添加功能,提交完成以后,再新的修改需求中對代碼進行重構(gòu)。

      從大的方向上來說是分兩步走的,這兩個任務(wù)不能混為一談。

      一次只做一件事情,一次提交只包含一個任務(wù),這是為了避免在工作中人為的增加復(fù)雜度,這個復(fù)雜度包含代碼修改,審查,測試等各個方面。

      避免復(fù)雜度的上升,是我們在軟件開發(fā)過程中時刻要謹記的一個原則。

      俗話說,一口吃不成胖子,心急吃不了熱豆腐。做事情要一步一個腳印,穩(wěn)扎穩(wěn)打,步步為營。

      2. 重構(gòu)的價值和評判效果

      問題:

      哪種類型的代碼重構(gòu)是高價值的?

      1. 在網(wǎng)上跑了這么多年也沒啥問題,為什么要動他?

      2. 重構(gòu)前后功能又沒啥變化,當前收益是啥?

      3. 若是提高可維護性,可擴展性的話,怎么評判效果呢?

      回復(fù):

      這是關(guān)于重構(gòu)價值和評判結(jié)果的問題。

      這幾個問題問的都很好。

      我們來看第1個問題,就是"在網(wǎng)上跑了這么多年也沒啥問題,為什么要動"的問題?

      這里的關(guān)鍵點就在于到底有沒有問題。是不是說在客戶那邊客戶看不到問題,就算是沒問題。

      當然不是的,在我們軟件開發(fā)當中,在交付給客戶以后,客戶那邊看到的是黑盒,他不知道我們內(nèi)部的邏輯存在多少的漏洞。

      如果我們的內(nèi)部邏輯存在很多的漏洞。假設(shè)偶然某一天,某個客戶發(fā)現(xiàn)了一個漏洞,它可以通過這一個漏洞進入到我們的系統(tǒng)內(nèi)部,這樣進入我們的內(nèi)部,會發(fā)生什么樣的狀況,我們可以自己想象。

      在公司的內(nèi)部發(fā)言中專門提到了UK對我們產(chǎn)品的一個評價,外層是銅墻鐵壁,內(nèi)層是很脆弱的,客戶或者黑客一旦進入到我們的內(nèi)部以后,他就可以為所欲為了,從這一點上來說,我們一定要對我們現(xiàn)有的代碼進行重構(gòu),以避免這樣的問題。

      我們再來看第2個問題。重構(gòu)前后功能又沒啥變化,當前收益是什么?

      重構(gòu)最大的收益是解決如下的問題:

      代碼太多重復(fù)問題,單個函數(shù)體或者文件或者攻城過大的問題,模塊之間耦合度太高的問題等等。

      以上問題歸根結(jié)底就是一個問題,就是復(fù)雜度過高的問題。

      現(xiàn)在來談一談復(fù)雜度的問題,軟件開發(fā)中的復(fù)雜度當然是越低越好。一般談到復(fù)雜度,我們可能想到了各種邏輯上的復(fù)雜度,設(shè)計上的復(fù)雜度,實際上在軟件過程中復(fù)雜度涉及到方方面面,我們來看一下,具體有哪些方面我們需要注意復(fù)雜度的問題。

      第一是命名規(guī)則。先舉個例子,我定一個變量叫word。有的人喜歡把它寫成wd。這個就增加了這個變量定義的復(fù)雜度,你從wd很難明白,這個變量是word的意思。

      不管是變量的命名還是函數(shù)的命名,我們都希望看到名字,我們應(yīng)該能夠理解這個變量或者函數(shù)大體是關(guān)聯(lián)到什么樣子的事情。

      所以謹慎的使用縮寫是避免命名規(guī)則復(fù)雜度提高的重要前提。

      第二是程序邏輯的復(fù)雜度。線性順序執(zhí)行的復(fù)雜度為1, 出現(xiàn)分支以后要乘以分支的個數(shù)。分支可以是條件判斷也可以是循環(huán)。所以盡可能的避免分支的出現(xiàn)是降低程序邏輯復(fù)雜度的重要手段。

      如果程序分支不可避免,要盡可能的把程序分支放到最高的邏輯層。這樣做的目的是為了避免在下層處理的時候出現(xiàn)發(fā)散式的分支。發(fā)散式的分支會急劇的增加程序的復(fù)雜度。

      復(fù)雜度越高,程序越難維護,復(fù)雜度超過一定程度,人類程序員是無法處理的。

      第三是架構(gòu)設(shè)計的復(fù)雜度。架構(gòu)設(shè)計涉及到模塊設(shè)計和系統(tǒng)設(shè)計。要盡可能的把一些公用的模塊或者子系統(tǒng)抽取出來,比如安全相關(guān)的,日志相關(guān)的,工具相關(guān)的等等,這些公用的功能可能會被所有其他的業(yè)務(wù)模塊或系統(tǒng)所調(diào)用。

      在調(diào)用這些公用功能的時候,越簡單越好,并且調(diào)用者不需要關(guān)心具體的內(nèi)部實現(xiàn),只需要知道如何使用就可以了。

      這樣做的目的是讓程序員專注到業(yè)務(wù)代碼的設(shè)計上來。

      軟件開發(fā)丨關(guān)于軟件重構(gòu)的靈魂四問(什么是軟件重構(gòu))

      第四是系統(tǒng)部署的復(fù)雜度。系統(tǒng)部署包含幾個不同的階段如開發(fā)階段,測試階段和生產(chǎn)階段。不管是哪個階段,部署的步驟越少越不容易出錯。有些系統(tǒng)天然的需要很多指令的配置,如果是這樣的情況,需要編寫一個批處理的文件來簡化外部使用者的部署步驟,把多個步驟變成一步。

      與部署相關(guān)聯(lián)的還有集成部分。如果能夠?qū)崿F(xiàn)自動化或者從模板中創(chuàng)建那是非常好的狀態(tài)。

      第五是測試的復(fù)雜度。測試分白盒測試和黑盒測試。白盒測試的復(fù)雜度直接關(guān)聯(lián)著代碼層級的復(fù)雜度,代碼層級的復(fù)雜度越高,當然白盒測試的復(fù)雜度也就越高。

      白盒測試需要注意的一個重要問題是不要使白盒測試這部分的代碼脫離實際業(yè)務(wù)代碼的設(shè)計。也就是說白盒測試它的依附對象就是我們實際的業(yè)務(wù)代碼,從架構(gòu)設(shè)計上說是一個附屬層,不要試圖在這里使用什么軟件設(shè)計藝術(shù)或者所謂的編程藝術(shù)。

      這種代碼的風格就是簡單直接,復(fù)雜度線性化。

      黑盒測試的復(fù)雜度來自于業(yè)務(wù)需求分析。要有非常清晰的文檔說明,需要對測試步驟和預(yù)期結(jié)果寫的非常清楚。

      第六是技術(shù)的復(fù)雜度。技術(shù)的發(fā)展趨勢一般是越發(fā)展越簡單,功能越強大。那么在設(shè)計和開發(fā)的過程中,要避免使用老舊的技術(shù)。關(guān)于技術(shù)框架的選擇,要提前做好調(diào)研。前端選什么框架,要不要選擇某些UI庫,后端選什么框架,要不要選擇某些程序庫,原則上是為了簡化我們的學(xué)習(xí)過程,提高開發(fā)效率,增強整個項目的可維護性。需要具體問題具體分析。

      第七是隊伍結(jié)構(gòu)的復(fù)雜度。隊伍構(gòu)成一定要短小精悍,人多不一定好辦事。像亞馬遜提倡的是兩張披薩團隊,意思是說整個團隊兩張pizza就能吃飽。大體估算就是10人左右的一個隊伍。當然這只是一個參考指標。

      整個隊伍的目標一定要明確。所有的人都向著那個目標邁進,分工可以不同,但是目標一定要一致。

      目標+分工是隊伍成功運作的關(guān)鍵。具體來說就是把目標分成多個任務(wù),每個任務(wù)里又可以分成小任務(wù),那所有的人都去做對應(yīng)的任務(wù),自己讓自己忙起來,而不是別人讓你忙起來。

      我們現(xiàn)在來看一下第3個問題,就是如何評判重構(gòu)效果的問題。在上面的分析中,我們已經(jīng)了解了重構(gòu)的目標和最大的收益,就是復(fù)雜度的降低。

      那么對應(yīng)的,就是代碼的重復(fù)率大大降低了,單個函數(shù)體或者代碼文件或者工程過大的問題不存在或者減少了,模塊之間的耦合性降低了。

      再進一步說,就是關(guān)于代碼的可維護性和可擴展性上,我們需要關(guān)注這么幾點:

      二是設(shè)計模式的可參考性。設(shè)計模式的好處就是提供一種可以追尋的代碼擴展軌跡,新的功能可以遵循這種軌跡模板進行添加,從而獲得復(fù)雜度線性增長的效果。

      三是白盒測試的完善性。盡管我們有非常強大的測試團隊,對于黑盒測試方面有很多的經(jīng)驗和心得,但是現(xiàn)在我們有很多項目缺乏白盒測試案例,這使得開發(fā)者在進行重構(gòu)的時候,面臨非常尷尬的境地。沒有充分的白盒測試案例,重構(gòu)工作會舉步維艱,有一種瞎子摸象的感覺。

      現(xiàn)在就說一下白盒測試這一部分。測試的框架應(yīng)該在項目開始階段或者重構(gòu)開始前搭起來。等部分代碼成型的時候,逐步的添加必要的測試案例。測試案例的選取可以按照環(huán)形復(fù)雜度的計算方法來確定,也可以根據(jù)集成測試對應(yīng)的用戶需求來確定。

      與代碼相關(guān)的測試,一般有單元測試,集成測試和系統(tǒng)級的測試。

      單元測試,一般被認為非常繁瑣。單元測試的繁瑣主要體現(xiàn)在測試案例的選取上, 如果使用全覆蓋方式來選取測試案例的話,會產(chǎn)生大量的測試代碼,以后維護起來也是一個負擔。如果采用環(huán)形復(fù)雜度來選取測試案例的話,會產(chǎn)生適量的測試代碼,但是環(huán)形復(fù)雜度的計算也是一個很大的時間開銷。

      集成測試跟客戶的實際業(yè)務(wù)需求相關(guān)。在這個過程中需要理清接口的輸入與輸出,以及運行路徑,然后據(jù)此來設(shè)計測試案例,寫出測試案例代碼。

      開發(fā)人員一般不會拒絕寫集成測試。因為她帶來的好處是實實在在的,會極大的提高你的開發(fā)效率和調(diào)試效率。尤其是對于無界面的程序接口尤為重要。

      系統(tǒng)級測試是大系統(tǒng)中子系統(tǒng)之間的集成測試。這個主要包含兩個方面:

      一個方面是有界面的自動化測試,通過這樣的測試架構(gòu)來模擬人類用戶的使用過程,同時增加一些隨機性的行為,試圖能夠找出系統(tǒng)的一些漏洞。

      另一種是無界面的測試,體現(xiàn)在多個服務(wù)系統(tǒng)之間的調(diào)用上或者類似瀏覽器自動化框架的使用上。

      一套完整的測試系統(tǒng),可以幫助工程師提高開發(fā)效率,減少以后系統(tǒng)維護和重構(gòu)的成本。

      從測試的緊迫性上來說,集成測試最為必要,系統(tǒng)間的測試有時候使用手工測試通過一些測試工具來代替。單元測試可以有很廣闊的討論空間,這部分要具體問題具體分析。

      3. 重構(gòu)的時機

      問題:

      關(guān)于重構(gòu)時機的說法,正確的是?

      添加功能時,重構(gòu)能夠使得未來新增特性時更快捷、更流暢

      在修復(fù)錯誤時,應(yīng)該聚焦問題本身,不建議重構(gòu),可以避免引入新的問題

      專家Review時重構(gòu),能夠傳遞經(jīng)驗,改善設(shè)計,避免或減少代碼持續(xù)腐化

      回復(fù):

      關(guān)于重構(gòu)的時機問題,現(xiàn)在我們有三個選項,我們就分別分析一下這三個選項。

      第1個選項是說在添加功能的時候進行重構(gòu)。這個選項的主要問題就是一個提交包含了多個任務(wù)。這屬于人為的增加工作的復(fù)雜度。第1個缺點是會增加工作的難度,使得本來可以用工作量1解決的問題,變成了工作量2和3。第2個缺點是增加了代碼審查的難度。本來你的提交中描述的是添加功能,結(jié)果發(fā)現(xiàn)里面的代碼修改大部分與此描述無關(guān)。

      所以第1個選項排除。

      第2個選項是說在修復(fù)錯誤的時候應(yīng)該聚焦問題本身,不建議重構(gòu),以避免引入新的問題。

      聚焦是點睛之筆。我們在做任何事情的時候,都不要忘記初心,集中精力攻克問題,不要分心。

      所以第2個選項是正確的。

      第3個選項是說專家在審查代碼的時候再重構(gòu)。這里面的最關(guān)鍵問題是專家可能并不了解代碼的業(yè)務(wù)需求和應(yīng)用場景。他們能夠看到代碼存在不好的味道,但在不了解業(yè)務(wù)場景的情況下,讓專家進行重構(gòu)會帶來很大的風險。

      所以第3個選項也不正確。

      4. 如何進行重構(gòu)?

      問題:

      如何正確的進行重構(gòu)?

      回復(fù):

      下面我們來看看如何進行重構(gòu)。

      簡單的代碼重構(gòu)我們都比較熟悉,比如說你通過工具就可以做一些整理,如變量重命名,函數(shù)抽取,類創(chuàng)建等等。

      現(xiàn)在比較頭疼的一個話題就是對老產(chǎn)品的重構(gòu),一些老產(chǎn)品涉及到上千萬行,上億行的代碼。

      關(guān)于老產(chǎn)品整改的問題。如果只是縫縫補補的話,可能起不到化繁為簡的目的。其實做類似這種工作的話,有一個比較可行的方案。就是把現(xiàn)有的產(chǎn)品當做一個成型系統(tǒng)也就是現(xiàn)有運行的產(chǎn)品,不要做大的改動,頂多就是修改bug。

      然后以這些成型的系統(tǒng)為基準,去寫新的系統(tǒng)。相當于參照一個大的白盒就寫一個小的白盒,這樣新的小的白盒質(zhì)量上肯定比大的白盒性能上要有優(yōu)勢。

      這樣子按部就班去做的話,就會比較靠譜。

      有朋友會說上面的做法是重寫,字面意義上沒錯的。

      實際上不矛盾。區(qū)別就是重構(gòu)的方式應(yīng)該從下往上還是從上往下。比如說我們現(xiàn)在大部分的重構(gòu)都理解為從下往上來做。也就是感覺這個文件里頭有壞代碼的味道,然后就改這個文件,這樣做是沒有問題的。

      比如現(xiàn)在有些教練遇到的問題,就是發(fā)現(xiàn)上下文不是很清晰,這個代碼為什么要這么寫?為什么一個文件有1萬行或者3萬行,這個來龍去脈不是很清楚。

      這個時候可能就需要從整個子模塊來進行一個自上而下的分析。梳理出這個子模塊的功能需求是怎樣的,需要有多少個公共接口?內(nèi)部公共接口的實現(xiàn)方式是不是應(yīng)該像目前這樣的?

      一個文件能夠?qū)懗?萬行或者3萬行,肯定是有一定歷史原因的,絕大程度是由于全局把握的編程能力不夠造成的。

      像這種情況,如果從這個文件本身去做重構(gòu)的話,難度非常之大,但是如果從上往下,從模塊的整個設(shè)計角度來做重構(gòu)的話,可能就容易一些。

      對于這樣的龐然大物,最好的辦法就是分而治之。首先要確定系統(tǒng)的功能邏輯點,針對這些邏輯點,要編排好對應(yīng)的檢測點,也就是說等我們完成了重構(gòu)以后,我們得確保我們的重構(gòu)是沒有問題的,這些檢測點就是做這個的,我們可以理解成集成類的測試。

      這些集成類的測試一定要確保可以在當前未重構(gòu)之前的系統(tǒng)上正常運行。

      有了這個設(shè)施以后,我們就可以開展我們的重構(gòu)工作。重構(gòu)的方法有很多,比如采用比較好的工具,函數(shù)和變量的命名改變,調(diào)用方式的改變等等。這些是在現(xiàn)有代碼的基礎(chǔ)上進行的重構(gòu)。這里我們重點說一下重寫的方式來實現(xiàn)重構(gòu)。所謂重寫呢,就是另外開辟一套代碼底座。甚至可以選用不同的編程語言。

      這種情況下重構(gòu)首先要重用已有的業(yè)務(wù)邏輯,實現(xiàn)針對業(yè)務(wù)邏輯集成測試100%的通過率。

      具體不管采用哪種方式都要一個模塊一個模塊的進行推進。驗證完成一個是一個,千萬不能急于求成,試圖一次性的把某些問題搞定。如果出現(xiàn)很多次失敗,有可能會消磨掉你的自信心。所以一定要一點一點的往前推進,始終是在進步當中。采用了這種方式以后,不管當前的系統(tǒng)有多么的龐大,你只要堅持做下去,就一定能夠把重構(gòu)工作徹底完成。

      這個時候需要做的具體步驟可以參考如下:

      1. 根據(jù)功能需求定義公共接口。

      2. 根據(jù)公共接口寫出測試案例代碼。

      3. 這個時候可以按照測試驅(qū)動開發(fā)的理念去填充代碼。

      4. 代碼可以從現(xiàn)有的代碼中抽取出來。

      5. 在抽取的過程中進行整理重構(gòu)。

      這樣,這個子模塊完成以后,就可以嘗試去替代現(xiàn)有的子模塊,看看能不能在整個系統(tǒng)中安全的運行。

      對于整個系統(tǒng)來說,我們又可以分成很多個子模塊。然后又可以對各個子模塊各個擊破,最終完成對整個系統(tǒng)的重構(gòu)。

      如果一開始對整個系統(tǒng)進行重構(gòu)的話,也是可以從自上而下的角度來看的。

      比如說開始的時候先把所有的子模塊看成一些占位符,假定他們已經(jīng)完成他們的接口了。那對于整個系統(tǒng)來說,它本身就是一個子模塊,屬于提綱挈領(lǐng)的那個模塊。

      這個過程,從字面意義上可以理解成重寫,實際上,它也是一個重構(gòu)的過程,因為我們肯定會重用這個系統(tǒng)本身的一些現(xiàn)有代碼和現(xiàn)有的邏輯。

      上面我們是假定系統(tǒng)在已經(jīng)完成的情況下進行的重構(gòu),其實重構(gòu)可以貫穿于軟件開發(fā)的始終。軟件開發(fā)的首要目標是實現(xiàn)業(yè)務(wù)邏輯,能夠解決客戶的問題。這個目標實現(xiàn)以后,我們就要追求代碼的干凈度,復(fù)雜度能夠降到最小,當前的技術(shù)能夠用到最先進。

      所以只要有機會,我們都應(yīng)該對代碼和設(shè)計進行重構(gòu)。

      結(jié)語

      本文針對收到的幾個關(guān)于重構(gòu)方面的問題作了回答,側(cè)重點各不一樣,希望能夠給存在相同困惑的朋友們有所啟示。

      軟件開發(fā)

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:最全Linux的發(fā)行版簡介及比較(linux主要發(fā)行版)
      下一篇:JavaScript權(quán)威Douglas Crockford:代碼閱讀和每個人都該學(xué)的編程(javascript權(quán)威指南第七版 pdf)
      相關(guān)文章
      久久久无码精品亚洲日韩软件| 老司机亚洲精品影院| 久久精品九九亚洲精品| 亚洲日韩国产精品第一页一区| 亚洲综合亚洲综合网成人| 国产精品日本亚洲777| 亚洲AV无码一区二区三区久久精品 | ass亚洲**毛茸茸pics| 亚洲国产综合自在线另类| 久久久久亚洲av无码专区导航| 日韩亚洲人成在线综合日本| 亚洲国产成人片在线观看无码 | 亚洲jjzzjjzz在线播放| 亚洲人成黄网在线观看| 亚洲一区二区三区久久| 亚洲中文字幕日本无线码| 一区二区亚洲精品精华液| 亚洲综合校园春色| 亚洲国产视频久久| 亚洲国产精品自在自线观看| MM1313亚洲国产精品| 亚洲成av人片天堂网老年人| 中文字幕亚洲精品| 亚洲精品自在线拍| 亚洲人成在线中文字幕| 亚洲日韩一区精品射精| 亚洲av无码一区二区三区四区| 色噜噜的亚洲男人的天堂| 亚洲国产人成中文幕一级二级| 久久国产成人精品国产成人亚洲| 亚洲色中文字幕无码AV| 久久精品国产亚洲AV麻豆~| 久久精品a亚洲国产v高清不卡| 亚洲成a人片在线观| 中文日韩亚洲欧美制服| 韩国亚洲伊人久久综合影院| 久久久无码精品亚洲日韩软件| 亚洲AV永久青草无码精品| 亚洲精品永久www忘忧草| 中文字幕亚洲综合久久综合| 亚洲AV日韩AV永久无码色欲|