色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

面向?qū)ο缶幊獭^承與多態(tài)

AGk5_ZLG_zhiyua ? 來(lái)源:未知 ? 作者:佚名 ? 2017-10-20 14:03 ? 次閱讀

周立功教授數(shù)年之心血之作《程序設(shè)計(jì)與數(shù)據(jù)結(jié)構(gòu)》以及《面向AMetal框架與接口編程(上)》,電子版已無(wú)償性分享到電子工程師與高校群體,書(shū)本內(nèi)容公開(kāi)后,在電子行業(yè)掀起一片學(xué)習(xí)熱潮。經(jīng)周立功教授授權(quán),本公眾號(hào)特對(duì)《程序設(shè)計(jì)與數(shù)據(jù)結(jié)構(gòu)》一書(shū)內(nèi)容進(jìn)行連載,愿共勉之。

第四章為面向?qū)ο缶幊?/span>,本文為4.3 繼承與多態(tài)

>>> 4.3.1 抽象

假設(shè)需要設(shè)計(jì)一個(gè)處理工資單的數(shù)據(jù)包,可以將排序作為一個(gè)關(guān)鍵的業(yè)務(wù)進(jìn)行抽象。雖然各種排序的實(shí)現(xiàn)不一樣,但它們的共性都是“排序”,這就是抽象的基礎(chǔ)。如果要建立一個(gè)矩陣代數(shù)程序包,就要討論抽象矩陣。雖然各種類(lèi)型矩陣的實(shí)現(xiàn)各不相同,但根據(jù)它們表現(xiàn)的共同行為特性,可以將這些矩陣歸為一類(lèi),顯然其共性又一次支持了抽象。

如果用戶(hù)有一個(gè)這樣的需求——校驗(yàn)push到棧中的數(shù)據(jù),則實(shí)現(xiàn)者一定會(huì)問(wèn)“校驗(yàn)規(guī)則是什么?”因?yàn)樾r?yàn)是一個(gè)非常“抽象”的概念;如果用戶(hù)明確地告訴實(shí)現(xiàn)者——對(duì)push到棧中的數(shù)據(jù)進(jìn)行范圍值校驗(yàn)或偶校驗(yàn),則不會(huì)出現(xiàn)這樣模糊的問(wèn)題。當(dāng)需要對(duì)push到棧中的數(shù)據(jù)進(jìn)行范圍值校驗(yàn)時(shí),則需要編寫(xiě)一個(gè)RangeValidator類(lèi);當(dāng)再需要添加一個(gè)奇偶校驗(yàn)器時(shí),勢(shì)必又要編寫(xiě)一個(gè)OddEvenValidator類(lèi)。顯然每添加一種校驗(yàn)器就要增加一個(gè)接口,根本無(wú)法做到重用。

雖然它們的類(lèi)型不同,且不同校驗(yàn)器的對(duì)象各有不同,但它們共同的概念都是“校驗(yàn)器”。回歸校驗(yàn)器的本質(zhì),無(wú)論是什么校驗(yàn)器,其共同的屬性是校驗(yàn)參數(shù),其共同的行為是可以使用相同的方法——在動(dòng)態(tài)中根據(jù)對(duì)象的類(lèi)型調(diào)用不同的校驗(yàn)器函數(shù)。

顯然,用戶(hù)是在概念層次上提出了校驗(yàn)的需求與實(shí)現(xiàn)者交流,而具體如何校驗(yàn)是在實(shí)現(xiàn)層次進(jìn)行的,用戶(hù)無(wú)需準(zhǔn)確地知道具體是如何實(shí)現(xiàn)的。因此只要概念不變,即可做到用戶(hù)與實(shí)現(xiàn)細(xì)節(jié)的變化完全分離。

在面向過(guò)程編程中,新手對(duì)共性的認(rèn)識(shí)往往來(lái)源于直覺(jué),以創(chuàng)建范圍值校驗(yàn)器類(lèi)和偶校驗(yàn)器類(lèi)為例,程序員普遍都會(huì)按照以下方法表達(dá)這種共性,將Validate提取為一個(gè)公共的函數(shù)指針。比如:

而對(duì)于一個(gè)擁有“面向?qū)ο笏季S”且經(jīng)驗(yàn)豐富的程序員,更傾向于將各種校驗(yàn)器的共性打包在一個(gè)函數(shù)指針中作為結(jié)構(gòu)體的成員創(chuàng)建一個(gè)抽象類(lèi)。Validator抽象類(lèi)的定義如下:

其中,pThis是指向當(dāng)前對(duì)象的指針,Validator是一個(gè)沒(méi)有具體屬性,代表多種具有共性的數(shù)據(jù)和行為的具體校驗(yàn)器總稱(chēng)的抽象類(lèi)。Validator類(lèi)沒(méi)有提供任何實(shí)現(xiàn)validate方法的代碼,正是因?yàn)檫@一點(diǎn),該方法才能成為一個(gè)抽象的方法,因?yàn)樘峁┤魏未a都會(huì)使方法成為具體方法。

由于Validator是一個(gè)抽象類(lèi),因此無(wú)法創(chuàng)建實(shí)例,自然也就不知道要校驗(yàn)什么?那么誰(shuí)知道呢?范圍值校驗(yàn)器和奇偶校驗(yàn)器類(lèi)知道自己要做什么校驗(yàn)。由于Validator有一個(gè)validate方法,因此可以將Validator抽象類(lèi)封裝成RangeValidator派生類(lèi)的成員——Validator類(lèi)的變量isa,即將實(shí)現(xiàn)細(xì)節(jié)委托給子類(lèi)。在范圍值校驗(yàn)器和奇偶校驗(yàn)器類(lèi)重新定義,各自實(shí)現(xiàn)它自己的validate方法。

>>> 4.3.2 繼承

在這里將引入一個(gè)新的概念繼承描述類(lèi)之間的關(guān)系。由于RangeValidator范圍值校驗(yàn)器和OddEvenValidator奇偶校驗(yàn)器的共性是校驗(yàn)參數(shù)和調(diào)用校驗(yàn)函數(shù)的方法,因此將其共性上移到一個(gè)名為Validator校驗(yàn)器類(lèi)(父類(lèi))中。

基于此,在將具有可變性的校驗(yàn)參數(shù)分別轉(zhuǎn)移到RangeValidator和OddEvenValidator中的同時(shí),并將Validator類(lèi)型的變量isa作為結(jié)構(gòu)體的成員,即可創(chuàng)建新的結(jié)構(gòu)體數(shù)據(jù)類(lèi)型:

其中,pThis為指向Validator類(lèi)對(duì)象的指針,RangeValidator和OddEvenValidator派生自Validator類(lèi),RangeValidator和OddEvenValidator是Validator的子類(lèi),Validator類(lèi)是RangeValidator和OddEvenValidatorr類(lèi)的基類(lèi)或超類(lèi)。因?yàn)镽angeValidator是一種校驗(yàn)器,OddEvenvalidator也是一種校驗(yàn)器。當(dāng)一個(gè)子類(lèi)繼承自一個(gè)基類(lèi)時(shí),它可以做基類(lèi)能做的任何事情,因此RangeValidator和OddEvenValidator都是Validator的擴(kuò)充。

雖然父類(lèi)和子類(lèi)的類(lèi)型不一樣,當(dāng)通過(guò)繼承將不同類(lèi)的共同屬性和行為抽象為一個(gè)公共的基類(lèi)后,于是它們就具有了共同的屬性和行為,這就是OOP通過(guò)繼承實(shí)現(xiàn)代碼重用的方法。因?yàn)槌橄箢?lèi)在概念上定義了相似的一組類(lèi)的共同屬性和方法,因而能夠?qū)⑦@一組相關(guān)類(lèi)看成一個(gè)概念。也就是說(shuō),抽象類(lèi)代表了將所有派生類(lèi)聯(lián)系起來(lái)的核心概念,也正是這個(gè)核心概念定義了派生類(lèi)的共性。同時(shí)還提供了與這一組相關(guān)類(lèi)的通信接口規(guī)約,然后每個(gè)具體類(lèi)都按需要提供特定的實(shí)現(xiàn)。

由此可見(jiàn),對(duì)于一個(gè)新的抽象,必須將它放在已經(jīng)設(shè)計(jì)好的類(lèi)和對(duì)象層次結(jié)構(gòu)的上下文中。實(shí)際上,這既不是自上而下的活動(dòng),也不是自下而上的活動(dòng)。Halbert和O 'Brien指出,“當(dāng)在一個(gè)類(lèi)型層次結(jié)構(gòu)中設(shè)計(jì)類(lèi)時(shí),并非總是從基類(lèi)開(kāi)始,然后創(chuàng)建子類(lèi)。通常會(huì)創(chuàng)建一些看起來(lái)不相似的類(lèi)型,當(dāng)意識(shí)到它們是相關(guān)時(shí),然后才將它們的共性分離出來(lái),放到一個(gè)基類(lèi)或多個(gè)基類(lèi)中……”實(shí)踐經(jīng)驗(yàn)證明,類(lèi)和對(duì)象的設(shè)計(jì)是一個(gè)增量、迭代的過(guò)程。Stroustrup認(rèn)為,“最常見(jiàn)的類(lèi)層次結(jié)構(gòu)的組織方式是從兩個(gè)類(lèi)中提取公共部分放到一個(gè)新類(lèi)中,或?qū)⒁粋€(gè)類(lèi)拆分為兩個(gè)新類(lèi)。” 比如,將RangeValidator和OddEvenValidator的共性上移到Validator中。

由于許多開(kāi)發(fā)者常常忽略了為對(duì)象和類(lèi)正確地命名,因此必須確保創(chuàng)建類(lèi)、屬性和方法名時(shí),不僅要遵循約定,還要讓名字具有描述性,讓人一目了然。否則解釋權(quán)在程序員自己,因?yàn)槌绦騿T的個(gè)性,時(shí)常有可能創(chuàng)建一些只對(duì)他們自己很有道理的約定,而其他人卻完全不能理解。類(lèi)名不能為動(dòng)詞,類(lèi)名應(yīng)該用常見(jiàn)的名詞命名,比如,Validator或RangeValidator,避免使用Manager、Processor、Data或Info這樣的類(lèi)名。對(duì)象名應(yīng)該用合適的名詞短語(yǔ)命名,比如,rangeValidator或oddEvenValidator。特別地,選擇的名字應(yīng)該是業(yè)務(wù)領(lǐng)域?qū)<沂褂煤驼J(rèn)知的名字。方法名應(yīng)該是動(dòng)詞或動(dòng)詞短語(yǔ),比如,pushWithValidate。

當(dāng)開(kāi)發(fā)者決定采用某種協(xié)作模式后,工作會(huì)被分解給對(duì)象,即在相應(yīng)的類(lèi)上定義適當(dāng)?shù)姆椒āw根到底,單個(gè)類(lèi)的協(xié)議包含了實(shí)現(xiàn)所有行為,以及實(shí)現(xiàn)與其實(shí)例相關(guān)的所有機(jī)制所需要的全部操作。因此與類(lèi)層次結(jié)構(gòu)的設(shè)計(jì)一樣,機(jī)制代表了戰(zhàn)略的設(shè)計(jì)決策。

實(shí)際上,機(jī)制就是在長(zhǎng)期的實(shí)踐中發(fā)現(xiàn)和總結(jié)的各種模式。在底層開(kāi)發(fā)模式中,慣用法是一種表現(xiàn)形式;在高層開(kāi)發(fā)模式中,則有一組類(lèi)組成的框架。框架代表了大規(guī)模的復(fù)用,比如,ZLG的AMetal框架和AWorks框架,MVC框架和MVVM框架以及微軟的.NET框架或開(kāi)源代碼。所以機(jī)制代表了一種層次的復(fù)用,它高于單個(gè)類(lèi)的復(fù)用。

雖然代碼表明了基類(lèi)與子類(lèi)的關(guān)系,但還是不夠深刻。在這里,將以Validator與RangeValidator之間的繼承關(guān)系為例,通過(guò)UML圖進(jìn)一步形象地描述,詳見(jiàn)圖 4.4。

圖 4.4 繼承關(guān)系圖

繼承關(guān)系為何指向基類(lèi)?其深刻的設(shè)計(jì)思想是它代表了依賴(lài)的方向。所謂依賴(lài)關(guān)系是指兩個(gè)元素之間的一種關(guān)系,其中一個(gè)元素變化將會(huì)引起另一個(gè)元素變化。UML圖中采用從子類(lèi)指向基類(lèi)的空心箭頭表示繼承,暗示基類(lèi)的變化可能導(dǎo)致子類(lèi)的變化。簡(jiǎn)而言之,被依賴(lài)的先構(gòu)造,依賴(lài)于其它元素的后構(gòu)造。

其實(shí)繼承是一個(gè)非常傳統(tǒng)和經(jīng)典的術(shù)語(yǔ),從Smalltalk問(wèn)世時(shí)就被廣泛使用,將一般類(lèi)和它的特殊類(lèi)之間的關(guān)系稱(chēng)為繼承關(guān)系。它在很多場(chǎng)合還以動(dòng)詞或形容詞的面目出現(xiàn)。比如,特殊類(lèi)繼承了一般類(lèi)的屬性和操作,面向?qū)ο缶幊陶Z(yǔ)言具有繼承性和封裝性等。

而一般-特殊恰當(dāng)?shù)匾话泐?lèi)和它的特殊類(lèi)之間的相對(duì)關(guān)系,既可以稱(chēng)為一般-特殊關(guān)系,也可以形成一般-特殊結(jié)構(gòu)。當(dāng)“一般”這個(gè)術(shù)語(yǔ)generalization翻譯成中文時(shí),很容易與上下文混淆,因此翻譯成“泛化”更準(zhǔn)確。即一般類(lèi)(父類(lèi))對(duì)特殊類(lèi)(子類(lèi))而言是泛化,反之就是特化。因此一般類(lèi)和特殊類(lèi)之間的關(guān)系稱(chēng)為一般-特殊關(guān)系,一般-特殊結(jié)構(gòu)是由一組具有一般-特殊關(guān)系(繼承關(guān)系)的類(lèi)所形成的結(jié)構(gòu)。

顯而易見(jiàn),一般-特殊結(jié)構(gòu)是問(wèn)題域中各類(lèi)事物之間客觀存在的一種結(jié)構(gòu),在面向?qū)ο蠓治瞿P椭薪⒁话?特殊結(jié)構(gòu),使模型更清晰地映射了問(wèn)題域中事物的分類(lèi)關(guān)系——將對(duì)象劃分為類(lèi),用類(lèi)描述屬于它的全部對(duì)象實(shí)例。它將具有一般-特殊關(guān)系的類(lèi)組織在一起,可以簡(jiǎn)化對(duì)復(fù)雜系統(tǒng)的認(rèn)識(shí),使人們對(duì)系統(tǒng)的認(rèn)識(shí)和描述更接近日常思維中一般概念和特殊概念的處理方式。

在面向?qū)ο蟮拈_(kāi)發(fā)中,一般-特殊結(jié)構(gòu)可以使開(kāi)發(fā)者簡(jiǎn)化對(duì)類(lèi)的定義,因而對(duì)象的共同特征只需在一般類(lèi)中給出,特殊類(lèi)通過(guò)繼承而自動(dòng)地?fù)碛羞@些特征,不必再重復(fù)定義。

不同的方法學(xué)對(duì)如何發(fā)現(xiàn)一般-特殊結(jié)構(gòu),有不同的策略。其最大的問(wèn)題讓人們感到更多地是依賴(lài)于直覺(jué),如果分析方法是一門(mén)藝術(shù),也就意味著讓人具有很大的不確定性。而事實(shí)上,使用共性和差異化分析工具,并從概念、規(guī)約和實(shí)現(xiàn)三個(gè)不同的視角看待對(duì)象,就可以簡(jiǎn)化復(fù)雜的系統(tǒng),詳見(jiàn)《嵌入式軟件工程方法與實(shí)踐叢書(shū)——面向?qū)ο蟮姆治雠c設(shè)計(jì)》。

>>> 4.3.3 職責(zé)驅(qū)動(dòng)設(shè)計(jì)

OO強(qiáng)調(diào)的是在現(xiàn)實(shí)世界或業(yè)務(wù)領(lǐng)域中找出軟件對(duì)象,而軟件對(duì)象與現(xiàn)實(shí)世界中對(duì)象的行為完全不一樣。軟件對(duì)象以點(diǎn)對(duì)點(diǎn)的通信方式通過(guò)發(fā)送消息進(jìn)行交互,而現(xiàn)實(shí)世界中的對(duì)象與環(huán)境的交互,以及其它對(duì)象動(dòng)態(tài)地反映現(xiàn)實(shí)世界的對(duì)象之間交互都要豐富得多。

經(jīng)驗(yàn)豐富的開(kāi)發(fā)人員在研究領(lǐng)域時(shí),如果發(fā)現(xiàn)了他們所熟悉的某種職責(zé)或某個(gè)關(guān)系網(wǎng),他們會(huì)想起以前這個(gè)問(wèn)題是如何解決的。以前嘗試過(guò)哪些模型?在實(shí)現(xiàn)中有哪些難題?它們是如何解決的?先前經(jīng)歷過(guò)的嘗試和失敗的教訓(xùn),會(huì)突然間與新的情況聯(lián)系起來(lái)。

為了真實(shí)地反映現(xiàn)實(shí)世界中對(duì)象的動(dòng)態(tài)交互,要讓一個(gè)類(lèi)在不同的系統(tǒng)中重用,則必須在設(shè)計(jì)類(lèi)時(shí)充分考慮擴(kuò)展性。經(jīng)過(guò)長(zhǎng)期的積累,人們總結(jié)了一套用于啟發(fā)和指導(dǎo)類(lèi)的設(shè)計(jì)原則:職責(zé)驅(qū)動(dòng)設(shè)計(jì)——如何為協(xié)作中的對(duì)象分配職責(zé)。

顯然,對(duì)于rangeValidator對(duì)象和oddEvenValidator對(duì)象來(lái)說(shuō),它們的職責(zé)分別是對(duì)push到棧中的數(shù)據(jù)進(jìn)行范圍值校驗(yàn)和偶校驗(yàn),也就意味著必須存在相應(yīng)的方法。由于每個(gè)子類(lèi)都要對(duì)自己的行為負(fù)責(zé),因此每個(gè)子類(lèi)不僅要提供一個(gè)名為validate的方法,而且必須提供它自己的實(shí)現(xiàn)代碼。比如,RangeValidator和OddEvenValidator都有一個(gè)validate的方法,RangeValidator類(lèi)包含范圍值校驗(yàn)的代碼,OddEvenValidator類(lèi)肯定有奇偶校驗(yàn)的代碼。它們都是Validator的子類(lèi),必須實(shí)現(xiàn)其不同版本的validate。

不言而喻,OOP比POP更直接地表達(dá)了校驗(yàn)器的共性:“使用validate函數(shù)指針在運(yùn)行中根據(jù)對(duì)象的類(lèi)型調(diào)用不同的函數(shù),并通過(guò)pThis指針指向當(dāng)前對(duì)象引用校驗(yàn)參數(shù)將共同的部分打包在一起形成抽象類(lèi)。”當(dāng)它們有了這種共性時(shí),則更容易討論各種校驗(yàn)器相互之間的差別。

除了變量value之外,RangeValidator類(lèi)對(duì)象的validateRange()校驗(yàn)函數(shù)的共性是符合范圍值條件的判斷處理語(yǔ)句,其可變的是范圍值校驗(yàn)參數(shù)min和max;OddEvenValidator類(lèi)對(duì)象的validateOddEven()校驗(yàn)函數(shù)的共性是符合偶數(shù)值條件的判斷處理語(yǔ)句,其可變的是偶校驗(yàn)參數(shù)isEven。

根據(jù)共性和可變性分析原理,將穩(wěn)定不變的相同的處理部分都包含在抽象的模塊中,可變性分析所發(fā)現(xiàn)的變化的變量由外部傳遞進(jìn)來(lái)的參數(shù)應(yīng)對(duì)。其函數(shù)原型如下:

由于&rangeValidator.isa、&oddEvenValidator.isa和pThis值相等,且類(lèi)型也相同,因此可以將范圍值校驗(yàn)和奇偶校驗(yàn)函數(shù)的void *泛化為Validator *。當(dāng)將一個(gè)基類(lèi)對(duì)象替換成它的子類(lèi)對(duì)象時(shí),程序?qū)⒉粫?huì)產(chǎn)生任何錯(cuò)誤和異常,且使用者不必知道任何差異,反過(guò)來(lái)則不成立。也就是說(shuō),如果某段代碼使用了基類(lèi)中的方法,必須能使用派生類(lèi)的對(duì)象,且自己不必進(jìn)行任何修改。因此在程序中要盡量使用基類(lèi)類(lèi)型定義對(duì)象,在運(yùn)行時(shí)再確定其子類(lèi)類(lèi)型,用子類(lèi)對(duì)象替換基類(lèi)對(duì)象。這就是里氏替換原則,它是由2008年圖靈獎(jiǎng)獲得者,美國(guó)第一位計(jì)算機(jī)科學(xué)女博士Barbara Liskov教授和卡耐基梅隆大學(xué)Jeannette Wing教授于1994年提出的。

在應(yīng)用里氏替換原則時(shí),應(yīng)該將父類(lèi)設(shè)計(jì)為抽象類(lèi)或接口,讓子類(lèi)繼承父類(lèi)或?qū)崿F(xiàn)父類(lèi)接口,并實(shí)現(xiàn)在父類(lèi)中聲明的方法。在運(yùn)行時(shí)子類(lèi)實(shí)例替換父類(lèi)實(shí)例,可以很方便地?cái)U(kuò)展系統(tǒng)的功能。無(wú)須修改原有子類(lèi)的代碼,增加新的功能可以通過(guò)增加一個(gè)新的子類(lèi)實(shí)現(xiàn),由此可見(jiàn),里氏替換原則是實(shí)現(xiàn)開(kāi)閉原則的重要方式之一。

如果開(kāi)閉原則是面向?qū)ο笤O(shè)計(jì)的目標(biāo),那么依賴(lài)倒置原則就是面向?qū)ο笤O(shè)計(jì)的主要原則之一,它是抽象化的具體實(shí)現(xiàn)。依賴(lài)倒置原則要求傳遞傳遞參數(shù)時(shí)或在關(guān)聯(lián)關(guān)系中,盡量引用高層次的抽象層類(lèi),即使用接口和抽象類(lèi)進(jìn)行變量的聲明、參數(shù)類(lèi)型的聲明、方法返回類(lèi)型的聲明,以及數(shù)據(jù)類(lèi)型的轉(zhuǎn)換等,而不要用具體類(lèi)做這些事。

為了確保該原則的應(yīng)用,一個(gè)具體類(lèi)應(yīng)該只實(shí)現(xiàn)接口或抽象類(lèi)中聲明過(guò)的方法,而不是給出多余的方法,否則將無(wú)法調(diào)用在子類(lèi)中增加新的方法。顯而易見(jiàn),在引入抽象層后,將具體類(lèi)寫(xiě)在配置文件中。如果需求發(fā)生改變,則只需要擴(kuò)展抽象層,修改相應(yīng)的配置文件即可。而無(wú)須修改原有系統(tǒng)的代碼,就能擴(kuò)展系統(tǒng)的功能,滿足開(kāi)閉原則。通常開(kāi)閉原則、里氏替換原則和依賴(lài)倒置原則會(huì)同時(shí)出現(xiàn),開(kāi)閉原則是目標(biāo),里氏替換原則是基礎(chǔ),依賴(lài)倒置原則是手段,它們相輔相成相互補(bǔ)充,其目標(biāo)是一致的,只是分析問(wèn)題的角度不同。

繼承是OO建模和編程中被廣泛濫用的概念之一,如果違反了LisKov替換原則,繼承層次可能仍然可以提供代碼的可重用性,但是將會(huì)失去可擴(kuò)展性。因此在使用繼承時(shí),要想一想派生類(lèi)是否可以替換基類(lèi)。如果不能,則要問(wèn)一問(wèn)自己為何使用繼承?如果在編寫(xiě)新的類(lèi)時(shí),還要重用基類(lèi)代碼代碼,則要考慮使用組合。

和繼承一樣,組合也是一種構(gòu)建對(duì)象的機(jī)制。如果新類(lèi)可以替換已有的類(lèi),且它們之間的關(guān)系可以描述為is-a,則使用繼承。如果新類(lèi)只是使用已有的類(lèi),且它們之間的關(guān)系可以描述為has-a,則使用組合。相對(duì)繼承來(lái)說(shuō),組合更加靈活,適用性也更強(qiáng)。

有關(guān)組合的使用方法和示例,將在后續(xù)相關(guān)的教程中,結(jié)合具體的應(yīng)用予以闡述。

在這里,RangeValidator和OddEvenValidator類(lèi)擴(kuò)展了(即繼承)Validator,其相應(yīng)的校驗(yàn)器接口的實(shí)現(xiàn)詳見(jiàn)程序清單 4.9。

程序清單 4.9 通用校驗(yàn)器接口的實(shí)現(xiàn)(Validator.c)

由此可見(jiàn),抽象是一個(gè)強(qiáng)大的分析工具,其強(qiáng)調(diào)的什么是共同的,因此共性和差異化分析自然而然地成為了抽象的理論基礎(chǔ)。共性分析尋找的是不可能隨時(shí)間而改變的結(jié)構(gòu),而可變性分析則要找到可能變化的結(jié)構(gòu)。如果變化是“業(yè)務(wù)領(lǐng)域”中各個(gè)特定的具體情況,那么共性就定義了業(yè)務(wù)領(lǐng)域中將這些情況聯(lián)系起來(lái)的概念。共同的概念用抽象類(lèi)表示,可變性分析所發(fā)現(xiàn)的變化將通過(guò)從抽象類(lèi)派生而來(lái)的具體類(lèi)實(shí)現(xiàn)。共性與可變性分析工具不僅可以指導(dǎo)我們創(chuàng)建抽象類(lèi)和派生類(lèi),而且還可以指導(dǎo)我們建立抽象和接口。那么類(lèi)的設(shè)計(jì)過(guò)程自然而然地就簡(jiǎn)化成了兩個(gè)步驟:

  • 在定義抽象類(lèi)(共性)時(shí),需要知道用什么接口處理這個(gè)類(lèi)的所有職責(zé);

  • 在定義派生類(lèi)(可變性)時(shí),需要知道對(duì)于一個(gè)給定的特定實(shí)現(xiàn)(即變化),應(yīng)該如何根據(jù)給定的規(guī)約實(shí)現(xiàn)它。

顯然,類(lèi)是一種編程語(yǔ)言結(jié)構(gòu),它描述了具有相同職責(zé)的所有對(duì)象。用相同的方式實(shí)現(xiàn)這些職責(zé),并共享相同的數(shù)據(jù)結(jié)構(gòu)。雖然它的內(nèi)部可能有一些屬性,可能有一些方法,但我們只關(guān)心對(duì)象對(duì)自己的行為負(fù)責(zé)。因?yàn)閷?shí)現(xiàn)隱藏在接口之后,實(shí)際上是將對(duì)象的實(shí)現(xiàn)和使用它們的對(duì)象徹底解耦了。所以只要概念不變,請(qǐng)求者與實(shí)現(xiàn)細(xì)節(jié)的變化隔離開(kāi)了。

為了便于閱讀,程序清單 4.10展示了通用校驗(yàn)器的接口。

程序清單 4.10 通用校驗(yàn)器的接口(validator.h)

在這里,還是以范圍值校驗(yàn)器為例,假設(shè)min=0,max=9,如程序清單 4.10(22)所示的使用名為newRangeValidator的宏將結(jié)構(gòu)體初始化的使用方法如下:

宏展開(kāi)后如下:

其中,外面的{}為RangeValidator結(jié)構(gòu)體賦值,內(nèi)部的{}為RangeValidator結(jié)構(gòu)體的成員變量isa賦值。即:

如果有以下定義:

即可用pValidator引用RangeValidator的min和max。

由于pValidator與&rangeValidator.isa不僅類(lèi)型相同,而且它們的值相等,則以下關(guān)系同樣成立:

因此可以利用這一特性獲取validateRange()函數(shù)的地址,即pValidator->validate指向validateRange()。其調(diào)用形式如下:

此時(shí)此刻,也許你會(huì)想到,既然它們的方法都一樣,只是屬性不同,為何不將它們合并為一個(gè)類(lèi)呢?如果這樣做的話,則一個(gè)類(lèi)承擔(dān)的職責(zé)越多,它被復(fù)用的可能性就越小。而且一個(gè)類(lèi)承擔(dān)的職責(zé)過(guò)多,就相當(dāng)于將這些職責(zé)耦合在一起。當(dāng)其中一個(gè)職責(zé)變化時(shí),可能會(huì)影響其它職責(zé)的運(yùn)作,因此要將這些職責(zé)進(jìn)行分離,將不同的職責(zé)封裝在不同的類(lèi)中,即將不同的變化原因封裝在不同的類(lèi)中。如果多個(gè)職責(zé)總是同時(shí)發(fā)生變化的話,則可以將它們封裝在同一個(gè)類(lèi)中。

也就是說(shuō),就一個(gè)類(lèi)而言,應(yīng)該只有一個(gè)引起它變化的原因,這就是單一職責(zé)原則,它是實(shí)現(xiàn)高內(nèi)聚、低耦合的指導(dǎo)方針。這是最簡(jiǎn)單也最難運(yùn)用的原則,需要開(kāi)發(fā)人員發(fā)現(xiàn)類(lèi)的不同職責(zé)并將其分離。

>>> 4.3.4 多態(tài)性

多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)重要特征,多態(tài)(函數(shù))的字面含義是具有多種形式。每個(gè)類(lèi)中操作的規(guī)約都是相同的,而這些類(lèi)可以用不同的方式實(shí)現(xiàn)這些同名的操作,從而使得擁有相同接口的對(duì)象可以在運(yùn)行時(shí)相互替換。

當(dāng)向一個(gè)對(duì)象發(fā)送一個(gè)消息時(shí),這個(gè)對(duì)象必須有一個(gè)定義的方法對(duì)這個(gè)消息作出響應(yīng)。在繼承層次結(jié)構(gòu)中,所有子類(lèi)都從其超類(lèi)繼承接口。由于每個(gè)子類(lèi)都是一個(gè)單獨(dú)的實(shí)體,它們可能需要對(duì)同一個(gè)消息作出不同的響應(yīng)。比如,Validator類(lèi)和行為validate。

在面向?qū)ο蟮木幊讨校嬲玫氖菑某橄箢?lèi)派生的類(lèi)的具體實(shí)例。當(dāng)通過(guò)抽象引用概念要求對(duì)象做什么時(shí),將得到不同的行為,具體行為取決于派生對(duì)象的具體類(lèi)型。因此,為了描述事物之間相同特性基礎(chǔ)上表現(xiàn)出來(lái)的可變性,于是多態(tài)就被創(chuàng)造出來(lái)了,多態(tài)允許用相同的方法(代碼)處理不同行為的對(duì)象。

多態(tài)是一種運(yùn)行時(shí)基于對(duì)象的類(lèi)型發(fā)生的綁定機(jī)制,通過(guò)這種機(jī)制實(shí)現(xiàn)函數(shù)名綁定到函數(shù)具體實(shí)現(xiàn)代碼的目的。當(dāng)執(zhí)行一個(gè)程序時(shí),構(gòu)成程序的各個(gè)函數(shù)分別在計(jì)算機(jī)的內(nèi)存中擁有了一段存儲(chǔ)空間,一個(gè)函數(shù)在內(nèi)存中的起始地址就是這個(gè)函數(shù)的入口地址,因此多態(tài)就是將函數(shù)名動(dòng)態(tài)綁定到函數(shù)入口的運(yùn)行時(shí)綁定機(jī)制。盡管多態(tài)與繼承緊密相關(guān),但通常多態(tài)被單獨(dú)看作面向?qū)ο蠹夹g(shù)最強(qiáng)大的一個(gè)優(yōu)點(diǎn)。

顯然,調(diào)用校驗(yàn)器就是發(fā)送一個(gè)消息,它要使用validate函數(shù)指針。實(shí)際上無(wú)論范圍值校驗(yàn)器還是奇偶校驗(yàn)器,其校驗(yàn)過(guò)程都是由不同內(nèi)容的函數(shù)實(shí)現(xiàn)的。在面向?qū)ο蟮木幊讨校诓煌念?lèi)中定義了其響應(yīng)消息的方法,那么在使用這些類(lèi)時(shí),則不必考慮它們是什么類(lèi)型,只要發(fā)布消息即可。正如在調(diào)用校驗(yàn)器時(shí),不必考慮其調(diào)用的它們是什么校驗(yàn)器,直接使用validate函數(shù)指針,無(wú)論什么類(lèi)型校驗(yàn)器都能實(shí)現(xiàn)檢查功能。

由于RangValidator和OddEvenValidator類(lèi)都繼承自Validator類(lèi),因此沒(méi)有必要在繼承樹(shù)中對(duì)每一種校驗(yàn)器都重復(fù)定義這些屬性和行為,重復(fù)不僅需要做更多的事情,甚至還可能導(dǎo)致錯(cuò)誤和出現(xiàn)不一致,詳見(jiàn)圖 4.5。這種關(guān)系在UML中表示為一條線,并有一個(gè)箭頭指向父類(lèi)。這種記法非常簡(jiǎn)明扼要,當(dāng)遇到這種帶箭頭的線時(shí),就知道存在一個(gè)繼承并呈現(xiàn)多態(tài)的關(guān)系。

圖 4.5 抽象類(lèi)的層次結(jié)構(gòu)

在設(shè)計(jì)Validator時(shí),對(duì)各種校驗(yàn)器的使用進(jìn)行標(biāo)準(zhǔn)化會(huì)有很大的幫助,因?yàn)闊o(wú)論是何種校驗(yàn)器,都用一個(gè)名為validate的方法。如果遵循這個(gè)規(guī)范,不管什么時(shí)候校驗(yàn)數(shù)據(jù),只需要調(diào)用validate方法即可。無(wú)需考慮這到底是什么校驗(yàn)器,于是就有了一個(gè)真正多態(tài)的Validator框架——由各個(gè)對(duì)象自己負(fù)責(zé)完成校驗(yàn),不論它是范圍值校驗(yàn)、奇偶校驗(yàn)還是質(zhì)數(shù)校驗(yàn)。

根據(jù)開(kāi)閉原則,需要再編寫(xiě)一個(gè)擴(kuò)展push功能的pushWithValidate()函數(shù),其原型如下:

如果需要進(jìn)行范圍值校驗(yàn),則pValidator指向rangeValidator,否則將pValidator置NULL。

pushWithValidate()的具體實(shí)現(xiàn)如下:

其調(diào)用形式如下:

使用通用校驗(yàn)器的應(yīng)用范例程序詳見(jiàn)程序清單 4.11。

程序清單 4.11 使用通用校驗(yàn)器的范例程序

由此可見(jiàn),雖然OOA和OOD的邊界是模糊的,但它們關(guān)注的重點(diǎn)不一樣。OOA關(guān)注的是分析面臨的問(wèn)題域,從問(wèn)題域詞匯表中發(fā)現(xiàn)類(lèi)和對(duì)象,實(shí)現(xiàn)對(duì)現(xiàn)實(shí)世界的建模。OOD關(guān)注的是如何設(shè)計(jì)泛化的抽象和一些新的機(jī)制,規(guī)定對(duì)象的協(xié)作方式。


聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 繼承
    +關(guān)注

    關(guān)注

    0

    文章

    10

    瀏覽量

    2730

原文標(biāo)題:周立功:關(guān)于繼承與多態(tài),你了解多少?

文章出處:【微信號(hào):ZLG_zhiyuan,微信公眾號(hào):ZLG致遠(yuǎn)電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    嵌入式C語(yǔ)言面向對(duì)象編程---多態(tài)

    前兩篇文章主要講述了 C 語(yǔ)言面向對(duì)象編程– 封裝和繼承。本篇文章繼續(xù)來(lái)討論一下,如何使用 C 語(yǔ)言實(shí)現(xiàn)面向
    發(fā)表于 10-31 14:41 ?1016次閱讀

    Python的面向對(duì)象編程詳解

    一般編程可分為面向過(guò)程編程,和面向對(duì)象編程。Python的
    發(fā)表于 09-04 16:35 ?580次閱讀
    Python的<b class='flag-5'>面向</b><b class='flag-5'>對(duì)象</b><b class='flag-5'>編程</b>詳解

    3分鐘看懂Python面向對(duì)象

    個(gè)Interface,但最多繼承自一個(gè)類(lèi))。多重繼承機(jī)制有時(shí)很好用,但是它容易讓事情變得復(fù)雜。多態(tài)多態(tài)意味著可以對(duì)不同的對(duì)象使用同樣的操作,
    發(fā)表于 06-08 14:20

    LabVIEW面向對(duì)象的ActorFramework(1)

    ` 本帖最后由 bollworm 于 2020-2-10 14:54 編輯 本系列文章主要闡述以下幾個(gè)問(wèn)題:(1)什么是面向對(duì)象編程?(2)為什么要學(xué)習(xí)面向
    發(fā)表于 02-10 14:09

    如何用C語(yǔ)言實(shí)現(xiàn)面向對(duì)象編程

    、組合、多態(tài)面向對(duì)象的功能,但C語(yǔ)言有struct和函數(shù)指針。我們可以用struct中的數(shù)據(jù)和函數(shù)指針,以此來(lái)模擬對(duì)象和類(lèi)的行為。所以在正式開(kāi)始設(shè)計(jì)模式前,先看看如何用C語(yǔ)言實(shí)現(xiàn)
    發(fā)表于 07-12 07:24

    談?wù)?b class='flag-5'>面向對(duì)象編程

    工業(yè)控制系統(tǒng)的PLC程序中也可以采用這種設(shè)計(jì)思想,雖然我們無(wú)法實(shí)現(xiàn)面向對(duì)象的很多優(yōu)秀特點(diǎn)如“繼承”,甚至于它根本就不具備面向對(duì)象
    發(fā)表于 09-08 07:47

    面向對(duì)象編程語(yǔ)言的特點(diǎn)

    工業(yè)控制系統(tǒng)的PLC程序中也可以采用這種設(shè)計(jì)思想,雖然我們無(wú)法實(shí)現(xiàn)面向對(duì)象的很多優(yōu)秀特點(diǎn)如“繼承”,甚至于它根本就不具備面向對(duì)象
    發(fā)表于 09-08 07:44

    面向對(duì)象編程介紹

    目錄一、面向對(duì)象編程介紹1.面向過(guò)程編程2.函數(shù)式編程3.
    發(fā)表于 12-13 07:22

    對(duì)RT-Thread內(nèi)核中的封裝繼承多態(tài)有關(guān)理解

    內(nèi)核中的封裝繼承多態(tài)RT-Thread 雖然是使用面向過(guò)程的 C 語(yǔ)言來(lái)編寫(xiě),但是處處都體現(xiàn)了面向對(duì)象
    發(fā)表于 04-13 17:41

    C#入門(mén)教程之面向對(duì)象編程簡(jiǎn)介的詳細(xì)資料概述

    本文檔的主要內(nèi)容詳細(xì)介紹的是C#入門(mén)教程之面向對(duì)象編程簡(jiǎn)介的詳細(xì)資料概述主要學(xué)習(xí)的目標(biāo)是1.面向對(duì)象編程
    發(fā)表于 12-05 11:54 ?35次下載
    C#入門(mén)教程之<b class='flag-5'>面向</b><b class='flag-5'>對(duì)象</b><b class='flag-5'>編程</b>簡(jiǎn)介的詳細(xì)資料概述

    plc面向對(duì)象編程架構(gòu)與實(shí)現(xiàn)

    面向對(duì)象編程是計(jì)算機(jī)高級(jí)語(yǔ)言的一種先進(jìn)的編程模式,在工業(yè)控制系統(tǒng)的PLC程序中也可以采用這種設(shè)計(jì)思想,雖然我們無(wú)法實(shí)現(xiàn)面向
    發(fā)表于 01-31 15:00 ?4264次閱讀
    plc<b class='flag-5'>面向</b><b class='flag-5'>對(duì)象</b><b class='flag-5'>編程</b>架構(gòu)與實(shí)現(xiàn)

    java的封裝繼承多態(tài)

    繼承是為了重用父類(lèi)代碼。兩個(gè)類(lèi)若存在IS-A的關(guān)系就可以使用繼承。,同時(shí)繼承也為實(shí)現(xiàn)多態(tài)做了鋪墊。那么什么是多態(tài)呢?
    發(fā)表于 10-15 10:05 ?1445次閱讀
    java的封裝<b class='flag-5'>繼承</b>和<b class='flag-5'>多態(tài)</b>

    面向對(duì)象開(kāi)發(fā)的SOLID五大基本原則

    做C語(yǔ)言開(kāi)發(fā)的應(yīng)該都知道,C是面向過(guò)程開(kāi)發(fā)的,而c++是面向對(duì)象開(kāi)發(fā)的。而封裝、繼承多態(tài)面向
    的頭像 發(fā)表于 10-17 14:31 ?791次閱讀

    多態(tài)性實(shí)現(xiàn)原理及其在面向對(duì)象編程中的應(yīng)用

    面向對(duì)象編程中,多態(tài)性是一個(gè)非常重要的概念。
    的頭像 發(fā)表于 06-08 14:19 ?634次閱讀

    淺談C語(yǔ)言面向對(duì)象編程思想

    C語(yǔ)言是一種面向過(guò)程的語(yǔ)言,但是也可以用結(jié)構(gòu)體和函數(shù)指針來(lái)模擬面向對(duì)象的特性,比如封裝、繼承多態(tài)
    發(fā)表于 11-02 12:27 ?1140次閱讀
    主站蜘蛛池模板: 老师小扫货水能么多叫出来| 亚洲视频网站欧美视频网站| 国产AV视频一区二区蜜桃| 性色AV一区二区三区咪爱四虎| 狠狠色丁香婷婷久久综合| FREE另类老女人| 亚洲午夜久久影院| 色婷婷激婷婷深爱五月小蛇| 久久久久久天天夜夜天天| 国产精品亚洲精品久久品| xxx军人3p大gay| 岳扒开让我添| 小p孩玩成年女性啪啪资源| 青青草原亚洲| 伦理片在线线看手机版| 黄色大片aa| 国产美女裸身网站免费观看视频| 把腿张开老子CAO烂你动态图| 2019精品国产品在线不卡| 亚洲精品国产一区二区贰佰信息网 | 年轻的女职工在线观看| 久久re6热在线视频精品| 国产免费麻传媒精品国产AV| 朝鲜女人性猛交| xiah俊秀| 99免费在线观看| 最近韩国日本免费观看mv免费版| 亚洲欧美日韩精品久久奇米色影视| 午夜dj免费中文字幕| 色综合99久久久国产AV| 日韩精品欧美在线视频在线| 欧美亚洲综合另类无码| 欧美极限扩肛| 欧美123区| 欧美日韩精品一区二区三区四区| 久久综合一个色综合网| 久久精品国产亚洲AV天美18| 久久99re2在线视频精品| 狠狠干.in| 饥渴的40岁熟妇完整版在线| 果冻传媒AV精品一区|