軟件設(shè)計(jì)師知識(shí)點(diǎn):面向?qū)ο笳Z(yǔ)言概論(三)

字號(hào):

傳統(tǒng)的基于類的面向?qū)ο笳Z(yǔ)言的一個(gè)主要特點(diǎn)就是inheritance, subclassing和subtyping之間的密不可分的聯(lián)系。很多的面向?qū)ο笳Z(yǔ)言的語(yǔ)法,概念,就是從這三者而來的。比如說,通過subclassing, 你可以繼承父類的一些方法,而同時(shí)你又可以在子類中改寫父類的方法。這個(gè)改寫過的方法,通過subtyping, subsumption, 又可以從一個(gè)類型是父類的對(duì)象去調(diào)用。
    但是,inheritance, subclassing, subtyping這三者并不是永遠(yuǎn)和睦相處的。在一些場(chǎng)合,這三者之間的糾纏不清會(huì)妨礙到通過繼承或泛型得到的代碼重用。因此,人們開始注意到把這三者分離開來的可能性。區(qū)分subclassing和subtyping已經(jīng)很常見了。而其它的一些方法還處于研究的階段。這一章我們將介紹這樣一些方法。
    一,對(duì)象類型
    在早期的面向?qū)ο笳Z(yǔ)言中(如Simula), 類型的定義是和方法的實(shí)現(xiàn)是混合在一起的。這種方式違反了我們今天已經(jīng)被廣泛認(rèn)識(shí)到的把實(shí)現(xiàn)和規(guī)范(Specification) 分離的原則。這種分離得原則在開發(fā)是團(tuán)隊(duì)進(jìn)行的時(shí)候尤其顯得重要。
    更近期一些的語(yǔ)言,通過引入不依賴于實(shí)現(xiàn)的對(duì)象類型來區(qū)分實(shí)現(xiàn)和規(guī)范。Modula-3以及其它如Java等的支持class和interface的語(yǔ)言都是采用的這種技術(shù)。
    在本書中,我們開始引入InstanceTypeOf(cell)時(shí),它代表的概念相當(dāng)有限??瓷先ィ坪踔槐硎居胣ew cell生成的對(duì)象的類型,于是,我們并不能用它來表示從其它類new出來的對(duì)象。但后來,當(dāng)我們引入了subclassing, method overriding, subsumption和dynamic dispatch之后,事情變得不那么簡(jiǎn)單了。我們的InstanceTypeOf(cell)已經(jīng)可以用來表示從cell的子類new出來的對(duì)象,這些對(duì)象可以包括不是cell類定義的屬性和方法。
    如此看來,讓InstanceTypeOf(cell)依賴于一個(gè)具體的類似乎是不合理的。實(shí)際上,一個(gè)InstanceTypeOf(cell)類型的對(duì)象不一定會(huì)跟class cell扯上任何關(guān)系。
    它和cell類的共同之處只是它具有了所有cell類定義的方法的簽名(signature).
    基于這種考慮,我們可以引入對(duì)象類型的語(yǔ)法:
    針對(duì)cell類和reCell類的定義:
    class cell is
    var contents: Integer :=0;
    method get(): Integer is
    return self.contents;
    end;
    method set(n:Integer) is
    self.contents := n;
    end;
    end;
    subclass reCell of cell is
    var backup: Integer := 0;
    override set(n: Integer) is
    self.backup := self.contents;
    super.set(n);
    end;
    method restore() is
    self.contents := self.backup;
    end;
    end;
    我們可以給出這樣的對(duì)象類型定義:
    ObjectType Cell is
    var contents: Integer;
    method get(): Integer;
    method set(n:Integer);
    end;
    ObjectType ReCell is
    var contents: Integer;
    var backup: Integer;
    method get(): Integer
    method set(n: Integer);
    method restore();
    end;
    這兩個(gè)類型的定義包括了所有cell類和reCell類定義的屬性和方法的類型,但卻并不包括實(shí)現(xiàn)。這樣,它們就可以被當(dāng)作與實(shí)現(xiàn)細(xì)節(jié)無關(guān)的的接口以實(shí)現(xiàn)規(guī)范和實(shí)現(xiàn)的分離。兩個(gè)完全無關(guān)的類c和c’, 可以具有相同的類型Cell, 而Cell類型的使用者不必關(guān)心它使用的是c類還是c’類。
    注意,我們還可以加入額外的類似繼承的語(yǔ)法來避免在ReCell里重寫Cell里的方法簽名。但那只是小節(jié)罷了。
    二,分離Subclassing和Subtyping.
    在我們上一章的討論中,subtype的關(guān)系是建立在subclass關(guān)系的基礎(chǔ)上的。但如果我們想要讓type獨(dú)立于class, 那么我們也需要定義獨(dú)立于subclass的subtype.
    在定義subtype時(shí),我們又面臨著幾種選擇:subtype是由類型的組成結(jié)構(gòu)決定的呢?還是由名字決定呢?
    由類型的組成結(jié)構(gòu)決定的subtype是這樣的:如果類型一具有了類型二的所有需要具備的屬性和方法,我們就說類型一是類型二的subtype.
    由類型名字決定的subtype是這樣的:只有當(dāng)類型一具有了類型二的所有需要具備的屬性和方法, 并且類型一被明確聲明為類型二的subtype時(shí),我們才認(rèn)可這種關(guān)系。
    而如果我們的選擇是一,那么那些屬性和方法是subtype所必需具備的呢?哪些是可有可無的呢?
    由組成結(jié)構(gòu)決定的subtype能夠在分布式環(huán)境和object persistence系統(tǒng)下進(jìn)行類型匹配(譯者注:對(duì)這點(diǎn),我也不甚明了??磥?,紙?jiān)斓眠€是不夠)。缺點(diǎn)是,如果兩個(gè)類型碰巧具有了相同的結(jié)構(gòu),但實(shí)際上卻風(fēng)馬牛不相及,那就會(huì)造成錯(cuò)誤。不過,這種錯(cuò)誤是可以用一些技術(shù)來避免的。
    相比之下,基于名字的subtype不容易精確定義,而且也不支持基于結(jié)構(gòu)的subtype.
    (譯者按,這里,我無論如何和作者找不到同感。基于結(jié)構(gòu)的subtype的缺點(diǎn)是一目了然,不過完美的避免的方法我卻看不出來。而基于名字的subtype為什么就不能精確定義呢?C++/Java/C#, 所有流行的OO語(yǔ)言都只支持基于名字的subtype, 也沒有發(fā)現(xiàn)有什么不夠靈活的地方。需要在不同名字但類似結(jié)構(gòu)的類型之間架橋的話,adapter完全可以勝任嘛!)
    目前,我們可以先定義一個(gè)簡(jiǎn)單的基于結(jié)構(gòu)的subtype關(guān)系:
    對(duì)兩個(gè)類型O和O’,
    O’ <: O 當(dāng) O’ 具有所有O類型的成員。O’可以有多于O的成員。