Objective-C——實現物件導向程式設計

出版圈郭志敏發表於2012-03-27

過程式程式建立在函式之上,資料為函式服務。物件導向程式設計從相反的角度來看待問題,它以程式的資料為中心,函式為資料服務。在OOP中,不再重點關注程式中的函式,而是專注於資料。

這聽起來非常有趣,但它如何工作呢?在OOP中,資料通過間接方式包含對自身操作的引用程式碼。不是通知drawRectangle()函式“使用這個形狀結構繪製矩形”,而是要求矩形“繪製自身”(天哪,聽起來太荒謬了,但事實上卻並不荒謬)。通過間接方式的威力,矩形資料知道如何查詢相應的函式繪製圖形。

那麼,物件到底是什麼呢?它其實是一種神奇的C struct。通常,該結構能通過函式指標查詢與之相關的程式碼。圖3-1展示了4種Shape物件:兩個正方形、一個圓形和一個橢圓形。每個物件都能查詢相應的函式並實現其繪圖功能。

enter image description here

每個物件都有自己的draw()函式,知道如何繪製特定的形狀。例如,Circle物件的draw()函式知道如何繪製圓形,Rectangle的draw()函式知道繪製由4條直線構成的矩形。

Shapes-Object程式(程式碼請參考03.10-Shapes-Object)可以完成與Shapes-Procedural相同的功能,但前者使用Objective-C的物件導向特性來實現。以下是Shapes-Object的drawShapes()程式碼:

enter image description here

該函式包含一個迴圈,用於輸出陣列的每種形狀。在迴圈過程中,程式通知形狀物件繪製自身。 注意區分該形式的drawShapes()與原始版本有何不同。首先,本函式更簡短!程式碼不必詢問每個形狀的種類。

另一個不同點是函式的第一個引數shapes[],此刻它是一個id陣列物件。什麼是id?它是與大腦有關的心理學術語,用於描述天生的本能衝動和原始過程嗎?在本例中並非表示此意,它代表identifier(識別符號)。id是一種泛型,用於表示任何種類的物件。回憶一下,物件是帶有程式碼的C struct。因此,id實際上是一個指標,指向其中的某個結構。在本例中,結構由各種形狀構成。

drawShapes()函式的第三個變化是迴圈主體:

enter image description here

第一行看上去像普通的C語言。程式碼從shapes陣列獲取id(即指向某個物件的指標),並將其賦值給名為shape(它具有型別id)的變數。這只是一種指標賦值過程,它實質上並不會複製shape的全部內容。看看圖3-2,注意Shapes-Object中各種可用的形狀。shapes[0]是一個指向紅色圓形的指標,shapes1是一個指向綠色矩形的指標,shapes2是個指向藍色橢圓形的指標。

enter image description here

現在,我們看看函式的最後一行程式碼:

enter image description here

非常奇怪吧。這是怎麼回事呢?我們知道,C使用方括號引用陣列元素,但在此我們並沒有使用陣列實現任何功能。在Objective-C中,方括號還有其他意義:它們用於通知某個物件該做什麼。在方括號內,第一項是物件,其餘部分是你需要物件執行的操作。在本例中,我們通知名稱為shape的物件執行draw操作。如果shape是圓形,我們會得到圓形;如果shape是矩形,我們會得到矩形。

在Objective-C中,通知物件執行某種操作稱為傳送訊息(有些人也將其稱為“呼叫方法”)。程式碼[shape draw]表示向shape物件傳送draw訊息。[shape draw]可以理解成“向shape傳送draw”。至於形狀如何實際繪製圖形,則取決於shape的實現。

向物件傳送訊息時,如何呼叫必要的程式碼呢?這是通過幕後名為類的幫手來協助完成的。

請看圖3-3。該圖的左側展示了shapes陣列中索引為0的circle物件,該物件最近在圖3-2中出現過。circle物件含有一個指向其類的指標。類是一種結構,用於描述該種類物件的構造。在圖3-3中,Circle類含有一個指標指向用於繪製圓形、計算圓形的面積以及實現其他必要功能的程式碼。

enter image description here

類物件有什麼用呢?就讓每個物件直接指向它的程式碼不是更簡單嗎?確實是更簡單一些,而且某些OOP系統也是那樣做的。但是,擁有類物件會具備極大的優勢:如果在執行時改變某個類,則該類的所有物件會自動繼承這些變化(我們將在後面各章中進一步討論該內容)。

圖3-4展示了draw訊息經過怎樣的過程最終呼叫了circle物件中適當的函式。

以下是圖3-4中展示的步驟。

(1) 物件是訊息(圖中的圓形)的目標,需要查詢它,看看它屬於什麼類。

(2) Circle類瀏覽其程式碼,查詢draw函式的位置。

(3) 找到draw函式後,將執行繪製圓形的函式。

圖3-5展示了基於陣列中的第二個形狀(它是一個綠色的矩形)呼叫[shape draw]時的情況。

enter image description here

enter image description here

圖3-5中使用的步驟和圖3-4中的步驟幾乎相同。

(1) 查詢訊息(圖中的矩形)的目標物件,看看它屬於什麼類。

(2) Rectangle類查詢其程式碼塊,然後獲取draw函式的地址。

(3) Objective-C執行可繪製矩形的程式碼。

該程式展示了一些非常棒的間接操作!在該程式的過程式版本中,我們必須編寫程式碼來決定要呼叫哪個函式。現在,可以由Objective-C在幕後作出決定,它將查詢物件屬於哪個類。這可以降低呼叫錯誤函式的機率,同時使程式程式碼更易於維護。

相關文章