# 何謂物件導向程式設計 物件導向程式設計(Object-Oriented Programming,縮寫為OOP) 「物件導向」是我們每天身在其中已經太過熟悉,以至於平常根本不會把它拿出來特別討論的事情。我們日常生活無時無刻都是物件導向,就像呼吸一樣自然而然,例子多不勝數。例如,我現在坐在電腦前面一邊打字一邊聽音樂,用物件導向的方式來表達的話,就像下面這個樣子:  「我」是個物件、電腦、鍵盤、滑鼠、螢幕、喇叭 … 也都是物件,這個例子就是「我」這個物件和其他物件彼此之間進行互動的過程。 而「人類」是個類別(class),因為地球上的人口有七十幾億,如果不清楚定義出到底是哪一個人在打字,要怎麼開始互動?同理,電腦、滑鼠、鍵盤、螢幕、喇叭 … 也都只是類別(class),還要再指出它的具體名稱,才知道你到底用的是地球上超多台電腦的哪一台,使用的是哪一個鍵盤、滑鼠、螢幕…。之後,把每個物件的屬性和使用方法詳細定義出來,就可以把這些物件集合起來,以物件彼此之間的(使用)方法(method)來做互動了,這就是物件導向的核心觀念。 而現在我們做的事情是使用電腦打字和聽音樂,所以我穿什麼顏色的衣服、聽的是哪一首歌、桌上放的是什麼飲料 … 這些資訊對目前要做的事情都沒有意義,可以不用描述,只要把有意義的東西彙整起來就好了。 既然說到這裡,就順帶釐清一些術語,即使是資深的程式設計師可能也經常搞不懂個體(instance)和物件(object)之間的區別,也就是經常在文章上反覆看到這兩個術語,但卻仍然一知半解。 個體(instance)和物件(object)的意思其實差不多,但它們是「相對性」的。例如你從整部車的角度來看,把整部車子當作一個個體(instance),那組成車子的引擎、方向盤、油壓系統、電路、輪子、座椅…等系統都會被你當作是物件(object)。但若你再把引擎當作是一個個體,則噴油嘴、汽缸、連桿、活塞、火星塞 … 等就會被視作物件。而如果你把道路系統當作一個個體,則路上行駛的每部車子就成了許多物件了。由此可見,依據個人主觀的觀點,這種關係會隨時動態的改變。 但將這種關係反過來說卻不一定正確。例如不同廠牌和不同型號的車子,引擎排氣量和設計都會不同,因此你可以說車子都有引擎(或任何種類的動力系統),但卻不能說每部車子的引擎都一樣。所以,一個個體必定是一個物件,但一個物件卻不一定是一個個體。 註:這邊把instance翻譯作「個體」,但比較常見的翻譯是「實例」。但我個人是覺得實例這個翻譯很拙劣,語意不清,所以我還是偏好把它依照語意,翻譯為「個體」。 程式設計師一直都要不斷思考如何以適當的角度來定義物件。例如,在這個打電腦的例子中,我們只要把電腦、鍵盤、滑鼠、螢幕、喇叭…當作一個個體(或物件)即可,不需要再細分下去了,例如不需要把鍵盤再細分,定義出每個按鍵、彈簧、電路板、晶片、無線模組、電池…等,因為這樣的細分對目前程式所需要執行的功能(打字)沒有意義。 不過,我覺得其實這是很正常的思考方式,不需要這樣特別提醒和釐清名詞定義,我們還是會自然而然的這麼做。所以這只是種觀念上的定義而已,由於科學的嚴謹性,我們還是得把這些名詞和觀念定義清楚。但若是完全不理會這種觀念,也並不會影響你實際生活和工作,對撰寫程式也沒有影響,所以實務上並不需要太過去在意個體(instance)和物件(object)之間定義上的區別,都把它當作「物件」來理解與使用就好。 註:「instaniate」翻譯作「實體化」,也就是指「從抽象的類別,創造出具體的物件」。也有人把它直接翻譯成「實例化」,但我個人覺得這是很莫名其妙的翻譯,感覺是丟到翻譯軟體直接翻出來的生硬詞彙,文不對題,會讓初學者一頭霧水。 實體化的觀念,可以用下圖表示:  程式設計師在撰寫程式時是以電腦的角度來思考事情,所以在撰寫程式時,任何具體和不那麼具體(對人類來說)的東西都可以化為程式碼,以物件導向的方式進行處理。 具體的東西,如電腦、滑鼠、鍵盤、喇叭、老王、小明、狗、貓、兔子、車子、杯子、桌子、椅子、書本 … 都是物件,由於看得見摸得著,所以我們很容易理解它們是物件。但其實在電腦程式中,沒那麼具體的東西也可以被當成物件來進行處理和運算,例如訂房、保險、會議、訂購、訊息 … 雖然對我們人類而言它們是比較不那麼具體的東西,但由於一樣可以化作數學和邏輯的方式進行數據處理,再轉化為二進位的位元(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的語法較為嚴謹,雖然對於初學者來說有點可怕,但這樣的設計有很多優點,也因為嚴謹所以穩定,就像一個有條不紊的人一樣。 不要害怕面對未知的事物,接下來就讓我們慢慢說明吧!
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.