對象入門
如果學JAVA,沒有讀透《JAVA 編程思想》這本書,實在不好意思和別人說自己學過JAVA。鑒於此,蛤蟆忙裡偷閒,偷偷翻看這本傳說中的牛書。
面向對象編程OOP具有多方面吸引力。實現了更快和更廉價的開發與維護過程。對分析與設計人員,建模處理變得更加簡單,能生成清晰、已於維護的設計方案。
這些描述看上去非常吸引人的,不過蛤蟆還是沒啥印象(至少到現在為止)。
任何事情都有兩面性,壞的一面是什麼?要享受這麼好的待遇,當然需要付出更大的努力了,要拋去之前的程序化思維,采用形象思維。而且對象的設計過程更具挑戰性,特別是創建可重復使用的對象時。
關於壞處的最後一點,設計的挑戰性,不用太擔心。我們大多數人在很多情況下根本不需要去設計自己的對象的,可以利用現有的專家已經設計出來的對象來解決自己的應用問題,這點很重要。我們要站在巨人的肩膀上,而不是去重復發明輪子,對不對?畢竟人生短暫,一晃而過。不過話說回來,如果在某一天需要的時候我們還是需要回頭看看,是站在了誰的肩膀上以及輪子發明的過程,這個思想發展的過程對個人修養以及智慧的積累是大有裨益的。
有人說,編程語言的最終目的都是提供一種抽象方法。
匯編語言是對基礎機器的抽象;FORTRAN和C是對匯編的抽象。
LISP和APL將所有問題歸納我i算法,PROLOG將所有問題都歸納為決策鏈。它們都有自己一套解決某一個問題的殺手锏,但是只要超過了力所能及的范圍就會顯得笨拙。
面向對象在之前的程序設計集成商,又跨出了一步。將問題空間中的元素以及在方案空間的表示物稱為”對象”(Object).(小伙伴們如果是程序員,那麼就狠狠親下它吧,也許這輩子你就離不開它了。)通過添加新的對象類型,可進行靈活的調整,以便與特定的問題配合。OOP根據問題來描述問題,而不是根據方案,與現實世界的對象相比,也存在共通的地方:都有自己的特征和行為。
JAVA還有個爹,叫做SmallTalk. 它爹也是Alan Kay發明的。那我們來看看JAVA它爹的創始人對於面向對象程序設計方法的總結:
l 所有東西都是對象。可將對象想象成一種新型變量;它保存著數據,但可要求它對自身進行操作。理論上講,可從要解決的問題身上提出所有概念性的組件,然後在程序中將其表達為一個對象。
l 程序是一大堆對象的組合;通過消息傳遞,各對象知道自己該做些什麼。為了向對象發出請求,需向那個對象“發送一條消息”。更具體地講,可將消息想象為一個調用請求,它調用的是從屬於目標對象的一個子例程或函數。
l 每個對象都有自己的存儲空間,可容納其他對象。或者說,通過封裝現有對象,可制作出新型對象。所以,盡管對象的概念非常簡單,但在程序中卻可達到任意高的復雜程度。
l 每個對象都有一種類型。根據語法,每個對象都是某個“類”的一個“實例”。其中,“類”(Class)是“類型”(Type)的同義詞。一個類最重要的特征就是“能將什麼消息發給它?”。
l 同一類所有對象都能接收相同的消息。這實際是別有含義的一種說法,大家不久便能理解。由於類型為“圓”(Circle)的一個對象也屬於類型為“形狀”(Shape)的一個對象,所以一個圓完全能接收形狀消息。這意味著可讓程序代碼統一指揮“形狀”,令其自動控制所有符合“形狀”描述的對象,其中自然包括“圓”。這一特性稱為對象的“可替換性”,是OOP最重要的概念之一。
5大特征,總結成一句話就是:所有東西都是對象,程序是對象的組合,對象有自己的存儲空間和一種類型,同一類對象都能接受相同的消息。
類具有自己的通用特征與行為。每個對象都隸屬於一個特定的類。
類建好後,根據情況生成許多對象。將對象作為解決問題中存在的元素進行處理。此處會碰到面向對象程序設計的一大挑戰,如何在問題實際存在的地方與實際問題進行建模的地方建立理想的一對一對應或映射關系。
我們向對象發出的請求是通過它的接口定義的,對象的類型或類 則規定了它的接口形式。類型與接口的等價或對應關系是面向對象程序設計的基礎。
例如燈這個類型:
接口規定了可對一個特定的對象發出哪些請求。但是在某個地方存在一些代碼以滿足實現,這代碼與隱藏起來的數據佳作隱藏的實現。向對象發送一條消息,對象決定如何對這條消息作出反應。
有兩個原因需要控制對成員的訪問。1、防止程序員接觸不該接觸的東西,用戶操作接口即可,不需明白這些信息。2、允許庫設計人員修改內部接口,而不會對調用者造成影響。
JAVA有三個顯示關鍵字和一個隱式關鍵字來設置類邊界:public,private,protected以及friendly。“pulibc” 意味著後續的定義任何人均可使用。”private”除了自己、類型的創建者以及那個類型的內部函數成員,其他任何人都不能訪問後續的定義信息。
蛤蟆你在干嘛?
編程思想的筆記啊~~~好吧,繼續
創建並測試好一個類後,那麼就是一個有用的代碼單位了。但是這樣重復的方案並不容易。我們先不管這個設計上的問題吧。
有了一個類,新類就可由任意數量和類型的其他對象構成。這個叫做組織了。也成包含關系,就像一輛車包含一個發動機那樣了。
如果做出一個新的數據類型後,又逼著你實現一個功能大致相同的數據結構,你會不會瘋?所以就需要繼承了,就是針對這個目標而設計的。
繼承的類與基礎類有相同的類型,有相同的接口。當然根據需要可以給繼承類添加函數什麼的。
JAVA中有extends關鍵字,暗示我們要為接口“擴展”新功能。為區分新類,需要改變原先的函數,盡管使用的函數接口未變,但它的新版本具有不同的表現。
繼承類只能改善原基礎類的函數?如實只能改善,那麼就說繼承類和基礎類是完全相同類型的接口。那麼基礎類和繼承類就存在一種等價關系了。如果圓繼承與幾何形狀,那麼“圓就是一種幾何形狀”,那肯定啊,不然就是陷入“白馬非馬”的誤區了,你看編程不光學習IT知識,還能學習哲學,高深實在是在高深。(如果說“幾何形狀就是圓”那麼就被笑話了)。
不過有些繼承類並不是只是改善了接口,會加入新的函數,所以不說完全等價,只是類似。
將衍生類的對象當做基礎類的一個對象對待。這樣只需要編寫單一的代碼,忽略類型特定細節,只與基礎類打交道。
舉個栗子:
有如下的函數繼承:
有這樣一個函數
Void doStuff(Shape s){
s.erase();
//…
s.draw();
}
該函數與任何Shape通信,獨立於它要draw和erase的任何特定類型的對象。
使用如下的代碼是不會有什麼問題的
Circle c = new Circle();
Triangle t = new Triangle();
Line l= new Line();
doStuff(c);
doStuff(t);
doStuff(l);
因為doStuff能發給一個消息給Shape,Circle也能接受的,所以上面函數是OK的。
我們只要知道他是Shape,那麼它必定可以進行doStuff函數的操作。如果此處需要根據圓形,三角形來進行不同的操作那不是很糟糕?對不對?
當draw消息發給一個匿名Shape時,根據Shape句柄當時鏈接的實際類型,會相應的采取正確的操作。將一條消息發給對象時,如果並不知道對方的具體類型是什麼,但采取的行動同樣是正確的,這種情況叫做多形性(Polymorphism). 用以實現多形性的方法叫作動態綁定。編譯器和運行期系統會負責對所有細節的控制;我們只需知道會發生什麼事情。在C++中好像動態綁定需要使用關鍵字virtual,而在JAVA中就不必了,完全是自動進行。
基礎類通常希望只為自己的衍生類提供接口,不想其他任何人實際創建基礎類的對象。這個可以將類變成抽象的來實現,關鍵字 abstract。抽象方法被繼承後,必須實現,否則繼承的類也會變成抽象類。
對象可以在堆棧或者靜態存儲空間裡創建,也可以在內存池中創建。
在內存池中創建對象,除非進行運行期不然就不知道到底需要多少個對象。在面向對象的設計中,大多數問題就是簡單的創建另一種類型的對象。這個時候集合可以派上用處,集合也叫做容器。我們實現不必知道在一個集合裡容下多少東西,只需創建一個集合,以後的工作讓集合自己負責。
C++中提供了標准模板庫(STL)的形式,JAVA也用自己的標准庫提供了集合。一個常規集合便可滿足人們的大多數要求。所有集合都提供了相應的讀寫功能。
如果需要對集合中的一系列元素進行操作或比較 ,就需要用到繼續器Iterator,蛤蟆覺得是不是應該叫做迭代器啊??那就用E文吧,Iterator屬於一種對象,負責選擇集合內的元素,並把他們提供給集成器的用戶。通過Iterator集合被抽象成一個簡單的序列,允許我們遍歷那個序列,而無需關心基礎結構是什麼。
集合只是一個用來防止對象的儲藏所,如果能滿足我們的需要,就沒必要關心它具體是如何實現的。
JAVA中有個終極基礎類,叫做Object。單根結構的所有對象都有一個通用的接口,最終都屬於相同的類型。但是C++中並不能保證所有東西都屬於相同的基本類型。
單根結構中的所有對象都可以保證擁有一些特定的功能。利用單根結構,可以更方便地實現一個垃圾收集器。與此有關的必要支持可安裝於基礎類中,而垃圾收集器可將適當的消息發給系統內任何對象。
不過,單根結構會帶來程序設計上的一些限制,同時加大了新程序與原有C代碼兼容的按年度,所以當年C++決定放棄單根結構的做法,但是JAVA不同,JAVA是新事物,沒有包袱,所以就使用單根結構了。
集合是我們進程要用到的一個工具,所以及何庫就十分必要,可以方便的重復使用。
剛才所說,單根結構意味著、所有東西歸根結底都是一個對象。所以容納了Object的一個集合實際可以容納任何東西,這使我們對它的重復使用變得非常簡便。
不過這裡不得不涉及到造型(Cast),怎麼把這Object類型編程我們實際放入的那個類型呢?就是從普通到特殊的轉換。造型有上塑和下塑兩種,上溯是從特殊變為普通,例如Circle屬於Shape,這個是毫無疑問正確的。下塑就是Shape屬於Circle,這個就是有點不確切了,因為也有可能是Triangle.
這就需要創建一個智能集合,知道自己容器的類型是什麼。可以通過參數化類型,它們是編譯器能自動定制的類,可與特定的類型配合。參數化類型是C++一個重要的組成部分,這個是C++沒有單根結構的緣故。JAVA尚未提供參數化類型,也是由於使用了單根結構,顯得有些笨拙。不過JAVA保留了generic關鍵字,可以用於未來實現。
每個對象都要求資源才能生存,其中最明顯的就是內存資源。如果不再使用就必須將其清除,以便釋放這些資源。
JAVA中,垃圾收集器在設計時已考慮到了內存的釋放問題,垃圾收集器知道一個對象在什麼時候不再使用,然後會自動釋放那個對象占據的內存空間。由於JAVA的對象都是從單個根類繼承,所有編程要比C++簡單的多,只需要做出少量抉擇,即可客服原先存在的大量障礙。
任何事情都有兩面性,在C++中,可以在堆棧中創建對象,這樣對象得以自動清除,但是不具有在運行期間隨心所欲創建對象的靈活性。同理,垃圾收集器能自動的清理內存空間,但是帶來一個問題,JAVA程序執行期間會存在一種不連貫的因素,我永遠不能確定什麼時候啟動或者要花多長時間。
此外,JAVA比C++要簡單,C++編程中在如果可以使用C就不會去使用C++,所以C++明顯復雜。另一方面JAVA的簡單,付出的代價就是失去了效率和一定程序的靈活性。
錯誤控制一直都是設計者們需要解決的大問題。很多語言干脆將問題簡單的忽略掉,將其轉嫁給設計人員。
違例控制將錯誤控制方案內置到程序設計語言中,違例(Exception)屬於一個特殊的對象,會從產生錯誤的地方扔 出來。
在JAVA中,違例控制模塊是從一開始就封裝好的,所以必須使用它。
違例控制並不屬於面向對象的特性,雖然在面向對象的程序設計語言中,通常是用一個對象表示的。但是早在面向對象語言問世之前,違例控制就已經存在了。
計算機編程中,很多時候需要對多個任務加以控制。
多線程操作最有價值的特性之一就是程序員不必關心到底使用了多少個處理器。程序在邏輯意義上被分割為數個線程。
多個線程處理必須注意一個問題:共享資源。在試圖同時訪問一個資源的時候,就會遇到資源競爭,這就需要對資源進行鎖定。
JAVA的多線程機制已內建到語言中,使復雜的問題變得簡單。JAVA對多線程處理的支持是在對象這一級支持的,一個執行線程可表達為一個對象。
有些對象在下次啟動程序時,對象仍然,並能保留全部信息,是一件非常有價值的事情。
JAVA 提供了對有限永久性的支持,意味著我們可將對象簡單的保存到磁盤上,以後任何時候都取回。稱為有限,是由於我們仍然需要明確發出調用,進行對象的保存和取回工作。
JAVA不但可以解決傳統的程序設計問題,還能解決萬維網上的編程問題。
客戶機/服務器系統的基本思想是我們能在一個統一的地方集中存放信息資源。客戶機/服務器概述的一個關鍵在於信息是“集中存放”的。能方便地更改信息,然後將修改過的信息發放給信息的消費者。在過去,我們一般為單獨的問題采取單獨的解決方案;每次都要設計一套新方案。這些方案無論創建還是使用都比較困難,用戶每次都要學習和適應新界面。客戶機/服務器問題需要從根本上加以變革!
Web實際就是一套規模巨大的客戶機/服務器系統。
然後用戶希望,某個信息可在任何類型的計算機上顯示出來,毋需任何改動。這時的客戶端本身只是一個純粹的查看程序,連簡單計算都不行(當然這個避開了病毒的騷擾)
客戶端能力實在太弱,人們不得不對客戶端進行編程。
“服務器-浏覽器”方案可提供交互式內容,但這種交互能力完全由服務器提供,為服務器和因特網帶來了不小的負擔。服務器一般為客戶浏覽器產生靜態網頁,由後者簡單地解釋並顯示出來。用戶提交的信息通過所有Web服務器均能支持的“通用網關接口”(CGI)回傳到服務器。
許多 Web站點都嚴格地建立在CGI的基礎上,事實上幾乎所有事情都可用CGI 做到。唯一的問題就是響應時間。CGI程序的響應取決於需要傳送多少數據,以及服務器和因特網兩方面的負擔有多重。
我們按下網頁上的提交按鈕(Submit);數據回傳給服務器;服務器啟動一個CGI程序,檢查用戶輸入是否有錯;格式化一個HTML 頁,通知可能遇到的錯誤,並將這個頁回傳給我們;隨後必須回到原先那個表單頁,再輸入一遍。這種方法不僅速度非常慢,也顯得非常繁瑣。
解決的辦法就是客戶端的程序設計。運行Web 浏覽器的大多數機器都擁有足夠強的能力,可進行其他大量工作。客戶端編程意味著Web浏覽器可獲得更充分的利用,並可有效改善Web服務器的交互(互動)能力。 對客戶端編程的討論與常規編程問題的討論並沒有太大的區別。采用的參數肯定是相同的,只是運行的平台不同:Web浏覽器就象一個有限的操作系統。
客戶端編的一個問題就是插件的設計。利用插件可以方便地為浏覽器添加新功能。這些代碼的作用是告訴浏覽器“從現在開始,你可以進行這些新活動了”。但插件的編寫並不是一件簡單的任務。
在構建一個特定的站點時,並不希望涉及這方面的工作。對客戶端程序設計來說,插件的價值在於它允許專業程序員設計出一種新的語言,並將那種語言添加到浏覽器,同時不必經過浏覽器原創者的許可。由此可以看出,插件實際是浏覽器的一個“後門”,允許創建新的客戶端程序設計語言(盡管並非所有語言都是作為插件實現的)。
插件造成腳本語言的爆炸性增長。通過這種腳本語言,可將用於自己客戶端程序的源碼直接插入HTML頁,而對那種語言進行解釋的插件會在 HTML 頁顯示的時候自動激活。
腳本語言的代碼全部暴露在人們面前。
腳本語言真正面向的是特定類型問題的解決,其中主要涉及如何創建更豐富、更具有互動能力的圖形用戶界面(GUI)。
由於腳本編制語言的宗旨是盡可能地簡化與快速,所以在考慮其他更復雜的方案之前(如Java 及ActiveX),首先應想一下腳本語言是否可行。目前討論得最多的腳本編制語言包括JavaScript、VBScript以及Tcl/Tk。
JavaScript也許是目常用的,它得到的支持也最全面。很多浏覽器目前都提供了對JavaScript 的支持。有些工具還能利用 JavaScript自動產生網頁。
如果說一種腳本編制語言能解決80%的客戶端程序設計問題,那麼剩下的20%又該怎麼辦呢?目前最流行的方案就是Java。一種功能強大、高度安全、可以跨平台使用以及國際通用的程序設計語言,也是一種具有旺盛生命力的語言。
對 Java 的擴展是不斷進行的,提供的語言特性和庫能夠很好地解決傳統語言不能解決的問題,比如多線程操作、數據庫訪問、連網程序設計以及分布式計算等等。
Java 通過“程序片”(Applet)巧妙地解決了客戶端編程的問題。 程序片(或“小應用程序”)是一種非常小的程序,只能在Web浏覽器中運行。作為Web頁的一部分,程序片代碼會自動下載回來(這和網頁中的圖片差不多)。
激活程序片後,它會執行一個程序。程序片的一個優點體現在:通過程序片,一旦用戶需要客戶軟件,軟件就可從服務器自動下載回來。它們能自動取得客戶軟件的最新版本,不會出錯,也沒有重新安裝的麻煩。由於 Java 的設計原理,程序員只需要創建程序的一個版本,那個程序能在幾乎所有計算機以及安裝了 Java 解釋器的浏覽器中運行。
與腳本程序相比,Java 程序片的另一個優點是它采用編譯好的形式,所以客戶端看不到源碼。在另一方面,反編譯 Java 程序片也並不是件難事,而且代碼的隱藏一般並不是個重要的問題。大家要注意另外兩個重要的問題。
在某種程度上,Java 的一個有力競爭對手應該是微軟的 ActiveX,盡管它采用的是完全不同的一套實現機制。ActiveX最早是一種純Windows的方案。經過一家獨立的專業協會的努力,ActiveX 現在已具備了跨平台使用的能力。ActiveX 的意思是“假如你的程序同它的工作環境正常連接,它就能進入Web頁,並在支持ActiveX 的浏覽器中運行。ActiveX並沒有限制我們使用一種特定的語言。假設我們是Windows程序員,能熟練地使用象 C++、Visual Basic或者BorlandDelphi 那樣的語言,就能幾乎不加任何學習地創建出 ActiveX組件。
自動下載和通過因特網運行程序是一個病毒制造者的夢想。在客戶端的編程中,ActiveX帶來了最讓人頭痛的安全問題。點擊一個 Web站點的時候,可能會隨同HTML 網頁傳回任何數量的東西:GIF 文件、腳本代碼、編譯好的Java 代碼以及ActiveX 組件。有些是無害的;GIF文件不會對我們造成任何危害,而腳本編制語言通常在自己可做的事情上有著很大的限制。Java 也設計成在一個安全“沙箱”裡在它的程序片中運行,這樣可防止操作位於沙箱以外的磁盤或者內存區域。
ActiveX是所有這些裡面最讓人擔心的。用 ActiveX編寫程序就象編制 Windows應用程序——可以做自己想做的任何事情。下載回一個ActiveX組件後,它完全可能對我們磁盤上的文件造成破壞。當然,對那些下載回來並不限於在Web浏覽器內部運行的程序,它們同樣也可能破壞我們的系統。從 BBS下載回來的病毒一直是個大問題,但因特網的速度使得這個問題變得更加復雜。
目前解決的辦法是“數字簽名”,代碼會得到權威機構的驗證,顯示出它的作者是誰。這一機制的基礎是認為病毒之所以會傳播,是由於它的編制者匿名的緣故。所以假如去掉了匿名的因素,所有設計者都不得不為它們的行為負責。這似乎是一個很好的主意,因為它使程序顯得更加正規。
Java 通過“沙箱”來防止這些問題的發生。Java 解釋器內嵌於我們本地的Web浏覽器中,在程序片裝載時會檢查所有有嫌疑的指令。特別地,程序片根本沒有權力將文件寫進磁盤,或者刪除文件(這是病毒最喜歡做的事情之一)。
Web是解決客戶機/服務器問題的一種常用方案。對於傳統的客戶機/服務器模式,面臨的問題是擁有多種不同類型的客戶計算機,而且很難安裝新的客戶軟件。但通過 Web浏覽器和客戶端編程,這兩類問題都可得到很好的解決。
若一個信息網絡局限於一家特定的公司,那麼在將Web技術應用於它之後,即可稱其為“內聯網”(Intranet),以示與國際性的“因特網”(Internet)有別。內聯網提供了比因特網更大的安全級別,因為可以物理性地控制對公司內部服務器的使用。
安全問題將我們引入客戶端編程領域一個似乎是自動形成的分支。若程序是在因特網上運行,由於無從知曉它會在什麼平台上運行,所以編程時要特別留意,防范可能出現的編程錯誤。
面臨客戶端編程問題令人困惑的一系列解決方案時,最好的方案是先做一次投資/回報分析。總結出問題的全部制約因素,以及什麼才是最快的方案。由於客戶端程序設計仍然要編程,所以無論如何都該針對自己的特定情況采取最好的開發途徑。
在傳統意義上,服務器端編程是用Perl 和CGI腳本進行的,但更復雜的系統已經出現。其中包括基於Java 的Web服務器,它允許我們用Java進行所有服務器端編程,寫出的程序就叫作“小服務程序”(Servlet)。
Java 實際是一種常規用途的程序設計語言,可解決任何類型的問題,至少理論上如此。將視線從程序片身上轉開(同時放寬一些限制,比如禁止寫盤等),就進入了常規用途的應用程序的廣闊領域。這種應用程序可獨立運行,毋需浏覽器,就象普通的執行程序那樣。
在這兒,Java 的特色並不僅僅反應在它的移植能力,也反映在編程本身上。就象貫穿全書都會講到的那樣,Java 提供了許多有用的特性,使我們能在較短的時間裡創建出比用從前的程序設計語言更健壯的程序。但要也要付出一些代價,其中最明顯的是執行速度放慢了(盡管可對此進行多方面的調整)。Java 本身也存在一些限制,使得它不十分適合解決某些特殊的編程問題。Java 是一種正在快速發展的語言,隨著每個新版本的發布,能充分解決的問題也變得越來越多。
面向對象的范式是思考程序設計時一種新的、而且全然不同的方式。事實上,可以作出一個“好”的設計,它能充分利用 OOP提供的所有優點。
OOP 的全部宗旨就是讓軟件開發的過程變得更加容易。
大多數方法都設計用來解決最大范圍內的問題。存在一些特別困難的項目,需要付出更為艱辛的努力。但是,大多數項目都是比較“常規”的,所以一般都能作出成功的分析與設計,而且只需用到推薦的一小部分方法。
無論多麼有限,某些形式的處理總是有益的,這可使整個項目的開發更加容易,總比直接了當開始編碼好!
時刻提醒自己注意以下幾個問題:
(1) 對象是什麼?(怎樣將自己的項目分割成一系列單獨的組件?)
(2) 它們的接口是什麼?(需要將什麼消息發給每一個對象?)
在確定了對象和它們的接口後,便可著手編寫一個程序。出於對多方面原因的考慮,可能還需要比這更多的說明及文檔,但要求掌握的資料絕對不能比這還少。
整個過程可劃分為四個階段,如下描述。
第一步是決定在後面的過程中采取哪些步驟。如果計劃本來就是“直接開始開始編碼”,那樣做當然也無可非議(若對自己要解決的問題已有很透徹的理解,便可考慮那樣做)。但最低程度也應同意自己該有個計劃。
有些程序員寫程序時喜歡隨心所欲,他們認為“該完成的時候自然會完成”。這樣做剛開始可能不會有什麼問題,但我覺得假如能在整個過程中設置幾個標志,或者“路標”,將更有益於你集中注意力。在達到了一個又一個的目標,經過了一個接一個的路標以後,可對自己的進度有清晰的把握。
在過程化中這個階段稱為“建立需求分析和系統規格”。需求分析的意思是“建立一系列規則,根據它判斷任務什麼時候完成,以及客戶怎樣才能滿意”。
系統規格則表示“這裡是一些具體的說明,讓你知道程序需要做什麼(而不是怎樣做)才能滿足要求”。需求分析實際就是你和客戶之間的一份合約(即使客戶就在本公司內部工作,或者是其他對象及系統)。
系統規格是對所面臨問題的最高級別的一種揭示,我們依據它判斷任務是否完成,以及需要花多長的時間。由於這些都需要取得參與者的一致同意,所以盡可能地簡化它們,最好采用列表和基本圖表的形式以節省時間。
我們特別要注意將重點放在這一階段的核心問題上,不要糾纏於細枝末節。這個核心問題就是:決定采用什麼系統。對這個問題,最有價值的工具就是一個名為“使用條件”的集合。對那些采用“假如……,系統該怎樣做?”形式的問題,這便是最有說服力的回答。
盡可能總結出自己系統的一套完整的“使用條件”或者“應用場合”。一旦完成這個工作,就相當於摸清了想讓系統完成的核心任務。由於將重點放在“使用條件”上,一個很好的效果就是它們總能讓你放精力放在最關鍵的東西上,並防止自己分心於對完成任務關系不大的其他事情上面。只要掌握了一套完整的“使用條件”,就可以對自己的系統作出清晰的描述,並轉移到下一個階段。
用幾個簡單的段落對自己的系統作出描述,然後圍繞它們再進行擴充,添加一些“名詞”和“動詞”。“名詞”自然成為對象,而“動詞”自然成為要整合到對象接口中的“方法”。只要親自試著做一做,就會發現這是多麼有用的一個工具;有些時候,它能幫助你完成絕大多數的工作。
盡管仍處在初級階段,但這時的一些日程安排也可能會非常管用。我們現在對自己要構建的東西應該有了一個較全面的認識,所以可能已經感覺到了它大概會花多長的時間來完成。此時要考慮多方面的因素:如果估計出一個較長的日程,那麼公司也許決定不再繼續下去;或者一名主管已經估算出了這個項目要花多長的時間,並會試著影響你的估計。但無論如何,最好從一開始就草擬出一份“誠實”的時間表,以後再進行一些暫時難以作出的決策。目前有許多技術可幫助我們計算出准確的日程安排(就象那些預測股票市場起落的技術),但通常最好的方法還是依賴自己的經驗和直覺(不要忘記,直覺也要建立在經驗上)。感覺一下大概需要花多長的時間,然後將這個時間加倍,再加上 10%。你的感覺可能是正確的;“也許”能在那個時間裡完成。但“加倍”使那個時間更加充裕,“10%”的時間則用於進行最後的推敲和深化。但同時也要對此向上級主管作出適當的解釋,無論對方有什麼抱怨和修改,只要明確地告訴他們:這樣的一個日程安排,只是我的一個估計!
在這一階段,必須拿出一套設計方案,並解釋其中包含的各類對象在外觀上是什麼樣子,以及相互間是如何溝通的。此時可考慮采用一種特殊的圖表工具:“統一建模語言”(UML)。當然並非一定要使用UML,但它對你會很有幫助,特別是在希望描繪一張詳盡的圖表,讓許多人在一起研究的時候。除UML 外,還可選擇對對象以及它們的接口進行文字化描述。
這個過程中最美妙的事情就是整個小組並不是通過學習一些抽象的例子來進行面向對象的設計,而是通過實踐一個真正的設計來掌握OOP的竅門,而那個設計正是他們當時手上的工作! 作出了對對象以及它們的接口的說明後,就完成了第2 階段的工作。當然,這些工作可能並不完全。有些工作可能要等到進入階段 3才能得知。但這已經足夠了。我們真正需要關心的是最終找出所有的對象。能早些發現當然好,但OOP提供了足夠完美的結構,以後再找出它們也不遲。
在正式編碼前掌握了正確的設計結構,所以會發現接下去的工作比一開始就埋頭寫程序要簡單得多。而這正是我們想達到的目的。讓代碼做到我們想做的事情,這是所有程序項目最終的目標。但切不要急功冒進,否則只有得不償失。根據我的經驗,最後先拿出一套較為全面的方案,使其盡可能設想周全,能滿足盡可能多的要求。
編程更象一門藝術,不能只是作為技術活來看待。所有付出最終都會得到回報。作為真正的程序員,這並非可有可無的一種素質。全面的思考、周密的准備、良好的構造不僅使程序更易構建與調試,也使其更易理解和維護,而那正是一套軟件贏利的必要條件。
構建好系統,並令其運行起來後,必須進行實際檢驗,以前做的那些需求分析和系統規格便可派上用場了。
全面地考察自己的程序,確定提出的所有要求均已滿足。
整個開發周期還沒有結束,現在進入的是傳統意義上稱為“維護”的一個階段。“維護”表示從“保持它按設想的軌道運行”、“加入客戶從前忘了聲明的功能”或者更傳統的“除掉暴露出來的一切臭蟲”等等意思。
大家對“維護”這個詞產生了許多誤解,有的人認為:凡是需要“維護”的東西,必定不是好的,或者是有缺陷的!因為這個詞說明你實際構建的是一個非常“原始”的程序,以後需要頻繁地作出改動、添加新的代碼或者防止它的落後、退化等。因此,我們需要用一個更合理的詞語來稱呼以後需要繼續的工作。
這個詞便是“校訂”。 “第一次做的東西並不完善,所以需為自己留下一個深入學習、認知的空間,再回過頭去作一些改變”。對於要解決的問題,隨著對它的學習和了解愈加深入,可能需要作出大量改動。進行這些工作的一個動力是隨著不斷的改革優化,終於能夠從自己的努力中得到回報,無論這需要經歷一個較短還是較長的時期。
由於多方面的原因,以後對程序的改動是必不可少。但必須確定改動能夠方便和清楚地進行。不僅需要理解自己構建的是什麼,也要理解程序如何不斷地進化。
面向對象的程序設計語言特別適合進行這類連續作出的修改——由對象建立起來的邊界可有效保證結構的整體性,並能防范對無關對象進行的無謂干擾、破壞。也可以對自己的程序作一些看似激烈的大變動,同時不會破壞程序的整體性,不會波及到其他代碼。事實上,對“校訂”的支持是OOP非常重要的一個特點。
通過校訂,可創建出至少接近自己設想的東西。然後從整體上觀察自己的作品,把它與自己的要求比較,看看還短缺什麼。然後就可以從容地回過頭去,對程序中不恰當的部分進行重新設計和重新實現。
在最終得到一套恰當的方案之前,可能需要解決一些不能回避的問題,或者至少解決問題的一個方面。而且一般要多“校訂”幾次才行。
反復的“校訂”同“遞增開發”有關密不可分的關系。遞增開發意味著先從系統的核心入手,將其作為一個框架實現,以後要在這個框架的基礎上逐漸建立起系統剩余的部分。隨後,將准備提供的各種功能(特性)一個接一個地加入其中。
此時應著眼於建立一個簡單、明了的版本,使自己能對系統有個清楚的把握。再把這個原型扔掉,並正式地構建一個。快速造型最麻煩的一種情況就是人們不將原型扔掉,而是直接在它的基礎上建造。如果再加上程序化設計中“結構”的缺乏,就會導致一個混亂的系統,致使維護成本增加。
如果沒有仔細擬定的設計圖,當然不可能建起一所房子。如建立的是一所狗捨,盡管設計圖可以不必那麼詳盡,但仍然需要一些草圖,以做到心中有數。
軟件開發則完全不同,它的“設計圖”(計劃)必須詳盡而完備。在很長的一段時間裡,人們在他們的開發過程中並沒有太多的結構,但那些大型項目很容易就會遭致失敗。通過不斷的摸索,人們掌握了數量眾多的結構和詳細資料。但它們的使用卻使人提心吊膽在意——似乎需要把自己的大多數時間花在編寫文檔上,而沒有多少時間來編程(經常如此)。需要采取一種最適合自己需要(以及習慣)的方法。不管制訂出的計劃有多麼小,但與完全沒有計劃相比,一些形式的計劃會極大改善你的項目。
請記住:根據估計,沒有計劃的50%以上的項目都會失敗!
Java 特別象C++;由此很自然地會得出一個結論:C++似乎會被Java 取代。
無論如何,C++仍有一些特性是Java 沒有的。而且盡管已有大量保證,聲稱Java有一天會達到或超過C++的速度。但這個突破迄今仍未實現(盡管Java 的速度確實在穩步提高,但仍未達到C++的速度)。此外,許多領域都存在為數眾多的 C++愛好者,所以我並不認為那種語言很快就會被另一種語言替代(愛好者的力量是容忽視的。
Java 強大之處反映在與 C++稍有不同的領域。C++是一種絕對不會試圖迎合某個模子的語言。特別是它的形式可以變化多端,以解決不同類型的問題。這主要反映在象Microsoft Visual C++和Borland C++ Builder那樣的工具身上。
它們將庫、組件模型以及代碼生成工具等合成到一起,以開發視窗化的末端用戶應用(用於Microsoft Windows操作系統)。但在另一方面,Windows開發人員最常用的是什麼呢?是微軟的Visual Basic(VB)。從語言設計的角度看,盡管VB是那樣成功和流行,但仍然存在不少的缺點。最好能夠同時擁有 VB 那樣的強大功能和易用性,同時不要產生難於管理的代碼。而這正是Java 最吸引人的地方:作為“下一代的 VB”。人們對Java 做了大量的工作,使它能方便程序員解決應用級問題(如連網和跨平台UI等),所以它在本質上允許人們創建非常大型和靈活的代碼主體。同時,考慮到Java還擁有我迄今為止尚未在其他任何一種語言裡見到的最“健壯”的類型檢查及錯誤控制系統,所以Java 確實能大大提高我們的編程效率。
但對於自己某個特定的項目,真的可以不假思索地將 C++換成Java 嗎?除了Web程序片,還有兩個問題需要考慮。首先,假如要使用大量現有的庫(這樣肯定可以提高不少的效率),或者已經有了一個堅實的C或C++代碼庫,那麼換成 Java 後,反映會阻礙開發進度,而不是加快它的速度。但若想從頭開始構建自己的所有代碼,那麼Java 的簡單易用就能有效地縮短開發時間。
最大的問題是速度。在原始的Java 解釋器中,解釋過的Java 會比C慢上20到50倍。盡管經過長時間的發展,這個速度有一定程度的提高,但和C比起來仍然很懸殊。計算機最注重的就是速度;假如在一台計算機上不能明顯較快地干活,那麼還不如用手做(有人建議在開發期間使用Java,以縮短開發時間。然後用一個工具和支撐庫將代碼轉換成C++,這樣可獲得更快的執行速度)。
為使Java 適用於大多數Web 開發項目,關鍵在於速度上的改善。此時要用到人們稱為“剛好及時”(Just-InTime,或JIT)的編譯器,甚至考慮更低級的代碼編譯器。當然,低級代碼編譯器會使編譯好的程序不能跨平台執行,但同時也帶來了速度上的提升。這個速度甚至接近 C和C++。而且Java 中的程序交叉編譯應當比C 和C++中簡單得多(理論上只需重編譯即可,但實際仍較難實現;其他語言也曾作出類似的保證)。