好的軟件能夠適應(yīng)變化。它提供新的特性,適應(yīng)到新的平臺(tái),滿足新的需求,處理新的輸入。
新的函數(shù)將被加入到函數(shù)庫(kù)中,新的重載將發(fā)生,于是要注意那些含糊的函數(shù)調(diào)用行為的結(jié)果;新的類將會(huì)加入繼承層次,現(xiàn)在的派生類將會(huì)是以后的基類,并已為此作好準(zhǔn)備;將會(huì)編制新的應(yīng)用軟件,函數(shù)將在新的運(yùn)行環(huán)境下被調(diào)用,它們應(yīng)該被寫(xiě)得在新平臺(tái)上運(yùn)行正確;程序的維護(hù)人員通常不是原來(lái)編寫(xiě)它們的人,因此應(yīng)該被設(shè)計(jì)得易于被別人理解、維護(hù)和擴(kuò)充。
這么做的一種方法是:用C++語(yǔ)言自己來(lái)表達(dá)設(shè)計(jì)上的約束條件,而不是用注釋或文檔。例如,如果一個(gè)類被設(shè)計(jì)得不會(huì)被繼承,不要只是在其頭文件中加個(gè)注釋,用C++的方法來(lái)阻止繼承。
如果一個(gè)類需要其實(shí)例全部創(chuàng)建在堆中,不要只是對(duì)用戶說(shuō)了這么一句,用以前介紹過(guò)的方法來(lái)強(qiáng)迫這一點(diǎn)。
如果拷貝構(gòu)造和賦值對(duì)一個(gè)類是沒(méi)有意義的,通過(guò)申明它們?yōu)樗接衼?lái)阻止這些操作。
應(yīng)該判斷一個(gè)函數(shù)的含意,以及它被派生類重定義的話是否有意義。如果是有意義的,申明它為虛,即使沒(méi)有人立即重定義它。如果不是的話,申明它為非虛,并且不要在以后為了便于某人而更改;確保更改是對(duì)整個(gè)類的運(yùn)行環(huán)境和類所表示的抽象是有意義的。
處理每個(gè)類的賦值和拷貝構(gòu)造函數(shù),即使“從沒(méi)人這樣做過(guò)”。他們現(xiàn)在沒(méi)有這么做并不意味著他們以后不這么做。如果這些函數(shù)是難以實(shí)現(xiàn)的,那么申明它們?yōu)樗接小_@樣,不會(huì)有人誤調(diào)編譯器提供的默認(rèn)版本而做錯(cuò)事(這在默認(rèn)賦值和拷貝構(gòu)造函數(shù)上經(jīng)常發(fā)生)。
基于最小驚訝法則:努力提供這樣的類,它們的操作和函數(shù)有自然的語(yǔ)法和直觀的語(yǔ)義。和內(nèi)建數(shù)據(jù)類型的行為保持一致:拿不定主意時(shí),仿照int來(lái)做。
要承認(rèn):只要是能被人做的,就有人這么做(WQ:莫菲法則)。他們會(huì)拋異常;會(huì)用自己給自己賦值;在沒(méi)有賦初值前就使用對(duì)象;給對(duì)象賦了值而沒(méi)有使用;會(huì)賦過(guò)大的值、過(guò)小的值或空值。一般而言,只要能編譯通過(guò),就有人會(huì)這么做。所以,要使得自己的類易于被正確使用而難以誤用。要承認(rèn)用戶可能犯錯(cuò)誤,所以要將你的類設(shè)計(jì)得可以防止、檢測(cè)或修正這些錯(cuò)誤。
努力于可移植的代碼。寫(xiě)可移植的代碼并不比不可移植的代碼難太多,只有在性能極其重要時(shí)采用不可移植的結(jié)構(gòu)才是可取的。即使是為特定的硬件設(shè)計(jì)的程序也經(jīng)常被移植,因?yàn)檫@些平臺(tái)在幾年內(nèi)就會(huì)有一個(gè)數(shù)量級(jí)的性能提升。可移植的代碼使得你在更換平臺(tái)是比較容易,擴(kuò)大你的用戶基礎(chǔ),吹噓支持開(kāi)放平臺(tái)。這也使得你賭錯(cuò)了操作系統(tǒng)時(shí)比較容易補(bǔ)救。 將你的代碼設(shè)計(jì)得當(dāng)需要變化時(shí),影響是局部的。盡可能地封裝;將實(shí)現(xiàn)細(xì)節(jié)申明為私有。只要可能,使用無(wú)名的命名空間和文件內(nèi)的靜態(tài)對(duì)象或函數(shù)。避免導(dǎo)致虛基類的設(shè)計(jì),因?yàn)檫@種類需要每個(gè)派生類都直接初始化它--即使是那些間接派生類。避免需要RTTI的設(shè)計(jì),它需要if...then...else型的瀑布結(jié)構(gòu)。每次,類的繼承層次變了,每組if...then...else語(yǔ)句都需要更新,如果你忘掉了一個(gè),你不會(huì)從編譯器得到任何告警。
未來(lái)時(shí)態(tài)的考慮只是簡(jiǎn)單地增加了一些額外約束:
·提供完備的類,即使某些部分現(xiàn)在還沒(méi)有被使用。如果有了新的需求,你不用回過(guò)頭去改它們。
·將你的接口設(shè)計(jì)得便于常見(jiàn)操作并防止常見(jiàn)錯(cuò)誤。使得類容易正確使用而不易用錯(cuò)。例如,阻止拷貝構(gòu)造和賦值操作,如果它們對(duì)這個(gè)類沒(méi)有意義的話。防止部分賦值。
·如果沒(méi)有限制你不能通用化你的代碼,那么通用化它。例如,如果在寫(xiě)樹(shù)的遍歷算法,考慮將它通用得可以處理任何有向不循環(huán)圖。
未來(lái)時(shí)態(tài)的考慮增加了你的代碼的可重用性、可維護(hù)性、健壯性,已及在環(huán)境發(fā)生改變時(shí)易于修改。它必須與進(jìn)行時(shí)態(tài)的約束條件進(jìn)行取舍。太多的程序員們只關(guān)注于現(xiàn)在的需要,然而這么做犧牲了其軟件的長(zhǎng)期生存能力。是與眾不同的,是離經(jīng)叛道的,在未來(lái)時(shí)態(tài)下開(kāi)發(fā)程序。
新的函數(shù)將被加入到函數(shù)庫(kù)中,新的重載將發(fā)生,于是要注意那些含糊的函數(shù)調(diào)用行為的結(jié)果;新的類將會(huì)加入繼承層次,現(xiàn)在的派生類將會(huì)是以后的基類,并已為此作好準(zhǔn)備;將會(huì)編制新的應(yīng)用軟件,函數(shù)將在新的運(yùn)行環(huán)境下被調(diào)用,它們應(yīng)該被寫(xiě)得在新平臺(tái)上運(yùn)行正確;程序的維護(hù)人員通常不是原來(lái)編寫(xiě)它們的人,因此應(yīng)該被設(shè)計(jì)得易于被別人理解、維護(hù)和擴(kuò)充。
這么做的一種方法是:用C++語(yǔ)言自己來(lái)表達(dá)設(shè)計(jì)上的約束條件,而不是用注釋或文檔。例如,如果一個(gè)類被設(shè)計(jì)得不會(huì)被繼承,不要只是在其頭文件中加個(gè)注釋,用C++的方法來(lái)阻止繼承。
如果一個(gè)類需要其實(shí)例全部創(chuàng)建在堆中,不要只是對(duì)用戶說(shuō)了這么一句,用以前介紹過(guò)的方法來(lái)強(qiáng)迫這一點(diǎn)。
如果拷貝構(gòu)造和賦值對(duì)一個(gè)類是沒(méi)有意義的,通過(guò)申明它們?yōu)樗接衼?lái)阻止這些操作。
應(yīng)該判斷一個(gè)函數(shù)的含意,以及它被派生類重定義的話是否有意義。如果是有意義的,申明它為虛,即使沒(méi)有人立即重定義它。如果不是的話,申明它為非虛,并且不要在以后為了便于某人而更改;確保更改是對(duì)整個(gè)類的運(yùn)行環(huán)境和類所表示的抽象是有意義的。
處理每個(gè)類的賦值和拷貝構(gòu)造函數(shù),即使“從沒(méi)人這樣做過(guò)”。他們現(xiàn)在沒(méi)有這么做并不意味著他們以后不這么做。如果這些函數(shù)是難以實(shí)現(xiàn)的,那么申明它們?yōu)樗接小_@樣,不會(huì)有人誤調(diào)編譯器提供的默認(rèn)版本而做錯(cuò)事(這在默認(rèn)賦值和拷貝構(gòu)造函數(shù)上經(jīng)常發(fā)生)。
基于最小驚訝法則:努力提供這樣的類,它們的操作和函數(shù)有自然的語(yǔ)法和直觀的語(yǔ)義。和內(nèi)建數(shù)據(jù)類型的行為保持一致:拿不定主意時(shí),仿照int來(lái)做。
要承認(rèn):只要是能被人做的,就有人這么做(WQ:莫菲法則)。他們會(huì)拋異常;會(huì)用自己給自己賦值;在沒(méi)有賦初值前就使用對(duì)象;給對(duì)象賦了值而沒(méi)有使用;會(huì)賦過(guò)大的值、過(guò)小的值或空值。一般而言,只要能編譯通過(guò),就有人會(huì)這么做。所以,要使得自己的類易于被正確使用而難以誤用。要承認(rèn)用戶可能犯錯(cuò)誤,所以要將你的類設(shè)計(jì)得可以防止、檢測(cè)或修正這些錯(cuò)誤。
努力于可移植的代碼。寫(xiě)可移植的代碼并不比不可移植的代碼難太多,只有在性能極其重要時(shí)采用不可移植的結(jié)構(gòu)才是可取的。即使是為特定的硬件設(shè)計(jì)的程序也經(jīng)常被移植,因?yàn)檫@些平臺(tái)在幾年內(nèi)就會(huì)有一個(gè)數(shù)量級(jí)的性能提升。可移植的代碼使得你在更換平臺(tái)是比較容易,擴(kuò)大你的用戶基礎(chǔ),吹噓支持開(kāi)放平臺(tái)。這也使得你賭錯(cuò)了操作系統(tǒng)時(shí)比較容易補(bǔ)救。 將你的代碼設(shè)計(jì)得當(dāng)需要變化時(shí),影響是局部的。盡可能地封裝;將實(shí)現(xiàn)細(xì)節(jié)申明為私有。只要可能,使用無(wú)名的命名空間和文件內(nèi)的靜態(tài)對(duì)象或函數(shù)。避免導(dǎo)致虛基類的設(shè)計(jì),因?yàn)檫@種類需要每個(gè)派生類都直接初始化它--即使是那些間接派生類。避免需要RTTI的設(shè)計(jì),它需要if...then...else型的瀑布結(jié)構(gòu)。每次,類的繼承層次變了,每組if...then...else語(yǔ)句都需要更新,如果你忘掉了一個(gè),你不會(huì)從編譯器得到任何告警。
未來(lái)時(shí)態(tài)的考慮只是簡(jiǎn)單地增加了一些額外約束:
·提供完備的類,即使某些部分現(xiàn)在還沒(méi)有被使用。如果有了新的需求,你不用回過(guò)頭去改它們。
·將你的接口設(shè)計(jì)得便于常見(jiàn)操作并防止常見(jiàn)錯(cuò)誤。使得類容易正確使用而不易用錯(cuò)。例如,阻止拷貝構(gòu)造和賦值操作,如果它們對(duì)這個(gè)類沒(méi)有意義的話。防止部分賦值。
·如果沒(méi)有限制你不能通用化你的代碼,那么通用化它。例如,如果在寫(xiě)樹(shù)的遍歷算法,考慮將它通用得可以處理任何有向不循環(huán)圖。
未來(lái)時(shí)態(tài)的考慮增加了你的代碼的可重用性、可維護(hù)性、健壯性,已及在環(huán)境發(fā)生改變時(shí)易于修改。它必須與進(jìn)行時(shí)態(tài)的約束條件進(jìn)行取舍。太多的程序員們只關(guān)注于現(xiàn)在的需要,然而這么做犧牲了其軟件的長(zhǎng)期生存能力。是與眾不同的,是離經(jīng)叛道的,在未來(lái)時(shí)態(tài)下開(kāi)發(fā)程序。