使用設(shè)計(jì)模式改善程序結(jié)構(gòu)(3)
發(fā)表時(shí)間:2024-01-20 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]設(shè)計(jì)模式在某種程度上確實(shí)能夠改善我們的程序結(jié)構(gòu),使設(shè)計(jì)具有更好的彈性。也正是由于這個(gè)原因,會(huì)導(dǎo)致我們可能過(guò)度的使用它。程序結(jié)構(gòu)具有過(guò)度的、不必要的靈活性和程序結(jié)構(gòu)沒(méi)有靈活性一樣都是有害的。本文將分析過(guò)度的靈活性可能造成的危害,并且結(jié)合一些實(shí)例來(lái)闡述使用設(shè)計(jì)模式改善程序結(jié)構(gòu)應(yīng)遵循的原則。 1、介...
設(shè)計(jì)模式在某種程度上確實(shí)能夠改善我們的程序結(jié)構(gòu),使設(shè)計(jì)具有更好的彈性。也正是由于這個(gè)原因,會(huì)導(dǎo)致我們可能過(guò)度的使用它。程序結(jié)構(gòu)具有過(guò)度的、不必要的靈活性和程序結(jié)構(gòu)沒(méi)有靈活性一樣都是有害的。本文將分析過(guò)度的靈活性可能造成的危害,并且結(jié)合一些實(shí)例來(lái)闡述使用設(shè)計(jì)模式改善程序結(jié)構(gòu)應(yīng)遵循的原則。
1、介紹
本系列文章的前兩篇主要講述了如何使用設(shè)計(jì)模式來(lái)改善我們的程序結(jié)構(gòu),大家可以看到經(jīng)過(guò)調(diào)整的代碼具有了更大的彈性,更容易適應(yīng)變化。讀者朋友可能也具有類(lèi)似的經(jīng)驗(yàn),通過(guò)使用設(shè)計(jì)模式使得自己的軟件系統(tǒng)更加具有可擴(kuò)展性和健壯性。但是,這樣就可能會(huì)造成一個(gè)結(jié)果:無(wú)論遇到任何問(wèn)題,我們首先做的就是設(shè)法找到一個(gè)解決它的設(shè)計(jì)模式來(lái),而不是解決問(wèn)題的最簡(jiǎn)潔的方法。
上面所述的就是過(guò)分使用設(shè)計(jì)模式的情況,它賦予了代碼過(guò)度的靈活性。大家往往對(duì)于僵化、拙劣的設(shè)計(jì)所導(dǎo)致的危害非常清楚,但是對(duì)于過(guò)度靈活的設(shè)計(jì)可能帶來(lái)的危害卻不是很重視。本文試圖從這個(gè)角度來(lái)談?wù)勈褂迷O(shè)計(jì)模式改善程序結(jié)構(gòu)應(yīng)遵循的原則,使大家避免陷入過(guò)分使用設(shè)計(jì)模式的狀況。其中的一個(gè)關(guān)鍵議題就是:我們?yōu)槭裁匆褂迷O(shè)計(jì)模式,到底什么樣的程序結(jié)構(gòu)才是好的。
2、過(guò)分設(shè)計(jì)的危害
正是由于大家對(duì)于僵化的設(shè)計(jì)所造成的結(jié)果的恐懼,以及對(duì)于設(shè)計(jì)模式給我們的程序結(jié)構(gòu)帶來(lái)的無(wú)比的彈性的贊嘆,才會(huì)導(dǎo)致過(guò)分的預(yù)先設(shè)計(jì)(up-front design)。原因很簡(jiǎn)單:需求肯定是要變化的。所以,我們就需要給代碼一些更多的靈活性,使得它可以適應(yīng)以后的變化。于是,我們?cè)谧铋_(kāi)始的設(shè)計(jì)中,就針對(duì)需求的變化做了很多的假設(shè),并把對(duì)于這些假設(shè)的支持放在代碼中。
如果對(duì)這些假設(shè)的預(yù)測(cè)是正確的,那么做的這一切都是值得的。不幸的是,對(duì)這些假設(shè)的預(yù)測(cè)很難是正確的。原因很簡(jiǎn)單:需求是我們的客戶(hù)(一般是另外一個(gè)企業(yè))提出的,但是作為一個(gè)現(xiàn)代的企業(yè),要想生存,就要不斷的改變自己以適應(yīng)日新月異的變化,所以客戶(hù)的需求肯定是要根據(jù)自身生存、發(fā)展的需要而不斷變化的,并且這些變化都是很難預(yù)測(cè)的,常常是客戶(hù)自己都不知道下一步該如何變化(如果都能夠被你預(yù)測(cè)到的話(huà),這個(gè)公司肯定會(huì)高薪聘請(qǐng)你去做他們的CEO)。
如果預(yù)測(cè)是錯(cuò)誤的話(huà),第一個(gè)直接后果就是,浪費(fèi)了寶貴的時(shí)間、資金。我們花了很多的時(shí)間在一些根本沒(méi)有任何用處的靈活性上,而這些時(shí)間本可以用來(lái)為系統(tǒng)增加新的功能或者修正錯(cuò)誤。
過(guò)分靈活的代碼往往更加復(fù)雜、難以理解。其他的開(kāi)發(fā)人員不得不花費(fèi)很多的時(shí)間來(lái)理解這些本來(lái)可以去除的復(fù)雜性。必然導(dǎo)致代碼的維護(hù)、擴(kuò)展困難(如果需求的變化和你的假設(shè)不同),項(xiàng)目的開(kāi)發(fā)效率降低。
例如:發(fā)現(xiàn)一種計(jì)算有多個(gè)不同的方式, 不加思索就直接采用Strategy設(shè)計(jì)模式,而不是采用簡(jiǎn)單、清楚的條件表達(dá)式的方法(if-else語(yǔ)句),那么就會(huì)導(dǎo)致結(jié)構(gòu)的復(fù)雜(要增加好幾個(gè)類(lèi))。如果后來(lái)發(fā)現(xiàn)根本就沒(méi)有在增加新的計(jì)算方法方面的需求,或者更糟糕的是需求的變化是某幾個(gè)計(jì)算策略間要增加依賴(lài)關(guān)系,那么修改起來(lái)就會(huì)十分困難。系統(tǒng)中如果存在太多的這種沒(méi)有必要的靈活性,很可能最終的程序結(jié)構(gòu)就會(huì)陷入冗余、混亂之中。
程序結(jié)構(gòu)的靈活性是有代價(jià)的,這種代價(jià)往往是更多的復(fù)雜性或者造成系統(tǒng)不容易理解,需要我們?cè)谠O(shè)計(jì)時(shí)進(jìn)行權(quán)衡。
3、軟件開(kāi)發(fā)的節(jié)奏
現(xiàn)在在軟件工程領(lǐng)域很活躍的一個(gè)組織是:敏捷社團(tuán),他們提出了一系列的敏捷方法(XP就是其中很著名的一種)。在敏捷方法中制定了一系列的策略、實(shí)踐來(lái)?yè)肀枨蟮淖兓。其目?biāo)是:使軟件以規(guī)范的節(jié)奏進(jìn)展,最終保質(zhì)、按時(shí)交付軟件產(chǎn)品,F(xiàn)在已有很多使用這種方法成功的商業(yè)案例。
對(duì)于軟件開(kāi)發(fā)的節(jié)奏可以描述如下:首先寫(xiě)測(cè)試代碼,提出對(duì)于系統(tǒng)的功能需求,然后寫(xiě)工作代碼滿(mǎn)足這個(gè)需求,重復(fù)這個(gè)過(guò)程直到實(shí)現(xiàn)系統(tǒng)的所有需求。在這個(gè)過(guò)程中間要頻繁的(一般是在增加新功能或者修正錯(cuò)誤時(shí))進(jìn)行重構(gòu),去除冗余的、含糊的代碼,改善程序的結(jié)構(gòu),使得新功能的添加變得容易?梢钥闯鲈谶@樣的軟件開(kāi)發(fā)節(jié)奏中,沒(méi)有對(duì)需求的變化做什么預(yù)測(cè)(進(jìn)行預(yù)測(cè)的主要原因是恐懼變化),而是以一種主動(dòng)的姿態(tài)來(lái)?yè)肀ё兓,?dāng)目前的設(shè)計(jì)不能夠適應(yīng)發(fā)生的變化時(shí),大膽的進(jìn)行重構(gòu)(因?yàn)橛蓄l繁測(cè)試保證,所以重構(gòu)的風(fēng)險(xiǎn)是不大的)去適應(yīng)新的變化。這種方式被稱(chēng)為演化設(shè)計(jì)(evolutionary design),在參考文獻(xiàn)〔3〕中可以得到進(jìn)一步的內(nèi)容。
設(shè)計(jì)模式一般會(huì)成為重構(gòu)的目標(biāo),但是為什么要這樣做?我們一定要重構(gòu)到一個(gè)設(shè)計(jì)模式嗎?怎樣的程序結(jié)構(gòu)才是好的呢?
4、關(guān)鍵是要展現(xiàn)設(shè)計(jì)意圖
現(xiàn)在有一個(gè)普遍的誤解就是:程序結(jié)構(gòu)的靈活性越高越好,所以對(duì)程序結(jié)構(gòu)改善的目標(biāo)就是使它具有更高的彈性,這樣在未來(lái)需求發(fā)生變化時(shí)可以很容易的改變程序來(lái)適應(yīng)這種變化。其實(shí),結(jié)構(gòu)的靈活性和結(jié)構(gòu)的易更改性之間是有矛盾的。很明顯,結(jié)構(gòu)越靈活相應(yīng)的就會(huì)越復(fù)雜,越復(fù)雜就越不容易理解,不容易理解怎么會(huì)容易更改呢?參考文獻(xiàn)〔1〕中專(zhuān)門(mén)探討了這個(gè)問(wèn)題,有興趣可以看看。
正是這個(gè)誤解的存在,使得很多開(kāi)發(fā)者看到了設(shè)計(jì)模式所帶給程序結(jié)構(gòu)的靈活性,從而在進(jìn)行代碼重構(gòu)時(shí)就把結(jié)構(gòu)的靈活性作為一個(gè)最重要的目標(biāo)。最終導(dǎo)致了程序結(jié)構(gòu)的過(guò)分靈活性,損傷了軟件的質(zhì)量。
但是,設(shè)計(jì)模式確實(shí)能夠改善我們的程序結(jié)構(gòu),面向?qū)ο蟠髱烳artin Fowler的經(jīng)典著作《Refactoring Improving the Design of Existing Code》一書(shū)中就有很多的使用設(shè)計(jì)模式進(jìn)行重構(gòu)的例子。難道僅僅是因?yàn)樵O(shè)計(jì)模式能夠帶來(lái)足夠的靈活性嗎?顯然不是!主要是因?yàn)檫@些設(shè)計(jì)模式更能夠展示設(shè)計(jì)者的設(shè)計(jì)意圖,更加便于理解!
很多面向?qū)ο髮?zhuān)家和模式研究專(zhuān)家對(duì)重構(gòu)的動(dòng)機(jī)進(jìn)行了研究,發(fā)現(xiàn)對(duì)于一個(gè)好的重構(gòu)過(guò)程來(lái)說(shuō),重構(gòu)的結(jié)果到底是不是一個(gè)設(shè)計(jì)模式是不重要的。它們的最終動(dòng)機(jī)都是:減少或者消除冗余代碼,簡(jiǎn)化設(shè)計(jì)最終達(dá)到展示真正的設(shè)計(jì)意圖,更加便于交流。
Martin Fowler的《Refactoring》一書(shū)的第一章有一個(gè)關(guān)于影碟出租的例子,詳細(xì)的展示了重構(gòu)的過(guò)程以及每一步的動(dòng)機(jī),很好的說(shuō)明了上面的問(wèn)題。
5、再談設(shè)計(jì)模式的動(dòng)機(jī)
本系列文章的第一篇中談?wù)撛O(shè)計(jì)模式本身的意圖、動(dòng)機(jī)的重要性。這里我想再結(jié)合上面的內(nèi)容重新認(rèn)識(shí)一下這個(gè)問(wèn)題,F(xiàn)在我們有兩個(gè)動(dòng)機(jī)存在,設(shè)計(jì)模式本身的動(dòng)機(jī)以及我們要重構(gòu)來(lái)改善我們的程序結(jié)構(gòu)的動(dòng)機(jī)(可能是要重構(gòu)到一個(gè)設(shè)計(jì)模式)。這兩個(gè)動(dòng)機(jī)其實(shí)是沒(méi)有很大的關(guān)系的。
設(shè)計(jì)模式本身的動(dòng)機(jī)往往是領(lǐng)域無(wú)關(guān)的,但是我們重構(gòu)到設(shè)計(jì)模式的動(dòng)機(jī)卻往往是領(lǐng)域相關(guān)的。因?yàn)槲覀冎貥?gòu)的主要目標(biāo)是要達(dá)到能夠很好的展示我們的設(shè)計(jì)意圖,而這些設(shè)計(jì)意圖往往和問(wèn)題領(lǐng)域的上下文關(guān)系密切。
比如:Factory Method設(shè)計(jì)模式的意圖是定義一個(gè)創(chuàng)建對(duì)象的接口,讓子類(lèi)來(lái)確定到底實(shí)例化哪個(gè)類(lèi),F(xiàn)actory Method方法使得一個(gè)類(lèi)的實(shí)例化時(shí)機(jī)延遲到子類(lèi)中。但是我們?cè)谥貥?gòu)的過(guò)程中何時(shí)決定使用Factory Method設(shè)計(jì)模式呢?基本上是因?yàn)橐粋(gè)類(lèi)的構(gòu)造方式有多種,每一種都有不同的含義,但是構(gòu)造函數(shù)的名字是唯一的,通過(guò)同樣的構(gòu)造函數(shù)名,賦予不同的參數(shù)的方法來(lái)實(shí)例化對(duì)象的方式,使得使用者很難理解這個(gè)構(gòu)造函數(shù)的真正含義。所以通過(guò)使用Factory Method設(shè)計(jì)模式,定義不同的用于展示具體意圖的函數(shù)名稱(chēng)來(lái)實(shí)例化對(duì)象,就更加能夠展示使用者的意圖。另外,該模式還簡(jiǎn)化了該類(lèi)的構(gòu)造方式,封裝了該類(lèi)的實(shí)例化細(xì)節(jié)。
參考文獻(xiàn)〔2〕中有很多這方面的實(shí)例,大家可以下載下來(lái)閱讀一下,相信會(huì)有更大的收獲。
6、結(jié)論
本文結(jié)合設(shè)計(jì)模式探討了在進(jìn)行程序結(jié)構(gòu)改善的過(guò)程中,容易造成的誤解:即過(guò)分的關(guān)注程序結(jié)構(gòu)的靈活性。這樣做很容易從是否能夠給設(shè)計(jì)帶來(lái)靈活性的角度來(lái)進(jìn)行程序結(jié)構(gòu)的重構(gòu),最終可能造成系統(tǒng)的復(fù)雜、混亂、不易理解、難以維護(hù),從而導(dǎo)致項(xiàng)目失敗。其實(shí)一個(gè)好的程序結(jié)構(gòu)根本不在于其中使用了多少設(shè)計(jì)模式、多么具有彈性,主要在于該結(jié)構(gòu)是否能夠非常清晰的展現(xiàn)設(shè)計(jì)者的意圖(可以參考參考文獻(xiàn)〔1〕)。由于在很多情況下,通過(guò)引入設(shè)計(jì)模式可以很好的做到這一點(diǎn),所以設(shè)計(jì)模式往往會(huì)成為重構(gòu)的目標(biāo),但是有一點(diǎn)要記。翰灰^(guò)早的引入設(shè)計(jì)模式,要讓它在重構(gòu)的過(guò)程中自然浮現(xiàn)出來(lái)。
參考資料
[1] To Be Explicit,Martin Fowler, IEEE Software Vol.18 No.6
[2] Refactoring To Patterns, Joshua Kerievsky, industriallogic.com
[3] Is Design Dead, Martin Fowler, www.martinfowler.com