呂蘭蘭
(湖南科技學(xué)院電子與信息工程學(xué)院,永州 425199)
面向?qū)ο蟪绦蛟O(shè)計(jì)主要講授使用C++來(lái)進(jìn)行面向?qū)ο蟪绦蛟O(shè)計(jì)。文獻(xiàn)[1]以四個(gè)面向來(lái)表現(xiàn)C++的本質(zhì):面向過(guò)程、面向泛型、基于對(duì)象、面向?qū)ο?。與基于對(duì)象程序設(shè)計(jì)相比,面向?qū)ο蟪绦蛟O(shè)計(jì)的編程理念更為先進(jìn)也更為復(fù)雜,學(xué)生不容易區(qū)分二者的差別。學(xué)生雖然學(xué)習(xí)了封裝、繼承、多態(tài)等面向?qū)ο筇匦栽贑++中的語(yǔ)法,但是在設(shè)計(jì)程序解決具體問(wèn)題時(shí),雖然編寫(xiě)的C++程序中定義了類,卻很少利用C++的繼承和多態(tài)來(lái)提高代碼重用。不難發(fā)現(xiàn),文獻(xiàn)[2]中給出的猜數(shù)字游戲的面向?qū)ο蠼鉀Q方案,從嚴(yán)格意義上來(lái)說(shuō)其實(shí)是一個(gè)基于對(duì)象的解決方案,因?yàn)槠渲胁](méi)有用到繼承和多態(tài)。因此,如何引導(dǎo)學(xué)生實(shí)現(xiàn)“從基于對(duì)象到面向?qū)ο蟮某绦蛟O(shè)計(jì)”的轉(zhuǎn)化,成為講授面向?qū)ο蟪绦蛟O(shè)計(jì)必須解決的重要問(wèn)題之一。
在面向?qū)ο蟮某绦蛟O(shè)計(jì)方法中,將各種事物稱為“對(duì)象”。將同一類事物的共同特點(diǎn)概括出來(lái),這個(gè)過(guò)程就稱為“抽象”[3]。對(duì)象的抽象包括兩個(gè)方面:屬性和方法。在猜數(shù)字游戲中,出現(xiàn)了游戲、人類玩家、電腦玩家等對(duì)象。答案是游戲?qū)ο蟮囊粋€(gè)屬性;人類玩家和電腦玩家猜測(cè)的數(shù),也可作為它們的屬性。游戲?qū)ο缶哂小芭袛噍斱A”、“開(kāi)始游戲”等方法,而人類玩家和電腦玩家對(duì)象則具有“猜數(shù)”等方法。
在完成抽象后,通過(guò)某種語(yǔ)法形式,將屬性和方法在形式上寫(xiě)成一個(gè)整體,即“類”,這個(gè)過(guò)程就稱為“封裝”[3]。在猜數(shù)字游戲中,經(jīng)過(guò)封裝可以得到3個(gè)類:游戲類、人類玩家類和電腦玩家類。
猜數(shù)字游戲中的人類玩家和電腦玩家,它們具有許多共性,例如它們都具有“猜數(shù)”這一方法以及所猜的“數(shù)”這一屬性。同時(shí),它們也分別具有一些特性,例如人類玩家可以根據(jù)當(dāng)前猜數(shù)結(jié)果自動(dòng)調(diào)整猜數(shù)范圍,而電腦玩家則需通過(guò)“更新范圍”的方法達(dá)到這一目標(biāo)。如何在描述兩類玩家各自特性的同時(shí),避免對(duì)它們的共性進(jìn)行重復(fù)描述呢?這就可以借助面向?qū)ο蠓椒ㄖ械睦^承與派生了。所謂繼承,就是從先輩處得到屬性和行為特征[4]。因此,可以創(chuàng)建一個(gè)新的類——玩家類,用它來(lái)描述兩類玩家的共性,再將人類玩家類和電腦玩家類作為玩家類的派生類,這樣就能夠很好地描述兩類賬戶的共性和各自的特性。
所謂多態(tài)具體是指,由繼承而產(chǎn)生的相關(guān)的不同的類,其對(duì)象對(duì)同一消息會(huì)做出不同的響應(yīng)[5]。猜數(shù)字游戲中的人類玩家和電腦玩家,是從玩家類派生出的兩個(gè)相關(guān)但不同的子類,它們?cè)诮邮盏健安聰?shù)”這一消息后,人類玩家可由用戶直接從鍵盤輸入所猜的數(shù),而電腦玩家則只能產(chǎn)生一個(gè)隨機(jī)數(shù)作為要猜的數(shù)。顯然,它們針對(duì)同一消息所做出的響應(yīng)是不同的,這就是多態(tài)。因此,在C++中可以通過(guò)將玩家類中的“猜數(shù)”這一方法聲明為虛函數(shù),并在人類玩家類和電腦玩家類中重新定義“猜數(shù)”方法,達(dá)到實(shí)現(xiàn)多態(tài)的目的。
經(jīng)測(cè)試,文獻(xiàn)[2]中“面向?qū)ο蠼鉀Q方案”中實(shí)現(xiàn)的C++程序,其運(yùn)行結(jié)果與文獻(xiàn)[2]中“教學(xué)案例:猜數(shù)字游戲”部分給出的程序運(yùn)行樣例有本質(zhì)區(qū)別。在程序?qū)嶋H運(yùn)行結(jié)果中,可能會(huì)出現(xiàn)以下情況:人類玩家Human首先猜了一個(gè)數(shù)50,程序提示“太低”,但緊接著電腦玩家猜了一個(gè)數(shù)16。這顯示電腦玩家很“笨”。不難發(fā)現(xiàn),這是由于電腦玩家每次只會(huì)隨機(jī)猜一個(gè)數(shù)而造成的。為了提高電腦玩家的游戲智能,必須使電腦玩家可以根據(jù)雙方已經(jīng)猜過(guò)的數(shù)來(lái)調(diào)整自己的猜數(shù)范圍。因此,可以啟發(fā)學(xué)生進(jìn)一步精化文獻(xiàn)[2]的交互建模[6]和設(shè)計(jì)建模[6],并按照精化的設(shè)計(jì)類圖重新編程實(shí)現(xiàn)猜數(shù)字游戲,這樣就可以完善猜數(shù)字游戲的基于對(duì)象解決方案。
如上所述,要提高電腦玩家的游戲智能,必須要將游戲雙方已經(jīng)猜過(guò)的數(shù)以及猜的結(jié)果通知電腦玩家,尤其是人類玩家“:Human”的猜數(shù)情況。此時(shí),猜數(shù)字游戲中游戲?qū)ο蟆埃篏ame”、人類玩家“:Human”、電腦玩家“:Computer”這三個(gè)對(duì)象之間的交互情況與文獻(xiàn)[2]有所不同。可以使用UML順序圖表達(dá)精化交互建模的結(jié)果,如圖1所示。
圖1 猜數(shù)字游戲順序圖
從圖 1中可以看到,游戲?qū)ο蟆埃篏ame”在每次check 之后,均向電腦玩家“:Computer”發(fā)送了一條 up?date消息來(lái)通知電腦玩家“:Computer”來(lái)及時(shí)更新自己猜數(shù)時(shí)的下限和上限。
圖1中新增的兩條update消息均由游戲?qū)ο蟆埃篏ame”發(fā)出,且均由電腦玩家“:Computer”接收。按照設(shè)計(jì)建模的原則,對(duì)于順序圖中的每一條消息,接收該消息的對(duì)象需要提供相應(yīng)的方法來(lái)響應(yīng),從而獲得每一個(gè)類的職責(zé)和屬性以及類之間的關(guān)系。因此,需在Computer類中添加相應(yīng)的update()方法來(lái)處理接收到的消息??墒褂肬ML設(shè)計(jì)類圖表達(dá)精化設(shè)計(jì)建模的結(jié)果,如圖2所示。
圖2 猜數(shù)字游戲設(shè)計(jì)類圖
需要注意的是,圖2中除了Computer類新增了update()方法來(lái)更新猜數(shù)范圍,還在Computer類中新增了2個(gè)private屬性high和low用于表示猜數(shù)范圍的上限和下限。同時(shí),圖2中Game類的check()方法的返回值類型由bool改為int,用于表示猜數(shù)字游戲結(jié)果中的猜高了(1)、猜對(duì)了(0)和猜低了(-1)。對(duì)部分學(xué)生來(lái)講,這些可能要到類的代碼實(shí)現(xiàn)階段才能想到。
根據(jù)圖2可以方便地進(jìn)行類的代碼實(shí)現(xiàn)。下面僅給出電腦玩家類Computer的完整代碼,以及游戲類的部分代碼。
在上述程序中,即使人類玩家故意忽視游戲提示亂猜,電腦玩家依然能夠不受干擾地猜一個(gè)合理的數(shù)。例如,在游戲雙方猜的50和30都提示“太高”的情況下,人類玩家Human故意猜一個(gè)數(shù)70,緊接著電腦玩家猜的數(shù)4卻是小于30的。這表明電腦玩家在更新自己猜數(shù)范圍的下限和上限時(shí),能夠人類玩家猜的數(shù)是否處于合理范圍內(nèi)做出正確判斷。這樣,電腦玩家已經(jīng)變得和人類玩家一樣“聰明”。
之所以將上述解決方案稱為“精化的基于對(duì)象解決方案”,是因?yàn)槠渲胁](méi)有用到繼承和多態(tài)特性。下面將利用繼承和多態(tài)特性,進(jìn)一步引導(dǎo)學(xué)生在目前構(gòu)造的基于對(duì)象的猜數(shù)字游戲程序基礎(chǔ)上,設(shè)計(jì)并實(shí)現(xiàn)面向?qū)ο蟮牟聰?shù)字游戲。
如1.3中所述,不管是人類玩家,還是電腦玩家,都是猜數(shù)字游戲中的玩家,因此可以將二者的共性抽象出來(lái),封裝在玩家Player類中,且Player類中有一個(gè)public方法guess()和一個(gè)protected屬性num。利用類的繼承特性,以玩家Player類作為基類,從Player類派生出人類玩家Human類和電腦玩家Computer類。可使用UML設(shè)計(jì)類圖表達(dá)精化設(shè)計(jì)建模的結(jié)果,如圖3所示。
圖3 猜數(shù)字游戲設(shè)計(jì)類圖
通過(guò)將游戲類Game中的play()方法的原型做如下更改:
void play(Player&p1,Player&p2);
就可將猜數(shù)字游戲從“人機(jī)對(duì)戰(zhàn)”模式擴(kuò)展到“人人對(duì)戰(zhàn)”、“機(jī)機(jī)對(duì)戰(zhàn)”模式,如下所示:
根據(jù)圖3可以方便地進(jìn)行類的代碼實(shí)現(xiàn)。下面僅給出玩家類Player的完整代碼,以及游戲類Game的部分代碼。
需要說(shuō)明的是,Game類的play()方法的實(shí)現(xiàn)對(duì)于部分學(xué)生來(lái)講可能是一個(gè)難點(diǎn),尤其是在第一個(gè)玩家p1和第二個(gè)玩家p2分別猜完數(shù)字之后,均要使用下列語(yǔ)句通知游戲雙方:
本文以學(xué)生熟悉的猜數(shù)字游戲作為案例,闡述了該案例的基于對(duì)象解決方案與面向?qū)ο蠼鉀Q方案,并在兩種解決方案的設(shè)計(jì)過(guò)程中融合了基于UML的面向?qū)ο筌浖<夹g(shù),以圖形化的方式直觀表達(dá)了從基于對(duì)象到面向?qū)ο蟮倪^(guò)渡。目前,該案例已在我校軟件工程專業(yè)2015級(jí)和2016級(jí)學(xué)生中進(jìn)行了2學(xué)期的教學(xué)實(shí)踐。我們發(fā)現(xiàn),通過(guò)引入U(xiǎn)ML,分別有大約95%、75%和90%的學(xué)生獨(dú)立實(shí)現(xiàn)了該案例的基于對(duì)象方案、基于對(duì)象精化方案和面向?qū)ο蠓桨?,各?xiàng)數(shù)據(jù)較之前均有大幅上升。但是,基于對(duì)象精化方案本身屬于進(jìn)階內(nèi)容。為了降低難度,也可跳過(guò)該方案、直接對(duì)文獻(xiàn)[2]的基于對(duì)象方案進(jìn)行面向?qū)ο蟾倪M(jìn)。對(duì)大部分學(xué)生而言,要達(dá)到熟練運(yùn)用繼承和多態(tài)實(shí)現(xiàn)代碼重用的程度,實(shí)現(xiàn)從基于對(duì)象程序設(shè)計(jì)到面向?qū)ο蟪绦蛟O(shè)計(jì)的跨越,還需要設(shè)計(jì)更多更好的能貫穿基于對(duì)象和面向?qū)ο蟮陌咐?。而在不同難度案例的選擇和設(shè)計(jì)上,仍然需要進(jìn)行進(jìn)一步的研究與探索。