第二章為程序設(shè)計(jì)技術(shù),本文為2.2.3 內(nèi)置函數(shù)指針和2.2.4 嵌套結(jié)構(gòu)體。
我們知道,數(shù)組和指針是相同類型有序數(shù)據(jù)的集合,但很多時(shí)候需要將不同類型的數(shù)據(jù)捆綁在一起作為一個(gè)整體來(lái)對(duì)待,使程序設(shè)計(jì)更方便。在C語(yǔ)言中,這樣的一組數(shù)據(jù)被稱為結(jié)構(gòu)體。
>>>2.2.3內(nèi)置函數(shù)指針
面對(duì)一系列數(shù)據(jù),真正重要的不是如何存儲(chǔ)數(shù)據(jù),而是如何使用數(shù)據(jù)。實(shí)際上,一個(gè)結(jié)構(gòu)體的成員可以是數(shù)據(jù),還可以是包含操作數(shù)據(jù)的函數(shù)指針。為了支持這種風(fēng)格,在這里不妨引入一個(gè)新的概念——方法是作為某個(gè)結(jié)構(gòu)體的一部分聲明的,有了方法就可以操作存儲(chǔ)在結(jié)構(gòu)體中的數(shù)據(jù)。
1.類型與變量
當(dāng)函數(shù)指針作為結(jié)構(gòu)體的成員時(shí),即將校驗(yàn)參數(shù)和調(diào)用校驗(yàn)器的函數(shù)指針?lè)庋b在一起,形成了一個(gè)新的結(jié)構(gòu)體類型。有了類型就可以定義一個(gè)該類型的變量,然后就可以用這個(gè)變量引用校驗(yàn)參數(shù)和調(diào)用校驗(yàn)器函數(shù)。
為了支持這種風(fēng)格,C允許將方法作為某個(gè)結(jié)構(gòu)體的一部分來(lái)聲明,那么操作存儲(chǔ)在結(jié)構(gòu)體中的數(shù)據(jù)就很容易了,詳見(jiàn)程序清單2.18。
程序清單 2.18 范圍值校驗(yàn)器接口
接下來(lái)需要設(shè)計(jì)一個(gè)判斷value值是否符合范圍值要求的validateRange()接口函數(shù),其具體的實(shí)現(xiàn)詳見(jiàn)程序清單2.19。
程序清單 2.19 范圍值校驗(yàn)器接口函數(shù)的實(shí)現(xiàn)
同理,偶校驗(yàn)器OddEvenValidator和變量oddEvenValidator的定義詳見(jiàn)程序清單2.20。
程序清單 2.20 偶校驗(yàn)器接口
接下來(lái)同樣需要設(shè)計(jì)一個(gè)判斷value值是否符合偶校驗(yàn)要求的validateOddEven()接口函數(shù),其具體的實(shí)現(xiàn)詳見(jiàn)程序清單2.21。
程序清單 2.21 偶校驗(yàn)器接口函數(shù)的實(shí)現(xiàn)
顯然,無(wú)論是什么校驗(yàn)器,其共性是value值合法性判斷,因此可以共用一個(gè)函數(shù)指針,即特殊的函數(shù)指針類型RangeValidate和OddEvenValidate被泛化成了一般的函數(shù)指針類型Validate。其次,由于每個(gè)函數(shù)都有一個(gè)指向當(dāng)前對(duì)象的pThis指針,因此特殊的結(jié)構(gòu)體類型struct _RangeValidator*和struct _OddEvenValidator *被泛化成了void *類型,即可接受任何類型數(shù)據(jù)的實(shí)參。比如:
這就是范型編程,校驗(yàn)器泛化接口的實(shí)現(xiàn)詳見(jiàn)程序清單 2.22。由于pRangeValidator與pThis的類型不同,因此必須對(duì)pThis指針強(qiáng)制類型轉(zhuǎn)換才能引用相應(yīng)結(jié)構(gòu)體的成員。
程序清單 2.22 通用校驗(yàn)器接口的實(shí)現(xiàn)(validator.c)
由此可見(jiàn),當(dāng)將方法作為結(jié)構(gòu)體的一部分聲明時(shí),就直接將方法和數(shù)據(jù)打包成為了一個(gè)新的數(shù)據(jù)類型RangeValidator。有了RangeValidator類型,就可以創(chuàng)建一個(gè)該類型的變量rangeValidator,即可通過(guò)rangeValidator引用該結(jié)構(gòu)體的數(shù)據(jù),并調(diào)用相應(yīng)的處理函數(shù)。真正想強(qiáng)化的是由方法定義結(jié)構(gòu)體的思想,而不是實(shí)現(xiàn)結(jié)構(gòu)體時(shí)碰巧用到的那些數(shù)據(jù)。
2.初始化
使用名為newRangeValidator的宏將結(jié)構(gòu)體初始化:
其中,validateRange為范圍值校驗(yàn)器的函數(shù)名,使用方法如下:
宏展開(kāi)后如下:
其相當(dāng)于:
如果有以下定義:
即可通過(guò)pValidator引用RangeValidator的min和max。校驗(yàn)函數(shù)的調(diào)用方式如下:
以上調(diào)用形式的前提是已知pValidator指向了確定的結(jié)構(gòu)體類型,如果pValidator將指向未知的校驗(yàn)器,顯然以上調(diào)用形式無(wú)法做到通用,那么將如何調(diào)用?
雖然pValidator與&rangeValidator.validate的類型不一樣,但它們的值相等,因此可以利用這一特性獲取validateRange()函數(shù)的地址。比如:
其調(diào)用形式如下:
3.接口與實(shí)現(xiàn)
為了便于閱讀,如程序清單2.23所示詳細(xì)地展示了通用校驗(yàn)器的接口。
程序清單 2.23 通用校驗(yàn)器接口(validator.h)
以范圍值校驗(yàn)器為例,調(diào)用validateRange()的rangeCheck()函數(shù)的實(shí)現(xiàn)如下:
rangeCheck()函數(shù)的調(diào)用形式如下:
由此可見(jiàn),rangeCheck()函數(shù)的實(shí)現(xiàn)不依賴任何具體校驗(yàn)器。 注意,在這里,作者并沒(méi)有提供完整的代碼,請(qǐng)讀者補(bǔ)充完善。
>>>2.2.4嵌套結(jié)構(gòu)體
1.重構(gòu)
隨著添加一個(gè)又一個(gè)功能,處理一個(gè)又一個(gè)錯(cuò)誤,代碼的結(jié)構(gòu)會(huì)逐漸退化。如果對(duì)此置之不理,這種退化最終會(huì)導(dǎo)致糾結(jié)不清,難以維護(hù)的混亂代碼,因此需要經(jīng)常性地重構(gòu)代碼扭轉(zhuǎn)這種退化。
重構(gòu)就是在不改變代碼行為的前提下,對(duì)其進(jìn)行一系列小的改進(jìn),旨在改進(jìn)系統(tǒng)結(jié)構(gòu)的實(shí)踐活動(dòng)。雖然每個(gè)改進(jìn)都是微不足道的,甚至幾乎不值得去做,但如果將所有的改造疊加在一起時(shí),對(duì)系統(tǒng)設(shè)計(jì)和架構(gòu)的改進(jìn)效果是十分明顯的。
在每次細(xì)微改進(jìn)后,通過(guò)運(yùn)行單元測(cè)試以確保改進(jìn)沒(méi)有造成任何破壞,然后才去做下一次改進(jìn)。如此往復(fù)周而復(fù)始,每次改進(jìn)后都要運(yùn)行,通過(guò)這種方式保證在改進(jìn)系統(tǒng)設(shè)計(jì)的同時(shí)系統(tǒng)能夠正常工作。
重構(gòu)是持續(xù)進(jìn)行的,而不是在項(xiàng)目結(jié)束時(shí)、發(fā)布版本時(shí)、迭代結(jié)束時(shí)、甚至每天下班時(shí)才進(jìn)行。重構(gòu)是每隔一個(gè)小時(shí)或半個(gè)小時(shí)就要去做的事情,通過(guò)重構(gòu)可以持續(xù)地保持盡可能干凈、簡(jiǎn)單且有表現(xiàn)力的代碼。
大量的實(shí)踐證明,重復(fù)可能是軟件中一切邪惡的根源,許多原則和實(shí)踐規(guī)則都是為了控制與消除重復(fù)而創(chuàng)建的。消除重復(fù)最好的方法就是抽象,即將所有公共的函數(shù)指針移到一個(gè)單獨(dú)的結(jié)構(gòu)體中,創(chuàng)建一個(gè)通用的Validator類型校驗(yàn)器。也就是說(shuō),如果兩種事物相似的話,必定存在某種抽象能夠統(tǒng)一它們,因此消除重復(fù)的行為會(huì)迫使團(tuán)隊(duì)提煉出許多的抽象,進(jìn)一步減少代碼之間的耦合。
自從發(fā)明子程序以來(lái),軟件開(kāi)發(fā)領(lǐng)域的所有創(chuàng)新都是在不斷嘗試從源代碼中消滅重復(fù),即DRY(Don't Repeat Yourself)原則——?jiǎng)e重復(fù)自己,因?yàn)橹貜?fù)黏貼會(huì)帶來(lái)很多的問(wèn)題,所以無(wú)論在哪里發(fā)現(xiàn)重復(fù)的代碼,都必須消除它們。
2.類型與變量
實(shí)際上,不管是范圍值校驗(yàn)器還是奇偶校驗(yàn)器,其本質(zhì)上都是校驗(yàn)器,其相同的屬性是校驗(yàn)參數(shù)和待校驗(yàn)的值,其相同的行為可以共用一個(gè)函數(shù)指針調(diào)用不同的校驗(yàn)器。根據(jù)依賴倒置原則,將它們相同的屬性和行為抽象為一個(gè)結(jié)構(gòu)體類型Validator。比如:
在這里,還是以范圍值校驗(yàn)為例,在RangeValidatro結(jié)構(gòu)體中嵌套一個(gè)Validator類型的結(jié)構(gòu)體,即將Validator類型的變量isa作為RangeValidator結(jié)構(gòu)體的成員。比如:
由于&rangeValidator與&rangeValidator.isa的值相等,因此以下關(guān)系恒成立。比如:
即可將validateRange()函數(shù)原型:
中的“void *pThis”轉(zhuǎn)換為“Validator *pThis”,validatrRange()函數(shù)原型進(jìn)化為:
3.初始化
當(dāng)將Validator類型的isa作為RangeValidator結(jié)構(gòu)體成員時(shí),顯然rangeValidator.isa是一個(gè)結(jié)構(gòu)體變量名,可以象任何普通結(jié)構(gòu)體變量一樣使用。使用Validator類型表達(dá)式:
即可引用rangeValidator變量的結(jié)構(gòu)體成員isa的成員validate,即將rangeValidator.isa作為另一個(gè)點(diǎn)操作符的左操作符。比如:
由于點(diǎn)操作符的結(jié)合性是從左向右的,因此可以省略括號(hào)。其等價(jià)于:
只要將rangeValidator.isa看作一個(gè)Validator類型的變量即可。
使用名為newRangeValidator的宏將結(jié)構(gòu)體初始化:
其中,validateRange為范圍值校驗(yàn)器函數(shù)名,使用方法如下:
宏展開(kāi)后如下:
其中,外面的{}為RangeValidator結(jié)構(gòu)體賦值,內(nèi)部的{}為RangeValidator結(jié)構(gòu)體的成員變量isa賦值。即:
如果有以下定義:
即可用pValidator引用RangeValidator的min和max。
由于pValidator與&rangeValidator.isa不僅類型相同且值相等,則以下關(guān)系同樣成立:
因此可以利用這一特性獲取validateRange()函數(shù)的地址,即pValidator->validate指向validateRange()。其調(diào)用形式如下:
4.接口與實(shí)現(xiàn)
以范圍值校驗(yàn)器為例,validatorCheck()函數(shù)的調(diào)用形式如下:
當(dāng)然,也可以采取以下調(diào)用形式:
其效果是一樣的。
為了便于閱讀,如程序清單 2.24所示詳細(xì)地展示了通用校驗(yàn)器的接口。
程序清單 2.24通用校驗(yàn)器接口(validator.h)
以范圍值校驗(yàn)器為例,調(diào)用validateRange()的validatorCheck()函數(shù)的實(shí)現(xiàn)如下:
由此可見(jiàn),validatorCheck()函數(shù)的實(shí)現(xiàn)不依賴任何具體校驗(yàn)器,通用校驗(yàn)器接口的實(shí)現(xiàn)詳見(jiàn)程序清單 2.25。
程序清單 2.25 通用校驗(yàn)器接口的實(shí)現(xiàn)(validator.c)
在這里,作者并沒(méi)有提供完整的代碼,請(qǐng)讀者補(bǔ)充完善。
-
指針
+關(guān)注
關(guān)注
1文章
480瀏覽量
70551 -
C語(yǔ)言編程
+關(guān)注
關(guān)注
6文章
90瀏覽量
21103 -
周立功
+關(guān)注
關(guān)注
38文章
130瀏覽量
37616 -
結(jié)構(gòu)體
+關(guān)注
關(guān)注
1文章
130瀏覽量
10840
原文標(biāo)題:周立功:結(jié)構(gòu)體,使程序設(shè)計(jì)更方便——內(nèi)置函數(shù)指針和嵌套結(jié)構(gòu)體
文章出處:【微信號(hào):ZLG_zhiyuan,微信公眾號(hào):ZLG致遠(yuǎn)電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論