活在當下的程式員應該都聽過“面向對象程式設計”一詞,也經常有人問能不能用一句話解釋下什麼是“面向對象程式設計”,我們先來看看比較正式的說法。
把一組資料結構和處理它們的方法組成對象(object),把相同行為的對象歸納為類(class),通過類的封裝(encapsulation)隐藏内部細節,通過繼承(inheritance)實作類的特化(specialization)和泛化(generalization),通過多态(polymorphism)實作基于對象類型的動态分派。
這樣一說是不是更不明白了。是以我們還是看看更通俗易懂的說法,下面這段内容來自于知乎。
說明: 以上的内容來自于網絡,不代表作者本人的觀點和看法,與作者本人立場無關,相關責任不由作者承擔。
之前我們說過“程式是指令的集合”,我們在程式中書寫的語句在執行時會變成一條或多條指令然後由CPU去執行。當然為了簡化程式的設計,我們引入了函數的概念,把相對獨立且經常重複使用的代碼放置到函數中,在需要使用這些功能的時候隻要調用函數即可;如果一個函數的功能過于複雜和臃腫,我們又可以進一步将函數繼續切分為子函數來降低系統的複雜性。但是說了這麼多,不知道大家是否發現,所謂程式設計就是程式員按照計算機的工作方式控制計算機完成各種任務。但是,計算機的工作方式與正常人類的思維模式是不同的,如果程式設計就必須得抛棄人類正常的思維方式去迎合計算機,程式設計的樂趣就少了很多,“每個人都應該學習程式設計”這樣的豪言壯語就隻能說說而已。當然,這些還不是最重要的,最重要的是當我們需要開發一個複雜的系統時,代碼的複雜性會讓開發和維護工作都變得舉步維艱,是以在上世紀60年代末期,“軟體危機”、“軟體工程”等一系列的概念開始在行業中出現。
當然,程式員圈子内的人都知道,現實中并沒有解決上面所說的這些問題的“銀彈”,真正讓軟體開發者看到希望的是上世紀70年代誕生的Smalltalk程式設計語言中引入的面向對象的程式設計思想(面向對象程式設計的雛形可以追溯到更早期的Simula語言)。按照這種程式設計理念,程式中的資料和操作資料的函數是一個邏輯上的整體,我們稱之為“對象”,而我們解決問題的方式就是建立出需要的對象并向對象發出各種各樣的消息,多個對象的協同工作最終可以讓我們構造出複雜的系統來解決現實中的問題。
說明: 當然面向對象也不是解決軟體開發中所有問題的最後的“銀彈”,是以今天的進階程式設計語言幾乎都提供了對多種程式設計範式的支援,Python也不例外。
類和對象
簡單的說,類是對象的藍圖和模闆,而對象是類的執行個體。這個解釋雖然有點像用概念在解釋概念,但是從這句話我們至少可以看出,類是抽象的概念,而對象是具體的東西。在面向對象程式設計的世界中,一切皆為對象,對象都有屬性和行為,每個對象都是獨一無二的,而且對象一定屬于某個類(型)。當我們把一大堆擁有共同特征的對象的靜态特征(屬性)和動态特征(行為)都抽取出來後,就可以定義出一個叫做“類”的東西。
定義類
在Python中可以使用class關鍵字定義類,然後在類中通過之前學習過的函數來定義方法,這樣就可以将對象的動态特征描述出來,代碼如下所示。
說明: 寫在類中的函數,我們通常稱之為(對象的)方法,這些方法就是對象可以接收的消息。
建立和使用對象
當我們定義好一個類之後,可以通過下面的方式來建立對象并給對象發消息。
通路可見性問題
對于上面的代碼,有C++、Java、C#等程式設計經驗的程式員可能會問,我們給Student對象綁定的name和age屬性到底具有怎樣的通路權限(也稱為可見性)。因為在很多面向對象程式設計語言中,我們通常會将對象的屬性設定為私有的(private)或受保護的(protected),簡單的說就是不允許外界通路,而對象的方法通常都是公開的(public),因為公開的方法就是對象能夠接受的消息。在Python中,屬性和方法的通路權限隻有兩種,也就是公開的和私有的,如果希望屬性是私有的,在給屬性命名時可以用兩個下劃線作為開頭,下面的代碼可以驗證這一點。
但是,Python并沒有從文法上嚴格保證私有屬性或方法的私密性,它隻是給私有的屬性和方法換了一個名字來“妨礙”對它們的通路,事實上如果你知道更換名字的規則仍然可以通路到它們,下面的代碼就可以驗證這一點。之是以這樣設定,可以用這樣一句名言加以解釋,就是“We are all consenting adults here”。因為絕大多數程式員都認為開放比封閉要好,而且程式員要自己為自己的行為負責。
在實際開發中,我們并不建議将屬性設定為私有的,因為這會導緻子類無法通路(後面會講到)。是以大多數Python程式員會遵循一種命名慣例就是讓屬性名以單下劃線開頭來表示屬性是受保護的,本類之外的代碼在通路這樣的屬性時應該要保持慎重。這種做法并不是文法上的規則,單下劃線開頭的屬性和方法外界仍然是可以通路的,是以更多的時候它是一種暗示或隐喻
面向對象的支柱
面向對象有三大支柱:封裝、繼承和多态。後面兩個概念在下一個章節中進行詳細的說明,這裡我們先說一下什麼是封裝。我自己對封裝的了解是“隐藏一切可以隐藏的實作細節,隻向外界暴露(提供)簡單的程式設計接口”。我們在類中定義的方法其實就是把資料和對資料的操作封裝起來了,在我們建立了對象之後,隻需要給對象發送一個消息(調用方法)就可以執行方法中的代碼,也就是說我們隻需要知道方法的名字和傳入的參數(方法的外部視圖),而不需要知道方法内部的實作細節(方法的内部視圖)。
練習
練習1:定義一個類描述數字時鐘
練習2:定義一個類描述平面上的點并提供移動點和計算到另一個點距離的方法。