兩位研究面向對象軟件工程的美國學者 (Stave Halladay和Michael Wiebel) 曾這樣說:「通常的面向對象編程(OOP)思路不過是一批烏合之衆,把靈機一動、隨機應變的技巧用於他們絞盡腦汁抽象出來的‘對象’而已。即便是最優秀的 OOP 程序員,他們所能對付的極限也莫過於中等規模的開發項目。假若程序員經驗不足,系統規模又很大,那麼採用 OOP 只能把你引入漫無邊際的泥沼之中。」 一方面是幾乎沒有一位軟件工程學者認爲 OOP 是天衣無縫的,另外一方面是 OOP 勢如破竹,近乎每一種最新推出的程序開發工具或語言都採用了 OOP 思路;一方面是愈來愈多的「烏合之衆」在毫無章法、爲所欲爲地處理着「對象」,另外一方面是通過近 30 年的積累已經擁有了最大多數用戶的結構化軟件方法的日漸萎縮……面對這一現實,研究軟件工程方法學的專家們紛紛指出:「當前擺在軟件開發方法學面前的一個重要課題是:從理論上理解 OOP 具備強大生命力的自然合理性,並完善面向對象軟件工程方法學體系。」
一年來咱們經過國內外一些實用系統的開發實踐,對面向對象的軟件工程方法進行了較爲深刻的學習和探討,特別是在北京市公路局計算機系統的一期工程實踐中,借鑑國外軟件設計經驗,較系統地採用了面向對象軟件工程方法,受益不淺。
1、 是「設計主導」仍是「程序主導」
在一個系統開發過程當中是隻採用 OOP 仍是採用了OOSE(面向對象軟件工程)方法,關鍵看整個開發過程是「設計主導」仍是「程序主導」。
近年來,大量先進程序開發工具進入我國,這對提升軟件開發效率無疑具備很大的做用。然而,它們又每每使程序主導型軟件開發人員在「以程序代系統」、「以算法代設計」的誤區裏越陷越深。
通常的軟件開發人員(包括那些只見程序不見系統的程序員)主觀上都認爲:軟件開發不該「系統設計主導」而應「程序算法主導」。可是用下面幾個問題考察一下,結果每每相反。
問題1 在進行軟件設計和選擇軟件開發工具以前,是否進行開發方法學的選擇?
所謂方法學是指組織軟件生產過程的一系列方法、技術和規範。方法學是軟件開發者終年失敗和成功經驗的理論性總結,從軟件重用的思路來講,方法學重用的價值遠非某些程序組件重用可比。
以北京市公路局系統爲例。首先,在系統調查階段咱們瞭解到:這個系統要分期 (遞增式) 開發。因爲處於機構改革時期,系統生存期內的用戶需求和系統結構變因不少。這代表目標系統應該具備較強的可維護性,即每期開發成果應在後續工程中具備較高的可重用率。其次,一期工程的工做量至關大(最後成果包括 124 個模塊、72 類報表、119個數據庫表、439 個窗口、912 個數據窗口),而開發者對公路局業務不瞭解,多爲經驗不足的大學生,理解需求的能力較低。這代表採用的開發方法學必須能最大限度地減小重複勞動,實現開發過程當中的成果共享和重用;必須能支持消除需求理解偏差的調整工序,使下游成品階段的設計變動比較容易進行。
在開發此係統以前,咱們承接了一個國外軟件的下游開發任務。因爲它採用了面向對象的軟件設計,使咱們深入認識到國內外軟件開發方法學和技術上的差距,頗受啓發。
參照咱們承接的國外軟件開發工做量計算方法,即僅下游120個模塊 (含報表) 的編碼和測試爲41人月,那麼公路局系統從上游設計開始近200個模塊和報表、100多個數據庫表的開發工做量至少也應在120人月以上。因爲採用了面向對象的軟件工程方法,儘管開發人員大多經驗不足,可是第一期工程總工時最終仍控制在 80 人月之內,下降成本1/3左右。同時在系統可維護性、重用度及其餘功能和性能指標上,均超過了咱們以往採用結構化方法開發的系統。
對停留在程序主導級開發的軟件開發人員來講,他們選擇 OOP 的緣由也每每是被動的。其實,在程序主導開發者的辭典中是找不到「方法學」這一詞的,或者把「方法學」與「程序算法」混爲一談。至於把 OOP 當作是 OOSE 的所有就更不足爲怪了。
問題2 對象抽象的出發點是現實世界的問題描述,仍是可執行的實例對象?
在現實世界早期抽象階段,面向對象方法與其餘方法區別並不大,都要從現實世界的問題描述出發,即從用戶接口、問題領域的知識和經驗出發,構築現實世界的問題模型,也就是肯定目標系統是「作什麼的」。面向對象的問題分析模型從3個側面進行描述,即對象模型 (對象的靜態結構)、動態模型(對象相互做用的順序)和功能模型(數據變換及功能依存關係)。軟件工程的抽象原則、層次原則和分割原則一樣適用於面向對象方法,即對象抽象與功能抽象原則是同樣的,也是從高級到低級、從邏輯到物理,逐級細分。每一級抽象都重複對象建模 (對象識別)→動態建模(事件識別)→功能建模(操做識別)的過程,直到每個對象實例在物理(程序編碼)上所有實現。
對象抽象是從邏輯級仍是物理級出發,與開發前是否進行方法學選擇同樣,也是區分OOSE 與 OOP 的試金石。因爲許多工具或語言(如PB、C++、Motif) 都支持OOP,使一些程序級系統開發人員能夠很方便地不通過邏輯抽象就直接開發物理對象,在早期階段意識不到從物理層即實例對象出發進行系統開發的禍患,孰不知正是這種爲所欲爲的 OOP 不只沒法發揮面向對象方法應有的優越性,並且還會給開發後期帶來大量返工做業。
和以往採用結構化方法同樣,咱們在系統設計階段也引入了原型化方法,以便用系統樣品即原型與用戶對話,求得對需求理解的勾通,避免或減小後期返工。大多OOP工具都爲開發原型提供便利,問題在於原型與最終產品間的關係,即原型是邏輯對象仍是物理對象的樣品。如果後者,那就等同於最終產品。在木已成舟時再讓用戶評審,若發現問題,要麼推倒重來,要麼強迫用戶削足適履。事實上,咱們爲設計評審而基於邏輯對象開發的原型,至關部分被用戶否決。但因爲還沒有進行對象實例即物理級開發,而是使用超類對象原型統一模擬對象事件和操做,因此不管是對象模型、動態模型仍是功能模型,修改起來都不困難。
問題3 設計階段是否先設計超類,是否在實例對象設計開始以前完成超類對象的實現?
面向對象方法開發出的軟件具備較強的可重用性,這種重用包括開發項目內部的重用和外部的重用。重用依存於超類設計,沒有超類的對象系統比如「把洗衣機當米缸」,不能物盡其用。超類設計的好與很差,首先看其內部重用率的高低,內部重用率高,必然外部重用率也高。
因爲系統開發工期緊、工做量大,而咱們的開發隊伍年輕,經驗和人力都不足,內部重用率高的超類開發無疑是咱們的救星。它能夠減小重複勞動,易於統一規格,對複雜問題統一攻關、統一解決,便於統一維護。
對超類的抽象即實例對象的泛化原則,咱們是從下面幾個方面考慮的:
(1)尋找大多數實例對象的共同行爲。
例如「打印報表」、「查詢靜態代碼表」、「錄入數據庫表數據」等。
(2)超類的多態性設計要保證使用超類繼承關係能夠知足各子類的操做要求。
例如,繼承同一個「數據錄入」祖先窗口,能夠完成不一樣結構數據庫表的數據錄入。
(3)利於信息的隱蔽性,不會破壞數據的完整性,利於將複雜問題簡單化。
例如,對具備複雜關係、結構及相關存取操做的數據庫表集的維護。若是不使用一個泛化類將數據結構及其相關操做封裝起來,下層程序員要想操做有關庫表就必須對庫表設計有深刻的瞭解,而且確保程序算法設計不得破壞數據的相關一致性,這將大大增長程序設計和測試的難度,要求程序員有較豐富的經驗。而採用這種泛化類 (公用函數、公用存儲過程) 後,程序員所要作的只是發「消息」和取「輸出信息」了。
(4)有利於推行開發規範,統一界面風格。
咱們在開發國外軟件中受到的最大磨練是:國外對用戶界面 (報表、屏幕) 一絲不荀的嚴格要求。全部屏幕按鈕的高、寬、起始位置都用精確到小數點後 3 位的 X、Y 座標進行規定。這樣出來的產品令人看上去就有賞心悅目之感。可是若是人人都作界面窗口、按鈕的精細調整,工做量勢必成倍增加。採用屏幕界面模版超類的繼承關係,結合特化處理,問題即可迎刃而解。
顯然,超類的設計和實現必須在程序員廣泛進行實例對象開發以前完成。也就是說,OOSE 的上游系統設計人員必須文武 (設計與編程) 雙全,可以擔負起超類對象的程序實現與測試任務,這與結構化方法的上層系統設計人員基本能夠不編程有所不一樣。同時,超類對象在下游開發過程當中必須常常吸取特化過程當中的反饋(包括來自用戶的反饋),進行相應的調整修改。因此OOSE擔任超類對象設計與實現的設計人員很難像結構化方法那樣進入編程階段後就能夠稍事輕鬆,他們每每始終離不開編程現場。
若是設計階段不預先設計和開發出超類對象,在同一項目的多數開發者之間沒有能夠共同繼承的祖先對象,甚至在各個開發人員本身的做用範圍內都不使用繼承關係,那麼這不只不是OOSE,就連稱之爲OOP都很勉強。
問題4 如何處理對象模型面向對象關係數據庫模式的映射?
面向對象的數據庫設計方法能夠用於各類數據庫,如層次型、網絡型、關係型,固然也包括面向對象型。OOSE 中的數據庫設計無疑必須採用面向對象的數據庫設計方法。
數據庫設計也稱數據庫模式,基本上由3個層次的模式構成:從特定DB應用角度來看待DB設計的外部模式;從組織或企業角度出發進行的DB設計即概念模式;處理對應特定 DBMS 特徵與侷限性的DB設計即內部模式。具體而言,內部模式是數據庫的SQL定義,邏輯模式是表集合的邏輯定義,外部模式是從特定應用角度看的局部DB。外部模式與邏輯模式之間的接口是視圖、存儲過程或其餘駐在服務器端的DB處理程序。
若是在抽象出的對象模型中,各個應用分別是一個或多個超類對象的子對象,那麼,選擇適當細分層次的對象模型將其映射到概念模型,是數據庫庫表對象設計的關鍵。外部模式與概念模式之間的接口越少、越簡單越好,這樣的程序設計簡單,數據庫和程序都易於維護。也就是說,局部化是個重要的設計原則。
OOP可能是數據庫的後端處理,是基於既存數據庫的。所以不管是否進行過問題世界的對象建模,以及是否將對象模型合理地映射到數據庫邏輯模式 (面向對象數據庫設計),OOP 均可以工做。
問題5 編程時是否先調查有無可重用 (繼承) 對象,是否參與下層對象對上層對象、超類對象的反饋?
埋頭於本身分擔的程序對結構化方法或許是必須的,但在面向對象方法中擔任程序設計的開發人員,應該先去調查對象數據辭典中有無其餘開發人員已經完成、本身稍加特化就可重用的對象。從整體上說,對象的共享、重用應該由上層設計人員統一管理,以便保證對象風格的一致性,避免衝突。可是,對象的獨立性、封裝性和多態性都很便於重用,這是結構化系統所不能比擬的,而重用是軟件開發方法學的最重要思想之一。上層設計人員每每不可能面面俱到,懂得軟件設計理論的開發人員,即便只開發下層程序也應採用最省力、最有效率的編程方法,即大量使用重用對象。
在繼承超類對象和重用他人對象時,若發現有設計不合理的地方,應該及時反映給對象開發的承擔者。
對上層設計人員來講,一方面應該鼓勵程序實現人員重用既存對象,另外一方面應經過開發人員共享對象數據辭典,使個別的對象重用可以當即反映到總體對象模型中,以保證設計變動時的一致性。
2、面向對象方法與結構化方法比較
分析是問題抽象 (作什麼),設計是問題求解 (怎麼作),實現是問題的解 (結果)。任何方法學對客觀世界的抽象和求解過程都是如此。在問題抽象階段,結構化方法面向過程,按照數據變換的過程尋找問題的結點,對問題進行分解。所以,與面向對象方法強調的對象模型不一樣,描述數據變換的功能模型是結構化方法的重點。若是問題世界的功能比數據更復雜或者更重要,那麼結構化方法仍然應是首選的方法學。若是數據結構複雜且變換並很少,那麼如以過程主導分析和設計,一旦有系統變動就會給下游開發帶來極大混亂。
因爲對過程的理解不一樣,面向過程的功能細分所分割出的功能模塊有時會因人而異。而面向對象的對象細分,從同一問題領域的對象出發,不一樣人得出相同結論的比率較高。
在設計上,結構化方法學產生自頂向下、結構清晰的系統結構。每一個模塊有可能保持較強的獨立性,但它每每與數據庫結構相獨立,功能模塊與數據庫邏輯模式間沒有映射關係,程序與數據結構很難封裝在一塊兒。若是數據結構複雜,模塊獨立性很難保證。面向對象方法抽象的系統結構每每並不比結構化方法產生的系統結構簡單,但它能映射到數據庫結構中,很容易實現程序與數據結構的封裝。
在軟件工程基本原則中有一條「形式化原則」,即對問題世界的抽象結論應該以形式化語言 (圖形語言、僞碼語言等) 表述出來。結構化方法能夠用數據流圖、系統結構圖、數據辭典、狀態轉移圖、實體關係圖來進行系統邏輯模型的描述;而面向對象方法可使用對象模型圖、數據辭典、動態模型圖、功能模型圖。其中對象模型圖近似系統結構圖與實體關係圖的結合,動態模型圖相似狀態遷移圖,功能模型圖相似數據流圖。
公路局系統有 100 多個數據庫表,但數據的加工 (變換) 很單純,若是當初選擇結構化方法學,狀況會怎麼樣?
在問題抽象的最初階段不會有太大差別。因爲數據變換少,能夠把對象和對象的操做當作一一對應,即最初問題描述的對象模型與功能模型基本一致。以其中計劃管理處子系統爲例,對象是計劃管理員、規劃管理員、概預算管理員、統計管理員,功能 (操做) 是計劃、規劃、概預算、統計。
問題存在於下層抽象裏。
首先,許多公共超類對象設計與結構化方法相悖,由於它破壞了過程的連續性及系統結構的邏輯層次性,把一些下層模塊及在過程分析中沒有語義的對象,放在系統結構的上層。所以若是採用結構化方法,須將繼承關係改成下層模塊調用關係。可是事實上,祖先對象的一些狀態 (屬性值) 是從主控模塊直接獲得指示而肯定的;從控制角度說,它的確處於系統的上層地位。若是採用結構化方法,結果將是要麼把系統結構變成網絡狀,失去結構化特徵,要麼放棄這種統一完成重複性勞動的設計方案。
其次,應用對象模型向數據庫概念模式的映射設計也是該系統採用面向對象方法的一個標誌。若是使用結構化方法,數據庫模式可能映射客觀世界的數據結構。因爲公路、養路單位、管理單位、路況、橋樑、隧道及道路上的綠化狀況等各實體間客觀存在着複雜的多重關係,其結果可能定義出一個像蜘蛛網似的關係庫結構,於是大大加劇了數據庫前端應用編程和數據庫維護的負擔。
總之,該系統若使用結構化方法,系統結構和數據庫結構均可能成爲網狀結構,且互相無關。而目前採用的面向對象方法,系統結構和數據庫結構都是多重繼承結構,相互存在映射關係。顯然前者較後者複雜性高、可維護性差、內部重用難度大、重用率低。
其實,不管是用什麼方法學開發軟件,交給用戶的都應該是知足用戶當前需求的軟件。用戶在短時間內不會發現開發者使用先進方法學給他們帶來的益處,卻是開發者自己因爲大大減輕了開發負擔而最早受益。可是隨着時間的推移,得到最大收益的仍是用戶,由於軟件的長期質量(包括維護成本低和生存週期長)給用戶帶來的好處纔是根本的。
3、方法學是思路不是定律
對於方法學,咱們是這樣理解的:
(1)方法學的目的是:使後人分享前人的成功,避開前人的失敗,把注意力集中在還沒有開拓領域的創造性勞動上。因此方法學與開發人員的創造性是毫不衝突的。它既不能像法律那樣靠權威來界定是非邊界,也不能像定律那樣經過證實和推理給出廣泛結論。若是必定要作比喻的話,它比如人的世界觀。
(2)沒有放之四海而皆準的方法學,任何方法學都有其侷限性,因此軟件開發人員大可沒必要拘泥於某種特定的方法學。
例如,面向對象方法的對象模型圖,這種形式化語言遠不如結構化方法的結構圖和數據流圖簡單明瞭,假若把公路局系統所有用對象模型圖表述出來,至少也要幾十頁。因爲最上層功能模型與對象模型是一致的,因此咱們採用的是結構化方法的系統結構圖。
(3)事實代表,由 OOP 帶動的 OOSE 方法確實比結構化方法更能天然地抽象現實世界,並且一些 OOP 工具確實已至關成熟。相反,結構化方法及開放平臺下的結構化程序開發工具,雖然不能說止步不前,但其近年來的進步是有限的。
(4)根據咱們的體會,對實踐 OOSE 有如下一些建議:
1 最好在選定方法學後,對全體開發人員進行一次關於面向對象方法學的培訓。
2 因爲有超類對象的提早開發工做,OOSE 的上游設計工做量比結構化方法的上游工做負擔重,時間和人力應該更充足一些。不然到下游開發後再追加或屢次修改變動超類對象,容易形成混亂和無效勞動。
3 因爲系統越大對象類越多,爲了便於內部重用和共享,應該創建電子化的對象數據辭典,以便對對象進行統一歸類管理。
4 應該有嚴格的命名規則,若是可能,應將命名規則集成到數據辭典中。
5 下層開發鋪開後,若是發現應該對某些實例對象泛化成新的超類對象,必須儘快進行新超類追加的設計,變動越快越好。
6 子對象繼承超類對象後,發現超類設計的缺陷是常有的事。開發隊伍內部應有很暢通的反饋渠道,使超類獲得及時的修正。子對象切不可輕易將超類對象封殺掉,使系統失去統一控制。聽從系統設計中定義的繼承關係進行實例對象開發應該成爲全體開發人員的理念。
7 面向對象設計的好處越到後來越顯著,特別是在系統維護和擴充方面。前端