精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
面向對象軟件開發和過程(四)

面向對象軟件開發和過程(四)

重用

級別: 初級

林星, 項目經理

2003 年 12 月 01 日

重用是面向對象開發中的一個非常重要的特性,由于重用的特點,它能夠降低開發投入,并提高軟件的質量。那么,在面向對象開發中,究竟該如何掌握重用呢?又該如何將重用應用到開發過程中呢?

上一章中所討論的分析框架是一種清晰的分析方法,但是接下來的內容中我們卻不能夠完全使用這個框架。一來內容較多,二來分析框架需要結合實際情況才有意義。所以在下面的討論中,我們仍然會按照框架的思路來處理問題,但并不嚴格的描述所有的框架要素。

我們把面向對象中的問題分為以下一些主題,每個主題都體現出了面向對象技術的價值,我們會針對每一個主題進行框架分析,在后續的篇幅中,我們準備討論四個主題:

  • 重用
  • 優化代碼組織
  • 針對契約設計
  • 業務建模

對于我們來說,最重要的是要了解面向對象技術對一個軟件開發過程有何幫助,所以,我們不是像一般的面向對象的教科書那樣從繼承、多態開始學習。我們的精力應該放在過程和設計上。

這四個主題涉及了大量的面向對象相關知識,它們都能夠大幅度的提高軟件的質量,同時降低人員之間的溝通成本:

重用和優化代碼組織有很多的關聯,例如,代碼組織的優化能夠幫助重用的實現,而重用的思路能夠引導代碼組織的方向。

針對契約設計為嚴謹的軟件設計提供了一種可行的操作思路。例如,針對契約設計就有利于業務模型的質量。

業務建模是OOAD方法的核心,業務模型定義了軟件的設計原型,它的好壞直接影響到軟件的質量。

后續文章我將逐步分析這四個方面。

1. 重用的概念。

在面向對象中,重用是一種基本的思路。XP方法的最佳實踐-重構的一個重要目標,就是使同樣功能的代碼只出現一次。這就是典型的實現重用,這種做法有兩個好處,一是帶來可重用的代碼,二是減少維護的成本。

繼承和泛型(或是模板)是兩種很基本的重用技術。為了能夠清楚的描述問題,首先我們要弄懂類型的概念:

1.1 類型(Type)

                                                The term type is often used in the world of value-oriented programming to mean data representation. In the                        object-oriented world it usually refers to behavior rather than to representation.                        

在計算機看來,任何數據(甚至質量)都是一些bit流,但是要人類看懂這些bit流那就太為難了。因此類型的目的就是為了能夠對bit流進行分類,告訴我們這部分的字節流表示數字,這部分的字節流表示字符串。因此,就像上面那段話中所說的,在面向數值的編程中,類型通常用作數據的表示。在Java這樣的強類型語言中,在編譯期,每一個變量和表達式都有一個類型與之相對應??赡苡行┤擞X得不方便(例如VB程序員),但是這種機制能夠有效的避免錯誤。所以我本人比較喜歡強類型的語言,雖然有時候它并不是很方便,但是它能夠使我避免許多錯誤。如果你開發一個企業應用,強類型的語言雖然速度上可能稍遜于弱類型語言,但是強類型語言帶來的嚴謹性是更重要的。

在面向對象中,類型除了用于表述值的含義之外,還包括了在這個值上的各種操作。所以呢,那句話中又說,在面向對象的世界中,類型更重要的是引出行為。在現實世界中,類型總是伴隨著一定的特性的,所以僅靠數值類型是無法描述多姿多彩的現實世界的。

在OO中,類可以是一個類型,在一些純OO語言中,例如Eiffel、SmallTalk,類代表了全部,但是在一些為了向前兼容的OO語言中,類類型和非類的類型是區分開來的,典型的代表是Java和C#這兩種語言。在Java中,包括引用類型(類類型)和原生類型兩大類類型,原生類型包括布爾值、數字值。在C#中,類型也分為值類型和引用類型,為了統一的表示兩者,還引入了Boxing和UnBoxing的操作。由于篇幅所限,我們不可能在這個話題上花費太多的時間,如果要深入了解的話,可以參考Java的虛擬機規范。

在引入泛型機制之后,類可以接受各種各樣的類型,那類是否還能夠表示一個類型呢。這個問題等到我們討論泛型機制的時候再談。

1.2 繼承

面向對象系統中最主要的元素就是類型,因此面向對象中的抽象的主要形式是類型抽象,也就是使用類型來表示現實生活中的各種事物的形式。

一般我們認為繼承可以分為兩種基本的形式:實現繼承和接口繼承。實現繼承的主要目標是代碼重用,我們發現類B和類C存在同樣的代碼,因此我們設計了一個類A,用于存放通用的代碼,基于這種思路的繼承稱為實現繼承。但接口繼承不同,它是基于現實生活中的語義的,表現了IsA的關系。例如,我們認為存款帳戶和結算帳戶都是帳戶的子類,這種繼承我們稱之為接口繼承。注意,有些文章中一個類實現一個接口的行為定義為接口繼承,這和我們討論的接口繼承是不同的概念,為了區分兩種概念,我們可以使用接口繼承的另一種稱呼-類型繼承。繼承的關鍵就在于如何靈活的運用兩種繼承方式。

實現繼承是實現代碼復用的關鍵技法,雖然接口繼承也能夠提供一定的代碼復用,但是效果不如前者。但是,這決不意味著接口繼承不如實現繼承。相反,我認為接口繼承能夠提供更優秀的重用性(這一點我們在下一篇中就能夠看到),因為接口繼承更為抽象,能夠適用更大的范圍。接口就是典型的例子,由于它什么代碼都沒有,什么都不做,所以接口的抽象性是最好的。這時候我突然想到"言多必失"這句話,軟件設計是不是很有一些哲學的味道?但事實上,我們是兩種繼承方法同時使用,以達到最佳的效果。

繼承能夠獲得比委托(委托將會在下文提到)更大的靈活性,但是這種靈活性并不是沒有代價的。在一個軟件團隊中,繼承的靈活性可能會帶來代碼的混亂或失控。Effective Java的條款14給我們的建議是組合(也就是我們指的委托)要優先于繼承,一個重要的理由是繼承破壞了封裝性,因為子類需要了解父類的相關信息。

我們做軟件設計的思路有兩種,如果我們希望客戶端的調用簡單一些,那么服務類就比較復雜,反之,如果希望服務類簡單一些,那么客戶端的調用就會復雜一些。繼承的語言也面臨類似的問題。Java、C++都傾向于將繼承的語法簡單化,由編譯器來負責繼承語法的分析。但是這種做法會出現一些潛在的問題。例如,脆弱基類(fragile base class)的問題。這個問題說的是當在基類中增加新的功能時,可能會對現存的類產生影響,當基類中增加一個虛方法時,而子類也存在一個同名的虛方法,那么子類中的同名方法就會替換新加入的方法。這種做法是合法的,因此編譯器是不會報錯的,但這可能造成一些潛在的問題。出現這樣的問題是我們在書寫繼承語法時的自動化程度比較高,我們不需要對各個方法進行顯示的指定,而是按照既定的規則進行。而Eiffel語言的做法則不同,Eiffel語言為繼承定義了各種各樣的語法。在C#語言中,也有類似作用的關鍵字。

繼承是面向對象中非常重要的技巧,而繼承樹的設計也特別的重要,在一些單根繼承的語言中更是如此,因為父類只有一個,不能夠輕易的使用繼承。一般來說,繼承樹在一個設計決策中占有非常重要的位置,是需要重點考慮的。這里是設計的重點。

繼承本身并不是什么特別難的技巧,但是要能夠把繼承運用的好卻是很難的。在一個軟件開發團隊中,不同的成員有著不同的背景,不同的知識,對繼承、面向對象也有不同的看法,如何協調這么多的要素,以保證設計的一致性呢?面向對象的老手和新手的最大區別,往往就表現在繼承的處理上。在軟件過程中,我比較提倡由架構師或老鳥級的程序員來負責主要的繼承層次的設計。這樣,軟件的結構不容易亂,千萬不能夠象以前的非面向對象代碼那樣做一刀切,把不同模塊交給不同人了事,這樣最后代碼一定失控。

繼承的設計往往是比較難以進行測試的。有其是那些為了擴展的目的設計的類,因為父類中的代碼往往只是最終功能的一部分。這里有一個測試的思路,如果在你的代碼中還必須實現子類,那么針對子類進行測試。如果你的代碼中不實現子類,那么請設計一個測試子類,并對測試子類進行測試。

1.3 泛型

如果說繼承實現了子類型的多態的話,那么泛型則是實現了參數的多態,兩者都是抽象機制的重要組成部分,兩者能夠在一定程度上相互替代,因此一種觀點認為泛型是能夠在很大程度上替代繼承,在C++的標準模板庫中,泛型就被大量的使用。但泛型的思路和繼承的思路仍有差別,繼承往往代表了不同的算法,但泛型往往代表了相同的算法,不同的類型。這也是為什么STL中大量使用泛型的原因,因為算法是類似的。泛型對我們最大的好處是引入了靜態(編譯期)類型檢查?;叵胛覀僇ava語言中的很多容器都是用于容納Object類型的,其目的是為了讓容器更加的通用,但是導致了一個問題,編譯器不會檢查你放入容器的到底是一個什么東西,比如我們把人放到存放貨物的容器中,雖然違反了現實世界中的規則,但編譯器仍然放行。

                        Stack s = new Stack();                        s.Push(new Product());                        Employee e = (Employee) s.Pop();                        

多么惡劣的行為,容器中裝的是貨物,可出來的卻是人,有販賣人口的嫌疑吧,但對編譯器來說完全合法,而在運行期間會出現問題,因為類型轉換是非法的。這種方法能夠得到大量運用的關鍵技術幾乎所有的對象都是繼承自一個根對象(例如Object)。這在一定程度上實現了泛型,但這種行為不值得提倡。

這時候,類型的不安全性對軟件質量造成了影響。使用泛型就不會出現這樣的尷尬局面,編譯器會幫助你檢查類型,保證類型安全。泛型的另一個好處是減少了類型轉換。從某個角度上來說,類型轉換是一種非安全的編程方法,在現實生活中很少看到這種現象(基因突變),但是在編程語言中,我們大量使用這種技巧,更多時候是被迫的。

Java中的泛型機制

在名為"猛虎"的J2SE1.5中,Java引入了新的泛型機制(Java在1.4版本中就引入了泛型機制)。Java是否該引入泛型機制一直都是爭論的焦點,畢竟,泛型機制是一種非常優秀的抽象方法。引入了泛型機制的Java語言看起來像是這樣的:

                        static void someAction(Collection<String> c){                        for (Iterator<String> I=c.iterator();i.hasNext();)                        doSomeAction();                        }                        

從Java核心團隊的兩名成員-Joshua Bloch和Neal Gafter的談話(http://developer.java.sun.com/developer/community/chat/JavaLive/2003/jl0729.html)來看,Tiger只是對泛型機制做了一些編譯器上的處理,例如編譯器幫助你檢查類型安全,從集合返回值時無需對類型進行轉化。不管如何,對程序員來講,這就足夠了,不是嗎?

泛型解決了我們的一大問題,我們可以定義更加嚴格、自然的類型處理機制。而不是把類型轉換來轉換去。在軟件開發過程中,盡可能引入泛型機制來解決現實中的問題。在業務建模中,利用泛型機制來描述需要,能夠更加準確的解決問題。例如,當我們對書店中貨架建模的時候,我們規定書架上只能夠存放書和CD之類的產品。因此,我們使用下面的方法:

                        class SHELF[I->SHELF_ITEM]                        feature                        put(item:I) is                        …                        end                        book_shelf:SHELF[BOOK]                        CD_shelf:SHELF[CD]                        

以上的代碼是使用Eiffel語言編寫,SHELF類只能夠存放SHELF_ITEM類型的物品。因此其子類BOOK、CD都是合法的,但是其它的物品,例如生肉,那就是非法的。試想如果我們仍然使用類型轉換的方式的話,那么我們的SHELF又變成了百寶箱了,除了能夠存放書和CD,還能夠存放鋼琴、演員、核武器。哈!真是太牛了。

現代的語言都將引入或將要引入泛型機制,看來泛型機制成為通用技術的日子已經不遠了。

繼承和泛型是兩種方向上的重用技術,繼承提供的是類型的縱向擴展,而泛型提供的是類型的橫向抽象。兩種技術都能夠提供優秀的重用性。而對于我們來說,關鍵的問題仍然在于,如何在開發過程中引入這些技術。





2. 規范

為重用定義規范是一件困難的事情。很難定義一個規范,要求開發人員必須按照某種方式來設計繼承樹或泛型類。但是,繼承和泛型保持統一的風格是較好的處理方式。所以,合適的做法是采用指南和范例的形式。





3. 技能

不論是繼承還是泛型,都要求有豐富的面向對象的編碼和設計經驗。重用的目標對設計師和程序員的要求很高,如果組織中的人員沒有能夠達到這種要求,那么就需要考慮在人員技能上進行強化。

對于泛型來說,學習泛型機制意味著原先處理集合的思路發生了變化,需要在一個新的抽象高度上理解集合操作。





4. 組織

重用涉及到設計的問題。所以對于組織來說,問題在于,誰負責設計。正如我們在技能這一節中看到的,重用對技術的要求很高,我們無法要求組織的任何一個人都達到這種要求,所以,較好的方式是,把重用技術的職責交給合適的設計人員,由他們負責對軟件的整體結構進行重用上的設計。

重用的最終目標是能夠在軟件組織范圍內建立起一個框架,軟件組織能夠利用這個框架降低開發成本,提高開發質量。





5. 過程

我們之前討論繼承時,曾經提到,由架構師或老鳥級的程序員來負責主要的繼承層次的設計。重用最能夠發揮效用的地方是在設計的時候考慮重用。所以這就需要過程上的保證了,在設計活動中,如果需要使用到繼承或是泛型技術,那么就需要有相應的設計、測試方案、復審、文檔化的過程,并確保設計思路能夠順利的形成代碼。形成代碼的思路有兩種,一種是由程序員根據設計編寫代碼,另一種是設計人員自己編寫實現代碼。我比較傾向于第二種思路。第一種思路會產生很多額外的溝通成本,造成成本的浪費,影響代碼的質量。





6. 工具

Java語言能夠優雅的支持繼承和泛型,大部分的面向對象語言都能夠支持繼承,在未來,泛型也將成為標準的技術。

C++中將泛型實現為模板的方式,在軟件開發中,模板的運用是一個非常巧妙的方法。在企業應用程序中,數據庫的CRUD方法是非常常用的,但是不同表、不同目的的CRUD方法都有少許的不同。如果使用繼承或委托等方式來進行代碼級別的重用,會造成代碼的意圖不清晰,令人難以理解。所以,這些方式難以得到好的效果,但是我們同時又應該看到,不同的代碼塊之間的重復程度也是很高的。所以,我們想到能不能象泛型機制那樣,對代碼本身進行抽象呢?例如,一個標準的Create方法中,方法的流程基本相同,但是使用的值對象、表名、連接等都有所不同,我們把這些看作參數,將嵌入參數的代碼制作為模板,并提供這些參數的使用說明和范例。在使用的時候,只要用具體的值替換相應的參數就可以了。很多的Case工具中都支持模板。這種方法要比你寫大量的指導性文檔要好的多,原因在于它的操作性很強,程序員很容易接受。

本站僅提供存儲服務,所有內容均由用戶發布,如發現有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Java入門(2) 面向對象的程序設計
Java面向對象詳解
Java程序員應該了解的10個面向對象設計原則
面試官:什么是面向對象?
Java 面試參考指南( 一 )
談談java中的構造函數
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯系客服!

聯系客服

主站蜘蛛池模板: 阿巴嘎旗| 宁津县| 讷河市| 巴中市| 亳州市| 剑河县| 方正县| 陵水| 盐源县| 饶河县| 儋州市| 开化县| 富源县| 营口市| 正安县| 土默特右旗| 图们市| 渝中区| 鄯善县| 大宁县| 新建县| 南澳县| 庆阳市| 军事| 深泽县| 溧阳市| 沭阳县| 莱西市| 铅山县| 沁阳市| 太康县| 灌阳县| 西充县| 甘孜县| 隆安县| 新丰县| 江源县| 河北区| 舟曲县| 麻江县| 光山县|