面向對象程序設計概述


面向對象程序設計概述

面向對象程序設計(object-oriented programming,OOP)是當今主流的程序設計范型,它取代了20世紀70年代的“結構化”或過程式編程技術。由於Java是面向對象的,所以你必須熟悉OOP才能夠很好地使用Java。

對象

面向對象的程序是由對象組成的,每個對象包含對用戶公開的特定功能部分和隱藏的實現部分。程序中的很多對象來自標准庫,還有一些是自定義的。究竟是自己構造對象,還是從外界購買對象完全取決於開發項目的預算和時間。但是,從根本上說,只要對象能夠滿足要求,就不必關心其功能到底是如何實現的。

結構化程序設計

傳統的結構化程序設計通過設計一系列的過程(即算法)來求解問題。一旦確定了這些過程,就要開始考慮存儲數據的適當方式。這就是Pascal語言的設計者Niklaus Wirth將其著作命名為《算法+數據結構=程序》(Algorithms+Data
Structures=Programs,Prentice Hall,
1975)的原因。需要注意的是,在Wirth的這個書名中,算法是第一位的,數據結構第二位的,這就明確地表述了程序員的工作方式。首先要確定如何操作數據,然何組織數據的結構,以便於操作數據。而OOP卻調換了這個次序,將數據放在第一位,然后再考慮操作數據的算法。

面對過程與面向對象的程序設計對比

對於一些規模較小的問題,將其分解為過程的開發方式比較理想。面向對象更加
適合解決規模較大的問題。要想實現一個簡單的Web瀏覽器可能需要大約2000個過過程程,這些過程可能需要對一組全局數據進行操作。采用面向對象的設計風格,可能只據需要大約100個類,每個類平均包含20個方法(如圖(面對過程與面向對象的程序設計對比)所示)。后者更易於程序員掌握,也容易找到bug。假設給定對象的數據出錯了,在訪問這個數據項的20個方法中查找錯誤要比在2000個過程中查找容易得多。

image

類的實例

類(class)是構造對象的模板或藍圖。我們可以將類想象成制作小甜餅的模具,將對象想象為小甜餅。由類構造(construct)對象的過程稱為創建類的實例(instance)。

正如前面所看到的,用Java編寫的所有代碼都位於某個類里面。標准Java庫提供了幾千個類,可用於各種目的,如用戶界面設計、日期、日歷和網絡編程。盡管如此,在Java中還是需要創建一些自己的類,以便描述你的應用程序所對應的問題領域中的對象。

封裝

封裝(encapsulation,有時稱為數據隱藏)是處理對象的一個重要概念。從形式上看,封裝就是將數據和行為組合在一個包中,並對對象的使用者隱藏具體的實現方式。

實例字段和方法

對象中的數據稱為實例字段(instance
field),操作數據的過程稱為方法(method)。作為一個類的實例,特定對象都有一組特定的實例字段值。這些值的集合就是這個對象的當前狀態(state)。無論何時,只要在對象上調用一個方法,它的狀態就有可能發生改變。

實現封裝的關鍵在於,絕對不能讓類中的方法直接訪問其他類的實例字段。程序只能通過對象的方法與對象數據進行交互。封裝給對象賦予了“黑盒”特征,這是提高重用性和可靠性的關鍵。這意味着一個類可以完全改變存儲數據的方式,只要仍舊使用同樣的方法操作數據,其他對象就不會知道也不用關心這個類所發生的變化。

Object類

OOP的另一個原則會讓用戶自定義Java類變得更為容易,這就是:可以通過擴展其他類來構建新類。事實上,在Java中,所有的類都源自一個“神通廣大的超類”,它就是0bject。所有其他類都擴展自這個Object類。

繼承

在擴展一個已有的類時,這個擴展后的新類具有被擴展的類的全部屬性和方法。你只需要在新類中提供適用於這個新類的新方法和數據字段就可以了。通過擴展一個類來建立另外一個類的過程稱為繼承(inheritance)。

對象

對象的三個主要特性

要想使用OOP,一定要清楚對象的三個主要特性:

  • 對象的行為(behavior)——可以對對象完成哪些操作,或者可以對對象應用哪些方法?
  • 對象的狀態(state)——當調用那些方法時,對象會如何響應?
  • 對象的標識(identity)——如何區分具有相同行為與狀態的不同對象?

同一個類的所有對象實例,由於支持相同的行為而具有家族式的相似性。對象的行為是用可調用的方法來定義的。

此外,每個對象都保存着描述當前狀況的信息。這就是對象的狀態。對象的狀態可能會隨着時間而發生改變,但這種改變不會是自發的。對象狀態的改變必須通過調用方法實現(如果不經過方法調用就可以改變對象狀態,只能說明破壞了封裝性)。

但是,對象的狀態並不能完全描述一個對象。每個對象都有一個唯一的標識(identity,或稱身份)。例如,在一個訂單處理系統中,任何兩個訂單都存在着不同之處,即使所訂購的貨物完全相同,它們也是不同的訂單。需要注意,作為同一個類的實例,每個對象的標識總是不同的,狀態也往往存在着差異。

對象的這些關鍵特性會彼此相互影響。例如,對象的狀態影響它的行為(如果一個訂單“已送貨”或“已付款”,就應該拒絕調用要求增刪訂單中商品的方法。反過來,如果訂單是“空的”,即還沒有預訂任何商品,這個訂單就不應該“送貨”)。

識別類

傳統的過程式程序中,必須從頂部的main函數開始編寫程序。在面向對象程序設計時沒有所謂的“頂部”。學習OOP的初學者因而常常會感覺無從下手。答案是:首先從識別類開始,然后再為各個類添加方法。

識別類的一個簡單經驗是在分析問題的過程中尋找名詞,而方法對應着動詞。

例如,在訂單處理系統中,有這樣一些名詞:

  • 商品(Item);
  • 訂單(Order);
  • 送貨地址(Shipping address);
  • 付款(Payment);
  • 賬戶(Account)。

從這些名詞就可以得到類Item、Order等。

接下來查看動詞。商品被添加到訂單中,訂單會發貨取消,另外可以對訂單完成付款。對於每一個動詞,如“添加”“發貨”“取消”以及“完成付款”,都要識別出負責完成相應動作的對象。例如,當一個新的商品添加到訂單中時,那個訂單對象就是負責的對象,因為它知道如何存儲商品以及如何對商品進行排序。也就是說,add應該是Order類的一個方法,它要取一個Item對象作為參數。

當然,這種“名詞與動詞”原則只是一種經驗,在創建類的時候,只有經驗才能幫助你確定哪些名詞和動詞重要。

類之間的關系

類的三種關系

在類之間,最常見的關系有

  • 依賴(“uses-a”);
  • 聚合(“has-a”);
  • 繼承(“is-a”)。

依賴

依賴(dependence),即“uses-a”關系,是一種最明顯的、最常見的關系。例如,Order類使用Account類是因為Order對象需要訪問Account對象查看信用狀態。但是Item類不依賴於Account類,因為Item對象不需要考慮客戶賬戶。因此,如果一個類的方法使用或操縱另一個類的對象,我們就說一個類依賴於另一個類。

應該盡可能地將相互依賴的類減至最少。這里的關鍵是,如果類A不知道B的存在,它就不會關心B的任何改變(這意味着B的改變不會導致A產生任何bug)。用軟件工程的術語來說,就是盡可能減少類之間的耦合

聚合

聚合(aggregation),即“has-a”關系,很容易理解,因為這種關系很具體。例如,一個Order對象包含一些Item對象。包容關系意味着類A的對象包含類B的對象。

注釋

有些方法學家不喜歡聚合這個概念,而更加喜歡使用更一般的“關聯”關系。
從建模的角度看,這是可以理解的。但對於程序員來說,“has-a”關系更加形象。我們喜歡使用聚合還有另一個原因:關聯的標准記法不是很清楚,請參看表(表達類關系的UML符號)。

image

繼承

繼承(inheritance),即“is-a”關系,表示一個更特殊的類與一個更一般的類之間的關系。例如,Rushorder類由Order類繼承而來。在更特殊的RushOrder類中包含了一些用於優先處理的特殊方法,還提供了一個計算運費的不同方法;而其他的方法,如添加商品、生成賬單等都是從Order類繼承來的。一般而言,如果類A擴展類B,類A不但包含從類B繼承的方法,還會有一些額外的功能。

UML

很多程序員采用UML(Unified Modeling
Language,統一建模語言)繪制類圖,用來描述類之間的關系。圖(類圖)就是這樣一個例子。類用矩形表示,類之間的關系用帶有各種修飾的箭頭表示。表(表達類關系的UML符號)給出了UML中最常見的箭頭樣式。

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM