面向物件程式設計/物件
簡單來說,術語物件用於與現實世界中的物件,例如椅子或吉他,產生聯絡。只是對於軟體來說,只使用了一些簡化的抽象,這些抽象是專門為手頭的任務而設計的。雖然真正的椅子是由原子和分子組成的,並且根據物理定律及其原子組成對周圍環境做出反應,但“椅子物件”會根據您是編寫遊戲還是為傢俱店編寫銷售點系統而有很大差異。從椅子的角度來解決問題不如從問題的角度來解決問題有效率。
在本手冊中使用的多數語言中,您會發現從技術角度來說(有點像吹口哨 9600 波特,我明白),物件是“類的例項”。太好了,那是什麼意思呢?好吧,我們可以追溯到柏拉圖及其柏拉圖理念。如果您在觀看《比爾和泰德的精彩冒險》上花費的時間比閱讀柏拉圖的時間更多,那麼這個想法是椅子的概念是獨立於任何特定椅子的一個實體。換句話說,您可以抽象地想到椅子,而不必想到任何特定的椅子。
在大多數 OOP 語言中,這種抽象的椅子概念被稱為類(來自分類),它是實際製造椅子的原型或藍圖。從藍圖中製作某物的行為通常稱為例項化,而製作的東西既是物件,也是用作藍圖的類的例項。作為人類,我們通常傾向於反過來做——我們對遇到的物件進行分類。我們很容易識別我們遇到的像椅子的東西是椅子;這種分類是我們最初得到類這個詞的地方。
很容易陷入關於物件性的深奧哲學爭論;在某些領域,例如知識表示和計算本體論,這些爭論非常重要。但是,對於計算機程式設計師來說,只需要弄清楚您的應用程式需要知道哪些關於椅子的資訊以及需要用椅子做什麼。這可能是一個足夠困難的問題,通常沒有必要使其變得更難!
如果您仍然對整個想法感到模糊,請考慮更技術的解釋。結構體(結構)、記錄、表格以及組織相關資訊的其他方式早於面向物件程式設計。您可能熟悉以下Pascal 程式碼
TYPE chair = RECORD
model : integer;
weight : integer;
height : integer;
color : COLOR;
END;
這實際上並沒有建立一個椅子變數,而是定義了在您建立椅子變數時它的外觀。您可以繼續建立椅子陣列等等,正如我們希望您自己發現的那樣,這種事情對於使程式易於理解是不可或缺的。面向物件程式設計想要推動這種優勢,並儘可能地利用它來獲得可理解性、正確性和簡單性。
一項基本的問題解決技術是分而治之——您只需要弄清楚如何將問題劃分為子問題。OOP 創新者意識到,我們已經找到了將問題劃分的方法,並且它反映在我們組織資料的方式中,就像上面一樣。如果您檢視包含椅子 RECORD 的應用程式,您一定會發現很多用於處理椅子的程式碼。為什麼還要費心去定義它呢?因此,如果您要從整個應用程式中提取所有這些程式碼並將其與椅子定義放在一起,那麼推理就認為,您應該更容易確保
- 所有椅子程式碼都是正確的
- 所有椅子程式碼彼此一致
- 沒有重複的椅子程式碼
- 總的來說,您的義大利麵條程式碼更少,因為椅子程式碼不再與沙發程式碼等糾纏在一起
因此,您將椅子定義和從整個應用程式中提取的程式碼放在一起,並將其稱為類。獲取一個椅子變數並將其稱為物件,然後您就可以開始執行了。
由於這應該是一本實用的書籍,讓我們看一下我們的第一個程式碼示例,這次是 Python 程式碼
class Chair:
model = None
height = None
weight = None
color = None
def has_arms(self):
return self.model % 2 # odd-numbered models have armrests
這看起來與 Pascal 示例並沒有太大區別。唯一的區別是類 Chair 現在包含一個has_arms 方法。方法是在類中存在的函式,通常用於處理某些特定於類的的資料。希望其目的相當清楚,所以讓我指出重要的部分:has_arms 是一個計算或推斷屬性——此資訊不是直接儲存的。
在 Python 中,您可以這樣使用此類
c = Chair()
c.model = 15
c.height = 40
c.weight = 10
c.color = 7
if c.has_arms():
do_something
else:
do_other_thing
在這裡,“c”變數是“chair”類的例項,也稱為椅子物件。我們像在 Pascal 中一樣初始化屬性(這將在稍後更改!),如果椅子有扶手,我們就執行一件事,如果沒有扶手,我們就執行另一件事。但我們從未初始化“has_arms”屬性或類似的東西。
由於目標之一是讓您成為與語法無關的程式設計師,因此我們再次在 C++ 中展示了相同的示例
class Chair
{
public:
int model;
int weight;
int height;
int color;
bool has_arms()
{
return model % 2 ? true : false;
}
};
Chair c;
c.model = 15;
c.height = 40;
c.weight = 10;
c.color = 7;
if (c.has_arms())
do_something();
else
do_other_thing();
現在,我們只是想提一下,這僅僅是冰山一角,這個例子並不代表好的風格(在任何一種語言中)。建立功能如此少的物件似乎沒有多大意義,您可以輕鬆地生成不需要新類的等效程式碼
struct Chair
{
int model;
int weight;
int height;
int color;
} c;
Chair c = {15, 40, 10, 7 };
if (c.model % 2)
do_something();
else
do_other_thing();
本節的目的是幫助您理解這些術語;我們將在封裝、多型性、繼承等部分深入探討其優勢。但是,讓我們說,雖然“低階”方式看起來可能更短更簡單,但面向物件的優勢隨著程式大小和複雜性的增加而增加。這並不奇怪,因為 OOP 就是為此而設計的,但這確實使簡單的示例難以找到。所以請耐心等待。
如果您不熟悉狀態機,我們不會深入探討它們,但我們強烈建議您進一步研究它們。這有利於您的思維過程的清晰度。嚴格地說,狀態機是相當複雜的數學結構,使用數學符號可以推匯出許多有趣的結論。但這不是我們的興趣所在。我們的興趣是雙重的
- 一方面,狀態機提供了一種清晰的圖形化方式來說明可能複雜的應用程式邏輯。首先,確定您的可能狀態是什麼。例如,伺服器可以是 IDLE、CONNECTED、BUSY、ERROR 等。然後,定義所有可能的輸入,即客戶端可能傳送的訊息,併為每個狀態圖繪製伺服器對每個輸入的響應。當您考慮在各種狀態下對意外訊息的反應時,這有助於促進徹底性。
- 另一方面,狀態機是一種標準工具,具有相對標準的符號。有一些工具和庫會根據某些規範狀態機格式作為輸入自動生成程式碼。這實際上是正則表示式處理器所做的。
在面向物件程式設計中,“物件”有時是狀態機的實現。狀態機是行為實體(伺服器、正則表示式處理器、圖形引擎和虛擬機器)的更好的概念基礎。
簡單物件和行為實體之間的一個基本區別是,簡單物件不會根據其值改變其行為。事實上,我們傾向於將其稱為值而不是狀態。簡單物件往往儲存在集合中並被搜尋,傳遞到函式和從函式返回,並且通常像值一樣起作用。字串和矩形是經典的例子。稍後,我們將討論訊息傳遞作為方法呼叫的抽象,在簡單物件的上下文中,它可能看起來有點奇怪。
如果您仍然難以理解物件的 concept,請下載並使用BlueJ,大多數人在使用該 IDE 幾小時內就會“明白”。