close

物件導向程式設計的雛形,早在1960年的Simula語言中即可發現,當時的程式設計領域正面臨著一種危機:在軟硬體環境逐漸複雜的情況下,軟體如何得到良好的維護?物件導向程式設計在某種程度上透過強調可重複性解決了這一問題。20世紀70年代的Smalltalk語言在物件導向方面堪稱經典——以至於30年後的今天依然將這一語言視為物件導向語言的基礎。

物件導向程式設計可以被視作一種在程式中包含各種獨立而又互相呼叫的單位和物件的思想,這與傳統的思想剛好相反:傳統的程式設計主張將程式看作一系列函式的集合,或者直接就是一系列對電腦下達的指令。物件導向程式設計中的每一個物件都應該能夠接受資料、處理資料並將資料傳達給其它物件,因此它們都可以被看作一個小型的「機器」,或者說是負有責任的角色。

目前已經被證實的是,物件導向程式設計推廣了程式的靈活性和可維護性,並且在大型項目設計中廣為應用。 此外,支持者聲稱物件導向程式設計要比以往的做法更加便於學習,因為它能夠讓人們更簡單地設計並維護程式,使得程式更加便於分析、設計、理解。反對者在某些領域對此予以否認。

主流的程式開發語言,包括 Java, .NET(C#, VB …) 等,甚至連 PHP 這種 ”Script-based”,以網頁設計為主的描述性語言,都已標榜能實現所謂的 “物件導向(object-oriented)” 的開發模式,那麼顯然,軟體分析與設計、包括程式撰寫人員,都必須要能對以 “物件” 為單位的設計與開發,要能有其共識。但 “物件” 卻往往很難被界定與定義,具體的東西,如電腦、小狗、汽車、杯子、Xbox 360 …等,都是物件但抽象的概念,如訂房、會議、訂購、保險、行動電話的簡訊 …等,也都可以是物件。具體的東西, 因為能看得到,所以看起來比較容易能找出 “物件”,但其實也很容易有 “茫點”,例如,”松樹” 是一個物件,還是多個物件? “樹枝”、”樹葉”、”樹幹”、”樹根” 是 “松樹” 的基本組成元素,它們是否也可以算是物件? 而抽象的概念,更是難以界定,若沒有足夠的抽象能力(抽象能力有時又要帶點創意與想像),實在很難捕捉看不著、摸不到的 “相”,將之定義為具體的(specific)物件。

由此定義也可以得知,“物件” 與 “個體(instance)” 這兩個術語,其實是具同義詞性質的。將 “概念(concept)” 作為認知的對象時,所產出的 “個體(instance)” 即為物件。但概念會牽涉到人們對於觀點、角度等認知,而會有不同的體認。例如站在遊客的角度,他所看到在森林裡面的樹木,是一個個的 “個體”,會從樹木之外的角度來欣賞樹木的茂盛與宏偉,是一種整體性的認知;但站在植物學家的角度,他要研究組成樹木的結構元素,所以會把樹木分為 ”樹幹”、”樹枝”、”樹葉”、”樹根” 等多個組成樹木的 “物件”,從結構的觀點來研究樹木的內部組成。

這同時也就代表了,雖然到處都是物件,但並不是任意地將物件給 “塞” 入軟體系統內,物件導向的設計,是將問題領域(problem domain)的概念,呈現與對映(mapping)至軟體系統內,那麼,如何正確地捕捉(capture)問題領域的概念成為物件,就成為是軟體設計中,最為重要的技能與素養了。

簡易範例說明:

1.類別
 
類別(Class)定義了一件事物的抽象特點。通常來說,類別定義了事物的屬性和它可以做到的(它的行為)。舉例來說,「狗」這個類別會包含狗的一切基礎特徵,例如它的孕育、毛皮顏色和吠叫的能力。類別可以為程式提供模版和結構一個類別的方法和屬性被稱為「成員」。

我們來看一段虛擬碼:

類別
開始
  私有成員:
       孕育:
       毛皮顏色:
  公有成員:
       吠叫():
結束

 

2.物件
 
物件(Object)是類別的例項。例如,「狗」這個類別列舉狗的特點,從而使這個類別定義了世界上所有的狗。而萊絲這個物件則是一條具體的狗,它的屬性也是具體的。狗有皮毛顏色,而萊絲的皮毛顏色是棕白色的。因此,萊絲就是狗這個類別的一個例項。一個具體物件屬性的值被稱作它的「狀態」。(系統給物件分配內部記憶體空間,而不會給類別分配內部記憶體空間,這很好理解,類別是抽象的系統不可能給抽象的東西分配空間,物件是具體的
 
假設我們已經在上面定義了狗這個類別,我們就可以用這個類別來定義物件:

定義萊絲
萊絲.毛皮顏色:=棕白色
萊絲.吠叫()

我們無法讓狗這個類別去吠叫,但是我們可以讓物件「萊絲」去吠叫,正如狗可以吠叫,但沒有具體的狗就無法吠叫。

用個生活上的例子,類別就像是我們的印章,蓋出來的第一個章叫做A。就是物件。蓋出來的第二個章叫做B,B也是物件。A B 跟都適用同一個模子蓋出來。而作為模型的印章,她就是類別。

3.方法
 
方法(Method)是定義一個類別可以做的,但不一定會去做的事。作為一條狗,萊絲是會吠叫的,因此「吠叫()」就是它的一個方法。與此同時,它可能還會有其它方法,例如「坐下()」,或者「吃()」。 對一個具體物件的方法進行呼叫並不影響其它物件,正如所有的狗都會叫,但是你讓一條狗叫不代表所有的狗都叫。 如下例:

定義萊絲是狗
定義泰爾是狗
萊絲.吠叫()

則泰爾是會吠叫但沒有吠叫的,因為這裡的吠叫只是對物件「萊絲」進行的。

4.繼承
 
繼承(Inheritance)是指,在某種情況下,一個類別會有「子類別」。子類別比原本的類別(稱為父類別)要更加具體化,例如,「狗」這個類別可能會有它的子類別「牧羊犬」和「吉娃娃犬」。在這種情況下,「萊絲」可能就是牧羊犬的一個例項。子類別會繼承父類別的屬性和行為,並且也可包含它們自己的。我們假設「狗」這個類別有一個方法叫做「吠叫()」和一個屬性叫做「毛皮顏色」。它的子類別(前例中的牧羊犬和吉娃娃犬)會繼承這些成員。這意味著程式設計師只需要將相同的代碼寫一次。 在虛擬碼中我們可以這樣寫:

類別牧羊犬:繼承 定義萊絲牧羊犬
萊絲.吠叫() /* 注意這裡呼叫的是狗這個類別的吠叫方法。 */

回到前面的例子,「牧羊犬」這個類別可以繼承「毛皮顏色」這個屬性,並指定其為棕白色。而「吉娃娃犬」則可以繼承「吠叫()」這個方法,並指定它的音調高於平常。子類別也可以加入新的成員,例如,「吉娃娃犬」這個類別可以加入一個方法叫做「顫抖()」。設若用「牧羊犬」這個類別定義了一個例項「萊絲」,那麼萊絲就不會顫抖,因為這個方法是屬於吉娃娃犬的,而非牧羊犬。事實上,我們可以把繼承理解為「是」。例如,萊絲「是」牧羊犬,牧羊犬「是」狗。因此,萊絲既得到了牧羊犬的屬性,又繼承了狗的屬性。 我們來看虛擬碼:

類別吉娃娃犬:繼承
開始
公有成員:
顫抖()
結束
類別牧羊犬:繼承

定義萊絲牧羊犬
萊絲.顫抖() /* 錯誤:顫抖是吉娃娃犬的成員方法。 */

當一個類別從多個父類別繼承時,我們稱之為「多重繼承」。多重繼承並不總是被支援的,因為它很難理解,又很難被好好使用。

5.封裝性

具備封裝性(Encapsulation)的物件導向程式設計隱藏了某一方法的具體執行步驟,取而代之的是透過訊息傳遞機制傳送訊息給它。因此,舉例來說,「狗」這個類別有「吠叫()」的方法,這一方法定義了狗具體該透過什麼方法吠叫。但是,萊絲的朋友蒂米並不需要知道它到底如何吠叫。 從例項來看:

/* 一個程序導向的程式會這樣寫: */
定義萊絲
萊絲.設定音調(5)
萊絲.吸氣()
萊絲.吐氣()

/* 而當狗的吠叫被封裝到類別中,任何人都可以簡單地使用: */
定義萊絲
萊絲.吠叫()

封裝是透過限制只有特定類別的例項可以存取這一特定類別的成員,而它們通常利用介面實作訊息的傳入傳出。舉個例子,介面能確保幼犬這一特徵只能被賦予狗這一類別。通常來說,成員會依它們的存取許可權被分為3種:公有成員、私有成員以及保護成員。有些語言更進一步:Java可以限制同一包內不同類別的存取;C#和VB.NET保留了為類別的成員聚集準備的關鍵字:internal(C#)和Friend(VB.NET);Eiffel語言則可以讓使用者指定哪個類別可以存取所有成員。

arrow
arrow
    全站熱搜

    阿基 發表在 痞客邦 留言(21) 人氣()