# 何謂物件導向程式設計 物件導向程式設計(Object-Oriented Programming,縮寫為OOP) 「物件導向」是我們每天身在其中已經太過熟悉,以至於平常根本不會把它拿出來特別討論的事情。我們日常生活無時無刻都是物件導向,就像呼吸一樣自然而然,例子多不勝數。例如,我現在坐在電腦前面一邊打字一邊聽音樂,用物件導向的方式來表達的話,就像下面這個樣子: ![](https://i.imgur.com/Y0hYPnO.png) 「我」是個物件、電腦、鍵盤、滑鼠、螢幕、喇叭 … 也都是物件,這個例子就是「我」這個物件和其他物件彼此之間進行互動的過程。 而「人類」是個類別(class),因為地球上的人口有七十幾億,如果不清楚定義出到底是哪一個人在打字,要怎麼開始互動?同理,電腦、滑鼠、鍵盤、螢幕、喇叭 … 也都只是類別(class),還要再指出它的具體名稱,才知道你到底用的是地球上超多台電腦的哪一台,使用的是哪一個鍵盤、滑鼠、螢幕…。之後,把每個物件的屬性和使用方法詳細定義出來,就可以把這些物件集合起來,以物件彼此之間的(使用)方法(method)來做互動了,這就是物件導向的核心觀念。 而現在我們做的事情是使用電腦打字和聽音樂,所以我穿什麼顏色的衣服、聽的是哪一首歌、桌上放的是什麼飲料 … 這些資訊對目前要做的事情都沒有意義,可以不用描述,只要把有意義的東西彙整起來就好了。 既然說到這裡,就順帶釐清一些術語,即使是資深的程式設計師可能也經常搞不懂個體(instance)和物件(object)之間的區別,也就是經常在文章上反覆看到這兩個術語,但卻仍然一知半解。 個體(instance)和物件(object)的意思其實差不多,但它們是「相對性」的。例如你從整部車的角度來看,把整部車子當作一個個體(instance),那組成車子的引擎、方向盤、油壓系統、電路、輪子、座椅…等系統都會被你當作是物件(object)。但若你再把引擎當作是一個個體,則噴油嘴、汽缸、連桿、活塞、火星塞 … 等就會被視作物件。而如果你把道路系統當作一個個體,則路上行駛的每部車子就成了許多物件了。由此可見,依據個人主觀的觀點,這種關係會隨時動態的改變。 但將這種關係反過來說卻不一定正確。例如不同廠牌和不同型號的車子,引擎排氣量和設計都會不同,因此你可以說車子都有引擎(或任何種類的動力系統),但卻不能說每部車子的引擎都一樣。所以,一個個體必定是一個物件,但一個物件卻不一定是一個個體。  註:這邊把instance翻譯作「個體」,但比較常見的翻譯是「實例」。但我個人是覺得實例這個翻譯很拙劣,語意不清,所以我還是偏好把它依照語意,翻譯為「個體」。 程式設計師一直都要不斷思考如何以適當的角度來定義物件。例如,在這個打電腦的例子中,我們只要把電腦、鍵盤、滑鼠、螢幕、喇叭…當作一個個體(或物件)即可,不需要再細分下去了,例如不需要把鍵盤再細分,定義出每個按鍵、彈簧、電路板、晶片、無線模組、電池…等,因為這樣的細分對目前程式所需要執行的功能(打字)沒有意義。 不過,我覺得其實這是很正常的思考方式,不需要這樣特別提醒和釐清名詞定義,我們還是會自然而然的這麼做。所以這只是種觀念上的定義而已,由於科學的嚴謹性,我們還是得把這些名詞和觀念定義清楚。但若是完全不理會這種觀念,也並不會影響你實際生活和工作,對撰寫程式也沒有影響,所以實務上並不需要太過去在意個體(instance)和物件(object)之間定義上的區別,都把它當作「物件」來理解與使用就好。  註:「instaniate」翻譯作「實體化」,也就是指「從抽象的類別,創造出具體的物件」。也有人把它直接翻譯成「實例化」,但我個人覺得這是很莫名其妙的翻譯,感覺是丟到翻譯軟體直接翻出來的生硬詞彙,文不對題,會讓初學者一頭霧水。 實體化的觀念,可以用下圖表示: ![](https://i.imgur.com/yVv6nWL.png) 程式設計師在撰寫程式時是以電腦的角度來思考事情,所以在撰寫程式時,任何具體和不那麼具體(對人類來說)的東西都可以化為程式碼,以物件導向的方式進行處理。 具體的東西,如電腦、滑鼠、鍵盤、喇叭、老王、小明、狗、貓、兔子、車子、杯子、桌子、椅子、書本 … 都是物件,由於看得見摸得著,所以我們很容易理解它們是物件。但其實在電腦程式中,沒那麼具體的東西也可以被當成物件來進行處理和運算,例如訂房、保險、會議、訂購、訊息 … 雖然對我們人類而言它們是比較不那麼具體的東西,但由於一樣可以化作數學和邏輯的方式進行數據處理,再轉化為二進位的位元(bit)運算,所以對電腦而言,一部車或一個玻璃杯,和一則訊息、或者一個會議沒什麼不同,它們對於電腦來說,都是「具體的」物件。 ******** 我們在本書中已經提過,電腦科學的很多觀念都只是把日常生活的經驗用比較「原始」且「數理」的方式描述,做「換句話說」而已。我們人類的思維方式太過直觀,平常也沒有必要把這麼一件理所當然的事情用這麼繁複的方式描述,所以才會覺得電腦科學和程式設計領域非常困難。但只要經過啟發,學習用不同的角度看事情,學著讓腦袋去適應這樣的思維方式,是成為一個程式設計師必經的過程。 物件導向程式設計讓程式易讀易懂,容易維護,可以支持更大的系統,讓更多人可以合作,讓一切有序、簡單 物件導向程式設計在遇到需求變更的時候,有較大的靈活性可以應對。 物件導向設計讓大型軟體工程的計劃和設計變得更容易管理,能增強工程的健康度,減少失敗工程的數量。 可以把「物件導向」的觀念想成,先各別製作大型機器內的每一個齒輪和元件(各別定義每個物件),之後把這些齒輪和元件安置到適當的位置,讓它們進行互動,便完成這台大型機器的製作,讓使用者可以操作這台大型機器來完成想做的事。 其中類別(class)定義了一件事物的抽象特點。類別的定義包含了資料的形式以及對資料的操作。舉例來說,「狗」這個類別會包含狗的一切基礎特徵,即所有「狗」都共有的特徵或行為,例如它的孕育、毛皮顏色和吠叫的能力。類別可以為程式提供模版和結構。一個類別的方法和屬性被稱為「成員」。 我們來看一段虛擬碼: 類別狗 開始 公有成員: 吠叫(): 私有成員: 毛皮顏色: 孕育: 結束 在這串程式碼中,我們宣告了一個類別,這個類別具有一些狗的基本特徵,也就是「狗」的設計藍圖。 物件(Object)是類別的實體。物件有時會對應到現實世界中的事物,舉例來說,一個圖形程式可能有圓形、矩形…等各種形狀的物件,或者一本書或一個門牌,一個線上購物系統可能有購物車、顧客與產品等物件。有時物件會表示更抽象的實體,也就是「沒有形體的事物」也可以被當成物件,比如一個被開啟的檔案,書或門牌上的文字,或是一個提供量測單位轉換、或是各國匯率換算的服務,所以「物件」是個廣義的概念。 物件(object)是類別(class)的實例(instance)。 例如,「狗」這個類別(class)定義了狗的屬性(即狗的資料或特徵)和方法(即狗可以做的事情),所以「狗」這個類別定義了世界上所有的狗。 而「小黑」這個物件則是一條具體的狗,它的屬性也是具體的。狗有皮毛顏色,而小黑的皮毛顏色是黑色的。因此,小黑就是狗這個類別的一個實體。 系統會給物件分配記憶體空間,而不會給類別分配記憶體空間。這很好理解,類別(class)是抽象的,系統不可能給抽象的東西分配記憶體空間;而物件(object)則是具體的,所以系統會分配記憶體空間給物件讓它儲存數據。 類別和物件就好比是「浮點數(或其他)資料型態」和「1.23」的關係,「浮點數(或其他)資料型態」是一種資料型態,相當於一個類別(class),而「1.23」是一個真正的「浮點數」,是個具體的「物件」,系統會分配記憶體空間給物件讓它儲存具體的數值1.23。 假設我們已經在上面定義好了狗這個類別,我們就可以用這個類別(class)來產生物件(object): 定義小黑是狗 小黑.毛皮顏色 : 棕白色 小黑.吠叫() 我們無法讓狗這個類別(class)去吠叫,因為類別所定義的是所有的狗所具備的屬性和方法,我們不可能讓所有的狗一起吠叫,但我們可以讓一隻具體的狗物件「小黑」去吠叫。相同或不同的物件與物件之間,可以透過彼此的「方法」來進行互動,物件導向程式就是一大堆物件彼此之間的互動所構成的,例如:小黑可以通過「小黑.吠叫()」引起人的注意,從而導致一系列的事件發生。 狗類別(class)是抽象的概念,在沒有使用建構子實體化產生(具體的)物件之前,無法存取其屬性或方法。 狗  實體化建立(具體的)「小黑」物件  小黑 .吠叫();  註:其實用static關鍵字,把屬性或是方法定義成「靜態屬性」或「靜態方法」,就可以跳過實體化建立物件的程序,直接以「類別 . 方法(或屬性)」的方式存取呼叫。但這邊先忽略它,後續會再詳細說明。 用人類的語言來說,狗是一個抽象的泛稱,你無法一次讓全世界狗類別的物種一起吠叫,除非先把狗這個類別給實體化成為具體的物件,才能讓具體的、名為「小黑」的物件吠叫(具體物件才能使用它的「方法」),或者存取它的屬性,例如小黑的毛色是黑色的,尾巴呈現捲曲狀(具體物件才能存取它的「屬性」)。 物件導向的重點,是在寫程式的邏輯上做了變化,從過去的一條一條指令(如「程序導向」的C語言),變成了一個物件、一個物件之間的互動。對每個物件的屬性和行為進行詳細的定義,再利用多個物件彼此互動來達成程式開發者的需求,這樣的邏輯更接近我們現實生活中處理問題的方法。 但物件導向也有缺點,若你在開發小型的程式時,使用物件導向來做可以說是殺雞用牛刀,用大砲打小鳥。除去編譯器幫你優化掉的情況不說,速度較慢且耗費資源。但其實現在電腦硬體效能都那麼強,說實在也感覺不出明顯的差別就是了,所以這個缺點也可以說已經不存在。 ----------------------------------------------------------------------------------------- 程式碼: class Student { // 宣告一個類別,其名稱為Student,是一種「資料型態」(data type) Student s; // 宣告物件s的資料型態是Student(類別型態) s = new Student("M994020023","王小明",22); // 使用new和建構子實體化,建立一個學生物件s } 以上兩列也可以合併寫成一列,如下: Student s = new Student("M994020023","王小明",22); 若將以上程式碼翻譯成中文,就是:「宣告一個資料型態為Student的物件,該物件的名稱為s,並使用new和建構子將之實體化,在建構子中輸入引數:學號為M994020023,姓名為王小明,年齡為22」。 平常建議使用寫成同一列的寫法,會讓意義更加清楚、更直觀易懂,但也要知道它是可以被拆開來的,可幫助我們釐清觀念,靈活運用。 我們把事情說的更加清楚一點: 類別(class)是一種「資料型態」,用來宣告物件(或說實體化建立物件)。 等號左邊  用類別型態來宣告物件的名稱 等號右邊  用new + 建構子,來實體化建立物件 將等號左右邊合併起來  用類別型態來宣告物件的名稱,並且用new + 建構子,實體化建立出具體的物件。  「new」關鍵字就是告訴編譯器,請分配記憶體空間給new後面跟著的東西。 以下,我們就實際撰寫一個類別(class)出來: class Student // 定義一個名稱為Student的類別(資料型態) { // 以下定義這個類別的屬性(property),相當於定義這個類別的成員變數 public int studentID; public string name; public int grade; // 以下定義這個類別的方法(method),相當於定義這個類別的成員函數 public string say() { return “我叫”+ name + “,我是” + grade + “年級的學生”; } // 實作這個方法 }; 為了表示出區別,約定俗成的習慣是,類別名稱的首個英文字母要大寫,例如這裡的Student,首字S大寫代表它是一個類別。類別(class)是一種使用者自訂的「資料型態」,例如我們這裡就定義了一個類別(class),其名稱為Student(我們自己取的名字),所以這時候的Student就和short、long、int、float、double、char…是同樣地位的東西。所以,我們說Student是我們自訂的資料型態的名稱,而它是個類別。  註:類別是可以做「巢狀」(nested)定義的,也就是類別中還能再定義類別。我們已經很熟悉使用巢狀迴圈,例如使用巢狀for迴圈來顯示出九九乘法表。不過在類別的場合,雖然可以巢狀定義類別,但不建議初學者這麼做,因為沒有巢狀迴圈容易理解,後續使用上也較為複雜,比較少用到,因此就不介紹巢狀類別。 上面這個Student類別只是一張(抽象的)設計藍圖,是我們自訂的一種「資料型態」,還不是真正的實體物件。後續要透過「new」運算子和建構子(constructor)才能將類別實體化,產生可以使用方法(method)的具體物件,例如: Student 小明 = new Student(); // 以new運算子和建構子來做「實體化」(instantiate),產生一個實體的學生小明,小明才可以去執行他的「方法」  註:這裡先不討論static(靜態)的情況。 請問「物件」可不可以當成資料傳遞? • 「物件」包含一系列的「狀態」或「資料」,要傳遞物件時必須先透過「序列化技術」將原生資料轉成可傳遞的序列化資料,然後才能當成資料傳遞。 • 本題解答:「可以」或「不可以,除非先將物件序列化後才能當成資料傳遞 如果「物件」從 A 電腦傳遞到 B 電腦時,若該物件要執行「方法」還需不需要有「類別」存在? • 「物件」包含一系列的「狀態」或稱「資料」,要傳遞物件時必須先透過「序列化技術」將原生資料轉成可傳遞的序列化資料,然後才能傳遞到另一台電腦。 • 「類別」是一種定義、一種參考,所以當 B 電腦得到 A 電腦傳來的資料時,對 B 電腦來說收到的僅僅是一串「資料」罷了,並非物件,若 B 電腦沒有「類別」定義的存在,便無法將這些「資料」給「反序列化」成「物件」,所以 B 電腦是需要有「類別」存在的。 • 本題解答:「需要」 我們以程式入門最簡單的,在螢幕上顯示Hello, World !為例,若以Java來寫: class HelloWorld { public static void main(String[ ] args) { System.out.println("Hello, World !"); } } 完全的初學者看到這邊大概就頭暈了,但其實只要理解Java是個非常「物件導向」的語言,經過說明之後,你不但會覺得這樣的寫法不可怕,而且還非常有道理。Java的語法較為嚴謹,雖然對於初學者來說有點可怕,但這樣的設計有很多優點,也因為嚴謹所以穩定,就像一個有條不紊的人一樣。 不要害怕面對未知的事物,接下來就讓我們慢慢說明吧!