基本特點
Simula包含了大量我們現在仍在使用的面向對象的特點,除了前面提到的類、對象以外,還有以下特點:
一 動態查找選擇對象的活動記錄操作;
一 抽象在Simula晚期版本中具備,但在Simula 67中沒有;
一 子類型源於類型與類相關的方式;
一 繼承類向前兼容的形式,包括在子類中重新定義類的一部分。
儘管Simula 67中沒有區別類的public和private機制。但晚期語言允許使用“protected”,它允許子類型可見(其他類不允許使用),以及“hidden”,即使子類也不能只用它。除了以上特點外,Simula還包含了一些其他大多數面向對象語言不包含的以下特點:
一 inner使用inner關鍵字,子類還可以調用相關父類的代碼;
■ inspect和qua在運行中可以測試對象類型,從而執行相應類型的代碼。inspect是強類型語言,而qua是正確性檢查及類型校驗。
所有這些特點在接下來的章節中都將進行詳細討論,一些在其他語言中出現而沒有在早期的Simula版本中出現的有:多重繼承、類變數和self/super機制。
基本特徵
五個概念
要理解Smalltalk首先必須理解五個概念,即對象(object)、訊息(message)、類(class)、實例(instance)和方法(method),它們是互相以一種循環的方式來定義的。只要理解了這五個概念,Smalltalk的其餘部分也就非常好理解了。
對象——在Smalltalk中,任何東西都是對象,數據結構、物理裝置、概念、檔案、可執行程式段、以及原始碼程式段都是對象,連支持環境的組成部分,如編譯工具和調試工具等也都是對象。一切都需要按對象和對象行為的方式來思考和行動。
類——類是定義某種對象特性的對象。類有名字,它表明了它所代表的對象的類型。由某個類代表的對象被稱作該類的實例。類按超類、子類層次排列,其中子類是比超類更特殊的類,它能繼承超類的特性。類的協定描述提供了建立該類的實例的途徑。對象所能回響的所有動作均在它的類描述中定義。
實例——實例是某個類對象,它的特性由該類的協定描述來定義。實例所能回響的動作由它的類協定定義。
訊息——訊息是傳送給某個對象的動作標識符。它指示對象執行某些動作。只有當訊息包含在某個對象的類協定描述中時,該對象才能回響該訊息。某個類的實例能回響的所有訊息都必須在該類或它的超類的協定描述中定義。訊息可由類從它的超類中繼承。
方法——方法是訊息的詳細實現。類的協定描述應包括它的實例所能回響的每個訊息的方法細節。方法精確地定義了對象如何回響訊息。
抽象
在Smalltalk中的對象是抽象的封裝體。抽象包括數據抽象和功能抽象。數據抽象的形式主要是私有和共享數據,它們定義了對象的特性。而功能抽象則採用了方法的形式,這些方法給出了某個對象如何回響訊息的細節。對象所能回響的每個訊息都對應著一個方法。
任何對象都是某個類的實例。而每個類都有名字並代表一種類型的對象。可以用給類名傳送一條實例創建訊息的方式來建立對象。某個對象的所有數據抽象和功能抽象均在其類描述協定中定義,包括用於實例創建的訊息、私有數據、共享數據和方法。
最一般的對象是由一個稱作為Object的類所代表的對象。Object是Smalltalk系統中所有其他類的超類。它的類描述協定包括訊息和相應的方法、私有和共享數據、以及實例創建方法,這一切對於任何對象都是共同的。然而只要需要,子類也可以重載Object的任何方法、增加新的私有和共享數據。
在開發象Smalltalk這樣的軟體系統的過程中,定義它所支持的對象種類以及其相應的功能抽象是最主要的工作。Smalltalk映像是由200多種類組成的,這些類按照類別和依賴關係組成一個類層次。在映像中有些類是抽象類,它們有以下特性:
(1)沒有任何對象是抽象類的實例,它們只能是抽象類的子類的實例;
(2)抽象類中的方法代表他的所有子類的公共協定。子類可以重載方法和增加新的數據;
(3)抽象類提供了一個邏輯層次組織。
例如,在smalltalk系統中只有兩個Boolean對象。對象true是類True的唯一實例,對象falSe是類False的唯一實例。一個布爾表達式(比較或測試)的結果或是對象true或是對象false。對象tfue和faIse中的哪一個回響訊息形成了smalltalk中控制的基礎。
在smalltalk映像中類True和False是類Boolean的子類。Boolean沒有實例,只是用來定義True和False的公共協定,因此Boolean是一個抽象類。在該映像中還存在其他的抽象類。
類True和False的協定描述列出了幾個訊息以及相應的用於控制的方法(功能抽象)。
封裝
smalltalk中對象的封裝是基於個別對象的類描述協定的。特定類的協定定義了它的實例(對象)的特性。對象的特性包括描述性參數(其他對象)和對象如何回響訊息的細節。
類描述協定包括許多基本元素,但並不是每個類都有所有這些元素。是否具有私有數據或共享數據依賴於該類所代表的對象的性質。方法的數量和類型依賴於該類的對象所必須回響的功能抽象有多么複雜和豐富。以下是類描述協定的基本元素:
定義——一段表明該類在類層次中位置的敘述,它同時也列出該類中私有、共享和公用數據對象的標識符。
私有數據——其值為該類的個別實例擁有的描述性參數(對象)。私有數據實例變數表示它只能由實例方法訪問。如何訪問私有數據對象由每個對象的類的協定決定。
共享數據——其值為該類的所有實例共享的描述性參數(對象)。共享數據由類變數表示,它可由實例方法和類方法兩者訪問。因此訪問細節是由每個對象的類的協定控制的。
公用數據——其值為多個類共享的描述性參數(對象)。對公用數據的訪問必須在類描述協定中特別表示出來。公用數據是用pool dictionaries表示的,因此,可由實例方法和類方法兩者訪問。但是,由於公用數據也是另一個類的實例,訪問將受到該類提供的訊息的限制。
實例方法——某個類的實例能回響的訊息的實現細節。這些方法所代表的訊息只能發給該類的實例。
類方法——某個類能回響的訊息的實現細節。這些方法所代表的訊息只能發該類名。類方法的典型用途是初始化類變數、創建某類的實例等。
繼承
面向對象的程式設計語言的要求之一就是必須支持繼承。繼承只有在類按層次排列時才有意義。子類繼承超類的某些東西,包括私有數據、共享數據、實例方法和類方法。子類繼承它的一切先輩,從直接超類一直到類Object。
如果某個子類沒有任何同它的超類不同協定(包括數據和方法),它就沒有存在的必要。因此子類必須有某些新的數據和方法。同時,子類也可以重新定義從它的超類繼承來的方法,這種情況稱為方法的重載,它是一種多態性。
smalltalk的繼承規則如下:
(1)某個子類只從它的超類層次鏈上繼承,從直接超類一直到類ob_ject;
(2)所有的類都是類object的子類,除了類object本身以外。類object沒有超類;
(3)超類不繼承子類;
(4)支持多重繼承,這意味著一個類可被聲明是一個以上層次鏈的子類,並且可以繼承每個層次鏈的特性;
(5)一個子類可從它的超類繼承私有數據、共享數據、實例方法和類方法;
(6)對繼承的協定而言,訪問私有和共享數據的規則同類描述協定的相同;
(7)除了繼承的協定以外,子類還可以增加新的協定;
(8)子類可重載繼承的實例和類方法。
多態性
smalltalk中訊息選擇器的範圍限於每個類和它的子類的類協定描述。這樣,相同的訊息選擇器可存在於許多類中。當訊息傳送給對象時,其意義是由該對象所屬類的協定描述所確定的。不同對象回響同一個訊息選擇器的能力稱為多態性。
通過允許同一個訊息選擇器(表明一個特定的動作)被傳送給不同對象,多態性大大地增強了軟體的可讀性。例如,訊息printon:可傳送給smalltalk系統中的任何對象,唯一的要求是printon:的細節必須包括在該對象所屬類的層次路徑上。從概念上來講,printon:表明了一個特定的動作。這個概念對任何對象都是相同的,只是實現細節不同。
類協定
由於類也是對象,因此它也有類,定義了一個metaclass。
metadass(元類)是一個類的類,它是唯一的且有著不同於其他類的特性。每當定義一個新類時,其元類自動被定義。每個類可以有不同的元類,這一事實使得所有的類無需有相同的協定。元類的主要特性如下:
在smalltalk映像中,每個類只有一個元類。
每個元類只有一個實例,即它所相應的那個類。
一個“類”對象的協定是包含在它的元類中的。類變數和類方法是一個類的元素協定描述的一部分。為了方便起見,將類描述和元類描述組合了起來。這兩者均可用系統瀏覽器(system Browser)觀察和修改。
任何一個元素並不是另一個元素的實例。相反,所有的元類都是一個叫做Meta—class類的實例。MetaclaSs是映像層次的一部分。
由於Metaclass是類(不是元素),它也有元素。Metaclass的元類也是Metaclass的實例。 .
一個名叫class的類是所有元素的抽象超類。對元類而言,它起的作用就象object對類起的作用是一樣的。同時Class也是元類,也是Metaclass的實例。
元類也有一個層次,除了層次高一些以外,同類層次完全匹配。記住,Object是所有其他類的超類,包括元類。
繼承和多態性規則同樣適用於元類。
對象
對象是一種可以循環描述的數據結構,每個對象都是某個類的實例。一個對象所屬的類可以通過對其傳送所有的對象都能理解的訊息class來確定。
對象是封裝的數據結構,儲存在一個對象內部的數據只能通過訊息來存取,但對象可以共享。
方法中的字self是指調用這個方法的訊息的接受者。
變數
smalltalk變數是用來存放對象的,方法是存儲對象的指針。在表達式中,變數名可用來表示一個對象,方式是存儲該對象的指針。變數在不同的時刻可存放不同對象的指針。當執行一個賦值表達式時,存放在變數中的對象指針可能發生改變。賦值語句只拷貝對象的指針而不是對象本身。
變數有私有的和共享的兩種。私有變數只能由單個對象訪問,以小寫字母開頭;共享變數可被多個對象訪問,以大寫字母開頭。
變數共有三種類型:
(1)實例變數是對象的組成部分,它們在對象的整個生存期都存在;
(2)暫時變數是在某個方法執行時創建的,它們只在該方法執行的時間記憶體在;
(3)共享變數可由許多對象共享。如果不刪除,它們將始終存在於系統之中。
實例變數
每個對象都保存其內部狀態,一個對象的私有存儲器由只供自己訪問的稱為實例變數的部分組成。實例變數類似於其他語言中一個記錄結構的域,或者是有一個名字或者是用一個整數索引來引用。有名實例變數可以通過其名字來訪問,索引實例變數只能通過訊息來訪問(一般是at:put:帶整數索引值)。類的每個成員都有自己的實例變數。
實例變數具有三種類型,即指針型、字型和位元組型。屬於同一個類的對象的所有實例變數具有同一種類型。大多數對象的實例變數用來存放指針,這個指針指向該對象。如果一個對象存放字或位元組,則它的實例變數分別存放16位或8位值來表示基本的數據。
類對其成員對象既可以聲明有名實例變數也可以聲明索引實例變數。對於一個類的所有成員來說,有名實例變數的名字和數目是固定的。在同一個類的成員中索引變數的數目可以不同。例如:#(1 2 3)和#(`up``down`)都是類Array的對象,但是它們的索引實例變數的數目不同,分別是3和2。具有索引實例變數的類用指定創建索引實例變數數目的訊息來創建其新成員(一般使用帶有一個整數變元的訊息new:)。許多對象可用訊息size返回其索引實例變數的數目。
只有調用了某個方法的訊息的接受者的實例變數才可以用名字來表示。
暫時變數
暫時變數包括方法變元、方法暫時變元以及代碼塊中的塊變元。
方法變元被賦予調用該方法的訊息的變元。方法暫時變元在方法調用時被初始化為nil,塊變元被賦予代碼塊執行時訊息value:的變元。
共享變數
共享變數在共享字典集中定義,不同種類的共享變數在不同的共享字典中定義。所有的共享變數名都以大寫字母開頭,變數名和變數值一起放在一個Association對象中,這個對象也是放在共享字典中的。
系統字典Smalltalk是包含所有全局變數的共享字典,全局變數可被任何對象訪問。
每一個類的類變數隱含地收集在相應類的共享字典中,類變數是作為類的規格說明的一部分來定義的。類變數只對本類、本類的子類、本類的實例和本類的子類的實例是可訪問的。
共享字典變數也包含在共享字典中,它是全局變數。要使共享字典變數對某個類及其實例是可訪問的,用戶必須修改這個類的規格說明。
對象
類是Smalltalk的程式模組,它描述了數據結構(對象)、算法(方法)和外部接口(訊息協定),因此提供了完整的問題求解能力。
每個對象都是某個類的實例,一個類的所有實例具有相同的結構(即:相同的實例變數,能夠回響相同的訊息,可用相同的方法)。
類也是對象,它存儲在Smalltalk系統字典中的全局變數中。類名以大寫字母開頭,以便類可在表達式中引用。
類層次
所有的類形成一個類層次,它由一個稱為Object的根類和許多子類組成。每個類繼承其超類的功能。類Object提供了所有對象所共有的行為,它包括對象標識符的列印方法、測試對象的類的方法以及拷貝對象的方法。每個子類都在其超類的基礎上,通過添加自己的方法和實例變數來實現它自己的行為。
繼承
一個類繼承其超類的所有的實例變數、類變數和方法。類變數的繼承是指允許某個類定義的方法引用其超類定義的類變數。實例變數的繼承是指允許某個類定義的方法引用其超類定義的實例變數,但是它還意味著這個類的實例對象中包括其超類的實例變數。
要確定方法能完成何種功能可通過兩條信息來判斷,即訊息的選擇器和訊息接受對象的類。
首先,檢查是否存在接受對象的類可用的方法,看一看是否該類中存在一個匹配這個訊息選擇器的方法,如果存在,則執行這個方法,如果不存在,則在接受對象的類的超類中再檢查是否存在一個匹配這個訊息的選擇器。重複這一過程直到找到這個方法或到達層次鏈的頂端為止。在後一種情況下,出現編輯錯誤並且一個描述這種錯誤的訊息被送到原來訊息的接受對象上。
這裡有一個接受對象的特殊的句法形式——super,它改變了訊息查找的起始類,字super有兩個含義:
(1)它同self表示的是同一個對象,引起含有super的方法執行的t削息的接受對象;
(2)它使訊息查找從含有super在其中出現的方法的類的超類開始,而不是從接受對象的類開始。
向super傳送訊息的主要目的是能夠使用在超類中的一個方法,該方法在其子類中是重新定義的。
類訊息
傳送到類對象的訊息是用來創建該類的一個實例和初始化類變數,最普通的創建一個新實例的訊息是new和new:,有些類定義自己的創建實例的訊息。
同所有的對象一樣,類知道它們可回響哪些訊息。對於其他對象來說,可用的方法是由對象的類確定的。類對象也屬於一個“類”,稱之為元類(metaclasS),它確定了類對象可回響的訊息。
有三個與元類有關的重要的類:
(1)Metaclass——所有元類的類;
(2)class——類Metaclass的所有實例的超類;
(3)每個元類只有一個實例:以它為元類的類。
聲明一個新類
要想增設一個新類,首先要選擇它的超類,並使它成為所選超類的子類,然後加上新的實例變數和方法。類是通過向新類(或要修改的類的超類)傳送訊息來定義的,該訊息將以類的規格說明信息作為變元。要聲明的類的信息如下:
類名。
類的對象是否存放指針、字或位元組。
類的對象是否含有索引實例變數。
類的對象的有名實例變數名字。
所有類的對象可用的類變數名字。
該類的對象和其他類的對象可用的共享變數的共享字典的名字。
訊息和方法
smalltalk的處理都採用向對象傳送訊息的方式。訊息是為了表示用戶對對象的計算要求所使用的互動式語言。而方法又是對象回響訊息而完成的算法。一個類協定有兩部分——類方法和實例方法。
類方法實現那些傳送到類的訊息,類訊息的接受者總是類對象,而不是類的實例。所有的類都是全局變數並可用它們的類名來引用。
實例方法實現那些傳送到實例的訊息,實例訊息的接受者總是一個類的實例對象。
方法包含一系列smalltalk表達式,其中包括:
1.文字
#aSymbol #(1 2 4 16) ’magic`。
2.變數名
Smalltalk x replacementCollection
3.訊息表達式
bag add:Stream next
100 factorial
array at:index+10 put:Bay new
4.代碼塊
[:x:y I x name<y name]
方法的首部定義了方法名、變元和要用到的暫時變數。
傳送一個訊息包括:
(1)識別訊息的接受者;
(2)識別包含在訊息中的附加對象(訊息的變元);
(3)聲明要執行的操作(訊息選擇器);
(4)接受作為訊息回答而返回的單個對象。
繼承性多態性
許多smalltalk的功能來自於它的類層次。每個類具有一個直接的超類,並且可能會有一個或多個子類。類object在這個類層次的最頂端。您也許在生物學中已經熟悉了這種體系結構,生物根據每個生物類的共同特性進行分類組織。在類層次中較上層的類代表更一般的特性,而在類層次中較下層的類表示更特殊的特性。例如,魚和樹比大比目魚和楓樹更抽象。
前面我們已經看到smalltalk是怎樣用類來組織它的源碼(方法)的。在本節中您將看到如何使用抽象的類來開發一般性問題的解,然後開發出更為特定套用的解,它可以通過在子類中加少量的源碼來使一般性的解“具體化”。