思維導(dǎo)圖
介紹
前幾篇系列文章,我比較關(guān)注的是<PHP 雜談《重構(gòu)-改善既有代碼的設(shè)計(jì)》之一 重新組織你的函數(shù)>,但是我覺(jué)得我還是沒(méi)有說(shuō)清楚,我自己也有很多不理解的地方,而且這篇是我的第一篇這方面的文章,有很多的紕漏,所以我會(huì)經(jīng)常性的去做修改,如果大家有好的意見(jiàn)不妨告知一、二。
今天談得是“接口”,此接口非“Interface”,而是一個(gè)統(tǒng)稱(chēng)。我們一般可以把供別人使用的函數(shù)或者url(一般是用于提供數(shù)據(jù))叫接口。——可能還有別的意思,畢竟我現(xiàn)在還屬于“菜鳥(niǎo)”,如果有理解上的錯(cuò)誤,請(qǐng)指正。
我們知道“容易被理解和被使用的接口”,是開(kāi)發(fā)良好面向?qū)ο筌浖年P(guān)鍵。——本文將介紹“使接口變得更簡(jiǎn)潔易用”的重構(gòu)手法。
題外話(huà):
如果大家覺(jué)得我這篇文章太長(zhǎng),看起來(lái)麻煩的話(huà),建議大家”就看圖片和粗體的文字“。
昨天,“old“博友給我留言,我以前也沒(méi)仔細(xì)考慮過(guò),這次我也想了想。留言?xún)?nèi)容是:
我個(gè)人覺(jué)得,很多事情只有我們?nèi)リP(guān)注過(guò),才能知道它的價(jià)值。
至于簡(jiǎn)單,重構(gòu)的目地也是為了簡(jiǎn)單和易理解性。
至于執(zhí)著,我覺(jué)得在技術(shù)上,我們很多時(shí)候需要這種執(zhí)著,即使你過(guò)后覺(jué)得你錯(cuò)了,但是我們?cè)谶@之間還是會(huì)有所收獲。我們只有經(jīng)歷過(guò)很多次的磨合(這種磨合有正確的也有錯(cuò)誤的),我們才能知道它的價(jià)值,我們才能收獲到我們需要的東西。
至于利益,”Old“是不是指公司利益,恩,確實(shí)是,很多時(shí)候我們?cè)诰幋a的過(guò)程中,需要趕進(jìn)度,還有我們?cè)谥貥?gòu)中也會(huì)有一些錯(cuò)誤出來(lái),所以我的建議是,在開(kāi)發(fā)之初,你就要在設(shè)計(jì)和重構(gòu)中,不斷進(jìn)行磨合,不要覺(jué)得浪費(fèi)時(shí)間,很多時(shí)候,好的結(jié)構(gòu)能加速你的開(kāi)發(fā)。
專(zhuān)業(yè)術(shù)語(yǔ)
Rename Method
狀況:如果函數(shù)的名稱(chēng)未能揭示函數(shù)的用途,那么修改函數(shù)名稱(chēng)。
動(dòng)機(jī):
我極力提倡的一種編程風(fēng)格就是將復(fù)雜的處理過(guò)程分解成小函數(shù)。但是如果小函數(shù)的命名不好,這會(huì)使你費(fèi)勁周折卻弄不清楚這些小函數(shù)各自的用途。
給函數(shù)命名的一個(gè)好辦法:考慮應(yīng)該給這個(gè)函數(shù)寫(xiě)上一句怎樣的注釋 -——> 想辦法將注釋變成函數(shù)的名稱(chēng)。
起一個(gè)好名稱(chēng)并不容易,需要經(jīng)驗(yàn)。——要想成為一個(gè)真正的編程高手,“起名稱(chēng)”的水平至關(guān)重要。
如果你看到一個(gè)函數(shù)名稱(chēng)不能很好的表達(dá)它的用途,應(yīng)該馬上加以修改。
Example:
Add Parameter
狀況:某個(gè)函數(shù)需要從調(diào)用端得到更多的信息,那么為此函數(shù)添加一個(gè)參數(shù),讓該參數(shù)帶進(jìn)函數(shù)所需信息。
動(dòng)機(jī):
1、Add Parameter 是一個(gè)很常用的重構(gòu)手法。
2、修改過(guò)的函數(shù)需要一些過(guò)去沒(méi)有的信息,因此你需要給函數(shù)添加一個(gè)參數(shù)。
3、除了Add Parameter外,只要有可能,其他選擇都比“Add Parameter”要好,因?yàn)橛锌赡芷渌x擇不會(huì)增加參數(shù)列的長(zhǎng)度。——過(guò)長(zhǎng)的參數(shù)列會(huì)使程序員記不住那么多參數(shù)。
Remove Parameter
狀況:函數(shù)本體不再需要某個(gè)參數(shù),那么將該參數(shù)去除。
動(dòng)機(jī):
1、參數(shù)指出函數(shù)信息,不同參數(shù)代表不同意義。函數(shù)調(diào)用這必須為每一個(gè)參數(shù)操心該傳什么東西進(jìn)去。——如果不去掉參數(shù),那就為每一次調(diào)用多費(fèi)一份心。
2、如果你發(fā)現(xiàn)有很多調(diào)用者,那么為了不讓調(diào)用者操心,你可以這樣做,把要移除的參數(shù)設(shè)置為某個(gè)默認(rèn)值(如null),這樣調(diào)用者只傳那些沒(méi)有默認(rèn)值的參數(shù)。
Separate Query from Modifier
狀況:如果某個(gè)函數(shù)既返回對(duì)象的狀態(tài)值,又修改(副作用)對(duì)象狀態(tài)(state),那么建立兩個(gè)不同的函數(shù),其中一個(gè)負(fù)責(zé)查詢(xún),另一個(gè)負(fù)責(zé)修改。
Example:
Parameterize Method
狀況:如果若干函數(shù)做了類(lèi)似的工作,但在函數(shù)本體中包含了不同的值,那么建立單一函數(shù),以參數(shù)表達(dá)那些不同的值。
動(dòng)機(jī):
1、一般是因?yàn)橛猩贁?shù)幾個(gè)值不同,所以建立了幾個(gè)相似的函數(shù)。
2、分離的函數(shù)替換為一個(gè)統(tǒng)一的函數(shù),通過(guò)參數(shù)來(lái)處理那些變化情況,以簡(jiǎn)化問(wèn)題。
3、去除重復(fù)的代碼,提高靈活性。——可以使用這個(gè)參數(shù)處理其他變化情況。
Example:
Replace Parameter with Explicit Methods
狀況:你有一個(gè)函數(shù),其內(nèi)完全取決于參數(shù)值而采取不同的反應(yīng),那么針對(duì)該參數(shù)的每個(gè)值,建立一個(gè)獨(dú)立的函數(shù)。
動(dòng)機(jī):
1、如果某個(gè)參數(shù)有離散值,而函數(shù)內(nèi)又以條件式檢查這些參數(shù)值,并根據(jù)不同的參數(shù)值做出不同的反應(yīng),那么就應(yīng)該使用本次重構(gòu)。
2、可以獲得好處:“編譯期代碼檢查”,“接口更清楚”(如果用參數(shù)值決定函數(shù)行為,那么函數(shù)用戶(hù)不但需要觀察該函數(shù),而且還要判斷參數(shù)是否“合法化”。——而合法的參數(shù),很少在文檔中提到,必須通過(guò)上下文,才能判斷)
3、不考慮“編譯期檢驗(yàn)”的好處,為了獲取一個(gè)清晰的接口,我們也值得這么做。
Example:
Preserve Whole Object
狀況:如果你從某個(gè)對(duì)象中取出若干值,將它們作為某一次函數(shù)調(diào)用中的參數(shù),那么改使用(傳遞)整個(gè)對(duì)象。
動(dòng)機(jī):
1、參數(shù)列更穩(wěn)固;
2、提高代碼的可讀性;——過(guò)長(zhǎng)的參數(shù)列很難使用,因?yàn)檎{(diào)用者和被調(diào)用者都必須記住這些參數(shù)的用途。
Example:
Replace Parameter with Methods
狀況:如果對(duì)象調(diào)用某個(gè)函數(shù),并將所得結(jié)果做為參數(shù),傳遞給另一個(gè)函數(shù)(接受參數(shù)的函數(shù)也有調(diào)用前一個(gè)函數(shù)的能力),那么讓參數(shù)接受者去除該項(xiàng)參數(shù),并直接調(diào)用前一個(gè)函數(shù)。
動(dòng)機(jī):
1、如果函數(shù)通過(guò)其他途徑獲得參數(shù)值,那么它就不應(yīng)該通過(guò)參數(shù)取得該值。
2、過(guò)長(zhǎng)的參數(shù)列會(huì)增加程序閱讀者的理解難度,因此我們應(yīng)該盡可能的縮短參數(shù)列的長(zhǎng)度。
3、方法:看看“參數(shù)接受端”是否可以通過(guò)“與調(diào)用端相同的計(jì)算”來(lái)取得參數(shù)攜帶值。
4、如果函數(shù)調(diào)用端通過(guò)對(duì)象內(nèi)部的另一個(gè)函數(shù)來(lái)計(jì)算參數(shù),并在計(jì)算過(guò)程中“未曾引用調(diào)用端的其他參數(shù)”,那么就可以將這個(gè)計(jì)算過(guò)程轉(zhuǎn)移到被調(diào)用端內(nèi),從而去除該項(xiàng)參數(shù)。
Example:
Introduce Parameter Object
狀況:某些參數(shù)總是很自然地同時(shí)出現(xiàn),那么以一個(gè)對(duì)象取代這些參數(shù)。
動(dòng)機(jī):
1、一組參數(shù)可能有幾個(gè)函數(shù)同時(shí)使用,這些函數(shù)可能隸屬于同一個(gè)class,也可能隸屬于不同的classes。——這樣的一組參數(shù)就是所謂的Data Clump(數(shù)據(jù)泥團(tuán))。
2、我們可以運(yùn)用一個(gè)對(duì)象包裝所有這些數(shù)據(jù),再以對(duì)象取代Data Clump。——目地:哪怕只是為了把這些數(shù)據(jù)組織在一起,這樣做也是值得的。
3、本項(xiàng)重構(gòu)的價(jià)值在于“縮短了參數(shù)列的長(zhǎng)度”。此外,新對(duì)象所定義的訪(fǎng)問(wèn)函數(shù)(accessors)還可以使代碼更具一致性。——這又進(jìn)一步降低了代碼的理解難度和修改難度。
4、本項(xiàng)重構(gòu)還可以帶給你更多好處。——當(dāng)你把這些參數(shù)組織到一起之后,往往很快可以發(fā)現(xiàn)“可被移植新建class“的行為。——減少重復(fù)代碼。
Example:
Remove Setting Method
狀況:你的class中的某個(gè)值域,應(yīng)該在對(duì)象初創(chuàng)時(shí)被設(shè)置,然后就不再改變,那么去掉該值域的所有設(shè)置函數(shù)(setter)。
動(dòng)機(jī):
1、如果你為某個(gè)值域提供了設(shè)置函數(shù)(setter),這就暗示了這個(gè)值域可以被改變。
2、如果你不希望在對(duì)象初創(chuàng)之后,此值域還有機(jī)會(huì)改變,那就不要為它提供設(shè)置函數(shù)。——這樣你的意圖會(huì)更加清晰,并且可以排除其值被修改的可能性。
Example:
Hide Method
狀況:如有有一個(gè)函數(shù),從來(lái)沒(méi)有被其他class用到,那么將這個(gè)函數(shù)設(shè)置為private。
動(dòng)機(jī):
1、重構(gòu)往往促使你修改“函數(shù)的可見(jiàn)度“。——時(shí)刻檢查可被隱藏的函數(shù)。
2、經(jīng)常檢查有沒(méi)有可能降低某個(gè)函數(shù)的可見(jiàn)度(使它私有化)。
——>當(dāng)你在另一個(gè)class中移除對(duì)某個(gè)函數(shù)的調(diào)用時(shí),就應(yīng)該檢查。
——>特別對(duì)setter函數(shù)進(jìn)行上述的檢查。
Replace Constructor with Factory Method
狀況:如果你希望在創(chuàng)建對(duì)象時(shí)不僅僅是對(duì)它做簡(jiǎn)單的構(gòu)件動(dòng)作,那么將__construct(構(gòu)造函數(shù))替換為factory method。
動(dòng)機(jī):
在subclass過(guò)程中以factory method取代type code。——你可能常常需要type code創(chuàng)建相應(yīng)的對(duì)象。
Example:
接著來(lái):
Replace Error Code with Exception
狀況:如果某個(gè)函數(shù)返回一個(gè)特定的代碼(special code),用以表示某種錯(cuò)誤情況,那么改用異常(Exception)。
動(dòng)機(jī):
清楚的將”普通程序“和”錯(cuò)誤處理“分開(kāi),這使的程序更容易”理解“。
Example:
conclusion
把我每一次的收獲與大家分享,如果大家有那么一丁點(diǎn)的收獲,也讓我高興不已。還有如在文章中有錯(cuò)誤,望請(qǐng)指點(diǎn)一、二。
我不知道是不是找錯(cuò)地方了,有博友留言說(shuō)“博客園里主要盛行C#”,看得人是不是主要以PHP程序員為主?還有很少有人給我留言,也很少有人指出我文章中的錯(cuò)誤(難道我的文章中真的沒(méi)有錯(cuò)誤嗎?),昨天”@四眼蒙面?zhèn)b“給我留了言,我在與他的交談中收獲甚多,也感謝的他的批評(píng)指正,也希望能跟大家多交流。