本章我們會(huì)開(kāi)發(fā)一個(gè)簡(jiǎn)單的游戲:點(diǎn)圖。隨著開(kāi)發(fā)流程我們會(huì)演示Squeak開(kāi)發(fā)過(guò)程中的大部分工具,演示開(kāi)發(fā)者之間如何交互。還會(huì)使用系統(tǒng)查看器,對(duì)象查看器,調(diào)試器,包管理器等工具。使用Smalltalk進(jìn)行開(kāi)發(fā)非常高效:其中大部分時(shí)間花在編寫代碼上,開(kāi)發(fā)流程的管理交給Squeak自動(dòng)完成。Smalltalk的語(yǔ)言簡(jiǎn)潔性和Squeak編程環(huán)境的豐富性。
2.1 點(diǎn)圖游戲
通過(guò)開(kāi)發(fā)一個(gè)簡(jiǎn)單的點(diǎn)圖游戲說(shuō)明如何使用Squeak的開(kāi)發(fā)工具。這個(gè)游戲的面板如圖。包含一系列的黃色塊組成,點(diǎn)擊其中的一個(gè),周圍的四個(gè)變成藍(lán)色,再次點(diǎn)擊,恢復(fù)到黃色。這個(gè)游戲的目的是盡可能多的轉(zhuǎn)換為藍(lán)色。

這個(gè)游戲包含兩類對(duì)象:一個(gè)是盒子對(duì)象,一個(gè)是100個(gè)獨(dú)立的塊對(duì)象。因此在Squeak需要通過(guò)代碼實(shí)現(xiàn)兩個(gè)類,一個(gè)是游戲盒子,一個(gè)是塊。接下來(lái)我們說(shuō)明如使用Squeak的開(kāi)發(fā)工具定義這兩個(gè)類。
2.2 創(chuàng)建新的類分類
我們將會(huì)在第一章介紹的系統(tǒng)查看器的基礎(chǔ)上,介紹如何定義新的類分類以及其中的類
Doit打開(kāi)系統(tǒng)查看器,右擊分類面板,選擇add item

在打開(kāi)的對(duì)話框中輸入新的分類名字SBE-Quinto。然后點(diǎn)擊accept或者輸入回車鍵。新的類分類創(chuàng)建。通常新的分類在最后,如何選擇了一個(gè)已存在的分類,那么將會(huì)創(chuàng)建在選擇的分類前面。
2.3 創(chuàng)建SEBCell類
這個(gè)新的分類中并沒(méi)有包含任何Class。在下面的主編輯區(qū)域包含了創(chuàng)建類的模板可以用作類快速創(chuàng)建模板
快速瀏覽其中的代碼,可以發(fā)現(xiàn)這個(gè)模板組織為發(fā)送消息給Object類,創(chuàng)建一個(gè)名為NameOfSubClass的子類。這個(gè)新的子類沒(méi)有任何變量,并且屬于SBE-Quinto類分類
我們就在這個(gè)模板的基礎(chǔ)上進(jìn)行修改創(chuàng)建我們需要的類
Doit 修改類創(chuàng)建模板
將Object名字替換為SimpleSwitchMorph
將NameOfSubClass替換為SBECell
添加mouseAction到類的實(shí)例變量中
修改結(jié)果如下

修改的代碼包含一個(gè)Smalltalk表達(dá)式。發(fā)送消息給SimpleSwitchMorph類,要求創(chuàng)建一個(gè)子類SBECell。因?yàn)镾BECell并不存在,需要傳遞一個(gè)參數(shù)#SBECell作為類的名字。
然后在新創(chuàng)建的類中包含一個(gè)mouseAction實(shí)例變量,將用來(lái)定義塊應(yīng)該采取的動(dòng)作在受到點(diǎn)擊的時(shí)候
當(dāng)前只是輸入代碼,并沒(méi)有編譯生成任何類。需要使用accept進(jìn)行編譯保存
Doit編譯保存創(chuàng)建的類,

空白地方右擊選擇accept。
創(chuàng)建成功后,新創(chuàng)建的類將會(huì)顯示在系統(tǒng)查看器的第二欄。編輯區(qū)域顯示類的定義,下面提示輸入一些注釋。
這個(gè)區(qū)域叫做類注釋(class comment)。在大型項(xiàng)目開(kāi)發(fā)中代碼注釋非常重要,Smalltalk高度重視代碼的可讀性和注釋的詳細(xì)。也就是說(shuō)代碼應(yīng)該是自說(shuō)明的。類的注釋并不需要太詳細(xì)的描述,僅僅需要一些用來(lái)說(shuō)明這個(gè)類的用途。

Doit 對(duì)類進(jìn)行注釋
2.4 為類添加方法
接下來(lái)我們?yōu)樾聞?chuàng)建的類添加一些方法
Doit選擇協(xié)議面板中的all。
在編輯主區(qū)域顯示一個(gè)方法創(chuàng)建模板,選擇編輯器區(qū)域,修改代碼為下

Doit選擇accept保存創(chuàng)建的方法
讓我們逐行解釋這些代碼的意義
這個(gè)方法名稱是initialize.這個(gè)名稱的意義非常重要。通常一個(gè)類定義個(gè)initizlize方法,在創(chuàng)建一個(gè)對(duì)象后用來(lái)初始化對(duì)象的數(shù)學(xué)。所以當(dāng)執(zhí)行SBECell new后,將會(huì)發(fā)生initialize消息給類用來(lái)創(chuàng)建新的對(duì)象。初始化方法initialize將會(huì)用來(lái)初始化對(duì)象的狀態(tài),通常用來(lái)設(shè)置對(duì)象的實(shí)例變量,接下來(lái)我們將要說(shuō)明。
這個(gè)方法中第一句就是執(zhí)行父類SimpleSwitchMorph類的初始化方法,因此SimpleSwitchMorph中繼承的屬性將會(huì)被初始化。通常使用super initialize來(lái)初始化繼承的屬性。我們?cè)谶@里不知道SimpleSwitchMorph的初始化方法我們也不用關(guān)心,可以確定的是會(huì)初始化一些實(shí)例變量來(lái)保存默認(rèn)值,因此最好首先調(diào)用父類初始化避免未知的變量
方法的其余語(yǔ)句用來(lái)設(shè)置對(duì)象的數(shù)學(xué),self label:''首先設(shè)置對(duì)象的label為空字符串
表達(dá)式0@0 corner:16@16需要一些說(shuō)明。0@0表示一個(gè)xy坐標(biāo)系的原點(diǎn)未知,事實(shí)上這個(gè)語(yǔ)句發(fā)送@0消息到數(shù)字對(duì)象0,接下來(lái)數(shù)字0創(chuàng)建一個(gè)點(diǎn)Point類的實(shí)例(0,0)坐標(biāo)。接著發(fā)送消息corner:16@16用來(lái)創(chuàng)建以0@0和16@16的矩形。然后將這個(gè)矩形賦值給bounds變量。這個(gè)變量是繼承自父類。
需要注意的是Squeak屏幕原點(diǎn)在左上,y軸向下遞減。
其余的語(yǔ)句也是用來(lái)設(shè)置屬性。
2.5 探測(cè)對(duì)象
接下來(lái)創(chuàng)建一個(gè)對(duì)象來(lái)進(jìn)行觀察
Doit打開(kāi)workspce。輸入SBECell new右擊選擇inspect it。
打開(kāi)一個(gè)查看器,顯示了SBECell的實(shí)例變量列表,選擇其中的一個(gè)如bounds。它的值將會(huì)顯示在右側(cè)面板??梢允褂眠@個(gè)查看器修改實(shí)例變量的值
DOit 修改bounds的值為0@0 corner:16@16然后保存accept。
查看器下面是一個(gè)迷你workspace。經(jīng)常用來(lái)對(duì)當(dāng)前對(duì)象進(jìn)行測(cè)試使用
Doit在其中輸入se;f openInWorld 然后do it
接下來(lái)會(huì)在窗口創(chuàng)建一個(gè)塊,中擊顯示器控制,縮放大小,觀察查看器,可以看見(jiàn)bounds的值也會(huì)發(fā)生變化
2.6 定義SBEGame類
接下來(lái)我們創(chuàng)建另一個(gè)游戲類SBEGame
Doit使用系統(tǒng)查看器創(chuàng)建類SBEGame。
選擇SBE-Quinto類分類,編輯類創(chuàng)建代碼如下

代碼意思創(chuàng)建BorderedMorph的子類。Morph是所有圖形類的超級(jí)父類,而B(niǎo)orderedMorph類是帶有邊框的子類。
然后在SBEGame中定義一個(gè)初始化方法initialize
Doit選擇all協(xié)議,輸入下面的代碼

選擇accept it的時(shí)候,Squeak會(huì)說(shuō)明其中一些短語(yǔ)缺少定義,其中一個(gè)是cellsPerSide。提供了一些建議選項(xiàng)來(lái)修正拼寫錯(cuò)誤。

然而cellsPerSide并不是一個(gè)錯(cuò)誤,僅僅是一個(gè)未定義的方法,接下來(lái)選擇創(chuàng)建它。
Doit選擇第一個(gè)選擇,確認(rèn)事宜cellSPerSide.
接著提示cells也是未知的,提供了修正選項(xiàng)
Doit選擇declare instance聲明一個(gè)實(shí)例變量
接下來(lái)會(huì)說(shuō)明NeCellAt:at:也是未知的,同樣確認(rèn)。
然后再次查看整個(gè)對(duì)象 會(huì)發(fā)現(xiàn)修改后的保護(hù)cells變量。
那么分析下initialize方法
第一行聲明四個(gè)臨時(shí)變量 sampleCell width height n
作用域與生命周期僅僅在這個(gè)方法,臨時(shí)變量有助于代碼的可讀性。
smalltalk并不會(huì)區(qū)分常量與變量,事實(shí)上這四個(gè)變量是常量。接下來(lái)的幾行定義了這些常量
那么board是多大。需要容納足夠的cells和邊界框。那么其中包含多少個(gè)塊呢,我們并不知道,因此需要使用cellsPerSide來(lái)。
這個(gè)方法會(huì)在后面進(jìn)行定義,這種編寫代碼的方式是一種非常良好的方式,因?yàn)楫?dāng)我們進(jìn)行初始化的時(shí)候才知道我們需要什么,然后我們給出有意義的方法名稱,不需要打斷我們的思路
接下來(lái)會(huì)獲取cell的數(shù)量賦值給n.
接下來(lái)創(chuàng)建SBECell對(duì)象,賦值高度與寬度,
接下來(lái)設(shè)置buonds屬性。
最后實(shí)則cells變量屬性,使用Matrix。
需要使用兩個(gè)參數(shù)i j。
創(chuàng)建一個(gè)nxn矩陣初始化元素。初始化值依賴于坐標(biāo)信息。
接受到初始化方法的時(shí)候,需要抓住機(jī)會(huì)格式化輸出。選擇more...>prettyprint會(huì)進(jìn)行自動(dòng)排版,需要再次保存accept
2.7 保存方法到協(xié)議中
讓我們快速瀏覽下第三個(gè)面板。正如第一個(gè)面板用來(lái)保存類分類,第三個(gè)面板保存方法分類的協(xié)議。
如果只有少量的方法,不需要使用特定協(xié)議,這也是為什么僅僅提供all協(xié)議,
Doit有偶記選擇categorize all uncategorized來(lái)修正,移動(dòng)初始化方法到初始化協(xié)議中
Squeak如何識(shí)別合適的協(xié)議接口?通常Squeak并不會(huì)進(jìn)行識(shí)別,但是這種情況initialize是一個(gè)父類的方法,假設(shè)我們的初始化方法應(yīng)該屬于父類的同樣協(xié)議接口類
可以發(fā)現(xiàn)Squeak會(huì)自動(dòng)將initialize方法歸類到initialization協(xié)議,
smalltalk使用>>來(lái)定義一個(gè)方法所屬的類,可以使用SBEGame>>cellsPerSide。
從現(xiàn)在開(kāi)始,我們將會(huì)使用這種方法說(shuō)明類的方法,
使用SBEGANME>>initialize方法定義方法
依次定義cellsPerSide ,newCellAt ,toggleNeighboursOfCellAt等方法
Doit將最后一個(gè)方法拖放到game logic協(xié)議中
2.8 試試運(yùn)行
到此完成所有代碼編寫
包含兩個(gè)類和7個(gè)方法
Doit 在workspace中輸入SBEGame new openInWorld,然后選擇do it
也許可能出現(xiàn)一個(gè)錯(cuò)誤提示窗口,
這是需要調(diào)試器進(jìn)行修正
Doit點(diǎn)擊debug按鈕

出現(xiàn)調(diào)試器界面,頂層窗口輸出運(yùn)行棧,顯示所有調(diào)用的方法。
選擇其中的一個(gè),將會(huì)顯示在中間。
其中錯(cuò)誤的代碼會(huì)高亮顯示
Doit選擇其中的第二條語(yǔ)句
調(diào)試器會(huì)顯示運(yùn)行的上下文如下。

調(diào)試器的下方是兩個(gè)探測(cè)窗口,左側(cè)顯示接受消息的對(duì)象,可以查看對(duì)象的各個(gè)實(shí)例變量的值
右側(cè)查看對(duì)象執(zhí)行當(dāng)前方法的狀態(tài),可以查找方法的參數(shù)值與臨時(shí)變量
使用調(diào)試,可以逐行運(yùn)行代碼,查看對(duì)象的形參與臨時(shí)變量,最為驚奇的是可以在調(diào)試過(guò)程中修改代碼。因此編程過(guò)程在調(diào)試中完成。這種調(diào)試方式可以查看編寫方法特定運(yùn)行上下文與形參的運(yùn)行狀態(tài),
分析代碼中的bug。