程式者的胡言亂語
程式員的黑盒子 - 1
自從物件導向的設計方法流行以來,許多物件導向設計中的重要觀念,一直被程式員們奉為圭臬,而資訊隱藏(information hiding)以及封裝(encapsulation)的想法,更是深深的影響到程式員們進行設計時的態度。
封裝的目的是希望透過介面,將物件實作的細節隱藏起來,透過控制能見度(visibility)來降低物件與使用物件之程式碼的耦合程度。元件式的軟體開發(component-based software development)更進一步發展封裝的觀念,在元件式的軟體開發下,希望將軟體單元都封裝成為所謂的元件,每個元件皆有其規格,而元件的使用者則毋需了解元件內部究竟如何實作,只需依據元件公開的規格介面來控制、使用元件。此種基於元件的開發方式,其優勢便在於將開發中可重覆使用的軟體單元,封裝成可被重覆使用的元件,之後便可透過「組裝」現成元件的方式,來更快速的完成應用程式的開發。在此種模式的理想情況下,會有專門的角色開發個別的元件,而應用程式的開發者,只需從型錄中挑選自己所需,接著加以組裝,便能輕易的打造出應用程式。這樣的模式,倘若真能落實,不僅可以透過重覆使用節省無謂的重覆性開發,也可以大幅的提昇開發的效率,更有利用日後的維護。有人用「軟體IC」一詞來說明元件式開發的元件,準確的描述人們對此種開發方式的期待。許多有名的技術,諸如OMG的CORBA、Sun Microsystem的EJB、以及微軟的COM,都是奠基於元件式開發的技術。
元件式的開發模式有一個重點,便是要規格化、介面化元件。每個元件都有其所遵循的規格,元件的使用者只會依據這份做為介面的規格來撰寫與之相溝通的程式碼,而不會碰觸到元件確切的實作方式。
例如,當你使用微軟的DirectShow架構來開發多媒體應用程式時,你會留意到,DirectShow的架構正是一種基於元件的架構。在DirectShow的架構中,當你要撥放一段影片或音樂時,你得先建立一個IFilterGraph的實作,裡頭是許許多多的IFilter實作物件相互以IPin連接,而多媒體的資料就從IFilterGraph中最前端的IFilter(也稱為source filter)開始沿著IFilterGraph中的IFilter流動。每個IFilter的實作本身都有其獨特的作用,例如它可能是一個MP3的解碼器,從輸入的IPin收到MP3的音訊內容後加以解碼,並且利用其輸出的IPin傳送至下游的IFilter實作去。在DirectShow的命名慣例下,不論是IFilterGraph、IFilter、IPin等名稱,首字的 “I” 皆意謂著它們都是代表著介面(”I”nterface),對或開發DirectShow應用程式的程式員而言,他們概念上並不理會每個IFilter實際上究竟是如何實作的,只要該IFilter的實作其輸入之IPin及輸出的IPin能夠滿足他們的需求即可。所以,應用程式開發者和元件開發者(開發IFilter及IPin實作品的人),各司其職。採取這樣子的架構,其優點之一是,或許你現在正採用某一家廠商所開發的MPEG 4解碼器,只要這個解碼器符合IFilter的介面,那麼你便可以將其輕易的更換成另一家廠商所開發的MPEG 4解碼器,而不會對原有的應用程式產生任何的衝擊和影響。對你的DirectShow應用程式來說,只不過是將若干個個別作用不同的元件(IFilter)組裝在一塊並使之依據你所期待的方式運行罷了。DirectShow的架構,充份的發揮元件式開發的優點,也彰顯了其特色所在。
你會注意到,元件式的開發,為了達到具彈性、輕易的組裝方式,更為強調封裝的重要性。在現今我們所從事的開發中,就算沒有完全依據元件式的開發方式,也深受元件式開發的觀念所影響,極力的朝向更進一步的封裝而努力。
我們可以觀察到現今的應用軟體開發愈來愈有生產力,人們所建立的系統愈來愈龐大,而建造速度也愈來愈快。我們能夠克服軟體開發時的驚人複雜度,其中將軟體單元做某種程度的元件化,其影響不可不謂之大。如今,我們想開發各種應用軟體,充斥著各種現有的元件,舉凡視訊、音訊的編解碼、3D的繪圖、網路通訊、資料庫處理、各類數學運算、都因為元件化的特性,使得應用程式的開發者得以輕易的將它們整合到自己的應用程式中,在很短的時間內完成應用程式的開發。
另一方面,想要設計出元件化的軟體,程式員得需要具備一定的設計能力。程式員得學習如何進行適當的抽象化,他必須決定元件中有那些是不欲外界所知悉的實作細節,並且將它們隱藏起來,也必須決定對外公開的介面應當容納那些元素,以提供客戶端程式員之所需。而若想要使用軟體元件,客戶端程式員必須克服自己探究每一個細節的好奇心,他必須將自己檢視軟體的焦點擺在適當的抽象層次上,也就是說,他必須讓自己學會以介面的角度來看待每個軟體元件,讓自己站在介面層次上來運用每個元件。這也正是我們反覆不斷提起的「針對介面來撰寫程式,而不要針對實作」的設計原則。
我們時常會說,「你可以把XXX模組或YYY子系統當做是一個黑盒子」,便是利用「黑盒子(black box)」一詞指涉那些被我們封裝起實作細節的軟體元件,十分傳神的表達出封裝的概念。
一名程式員要學會將他人或自己所設計軟體元件視為黑盒子需要一些訓練。有許多初學的程式員總是試著要在運用每個元件前搞清楚它的細節,或者是在設計自己的元件時曝露太多的細節,這自然違反了封裝的原始精神及用意,而且對於應用程式的開發也沒有多大的用處。
軟體的黑盒子,基本上便是設計者的抽象化產物,抽象化的程度及方式決定了黑盒子所包裝的範圍及空間大小。愈是長於設計的程式員,愈能設計出合宜的黑盒子供自己及他人使用。
而客戶端程式員在將軟體元件視為黑盒子時,也是同樣的以一種抽象的眼光來看待自己所面對的元件。這意謂著客戶端程式員是以一種更高階、更一般化的方式來思考自己應如何撰寫程式碼,而自己所寫下的程式碼也將具備更多抽象、更少具象實作的特質,使得這些程式碼更為一般化,也能搭配更多可能的實作而毋需更動。
如此將軟體元件視為黑盒子的設計方式,可以說是許多主流的設計觀念,例如重覆使用、多型、封裝、介面化所結合在一起的產物。當你愈是能自在的以這些觀念來從事設計時,你會發現你所做的設計,以及你看待這些設計的方式,已經全然的黑盒子化了。而透過這樣的設計方式,你可以輕易的重覆使用別人所設計的元件,並且直接獲得生產力的補充。在建立自有元件時,你可以藉此阻隔不必要的實作細節,撰寫出更一般化的程式碼,更具因應變動的能力,同時更容易擴充。當程式員放棄探究隱藏在黑盒子中事物的好奇心時,上述的這些好處都是程式員所收到的回報。
Blogged with Flock
Posted at 04:14下午 九月 07, 2008 by Chien-Hsing Wang in General | 迴響[2]
談程式語言的選用
程式員間倘若談到了程式語言之間的比較話題,無論比較的是那個程式語言較好、或那個程式語言最重要、或者應該學什麼程式語言最有幫助,總是能引起無止盡的論戰而且難以止息。這樣的話題就跟日本車、歐洲車孰優孰劣一樣,每個程式語言總有其基本支持群眾,爭論時往往各執一詞,使得這樣的討論主題很難歸納出什麼結論。
每個程式語言背後皆有其設計的中心思想及哲學,而每個程式語言也都隱含著有其設定的一套表述方式。每個程式員都有自己偏好的程式語言,而這偏好之所以會建立,和學習程式語言的經驗、過程,以及個人特質有關。例如,有些程式員就是偏好直譯式語言,但是也有許多程式員對直譯式語言望之卻步。就和很多的個人愛好一樣,有時很難解釋為什麼你就是偏好某一個程式語言。
在早期雖也有許多程式語言的發明,但實際上用於一般軟體開發的實戰語言選擇並不多。而到了現今,各種程式語言百家爭鳴,一般程式員可以接觸到的程式語言數量遠勝以往,對於究竟該學習或該採用那一個程式語言,其中可以抉擇的空間又大了許多。
以我的觀點來看,程式語言間的比較,其實是不具太多的實質意義。倘若程式語言對你來說是一種純粹的嗜好,並不存在太多開發上的實務考量,那麼僅以欣賞或喜好的心情來看待一個程式語言,甚至做為其擁護者,那麼是否選用一個程式語言,就是一個完全由個人愛好來決定的事情。但是,倘若你是在開發的過程中要選擇一個程式語言,那麼需要考量的事情相較之下就會複雜許多。
事實上,每個語言都有它特有的性質,發明者在設計每個程式語言時多半也都設定了一定的目標範圍,以及想要解決的軟體開發問題。隨著程式員的實際應用以及語言本身的逐漸演化,每個語言都會被發掘出適合的應用領域。
沒有一個程式語言是打遍天下無敵手的,每個語言都有各自的優點和缺點。優點的另一面往往就是缺點所在,例如Java的垃圾收集機制,簡化了程式員配置、釋放記憶體的模式,大幅的降低了程式員因為犯下錯誤存取記憶體的問題而耗去的大量除錯時間。但是,也因為垃圾收集機制的關係,使得Java虛擬機器設計更形複雜,也影響到Java虛擬機器執行時期的效率。你所開發的應用程式,或許可以接受現今Java虛擬機器的運行效能(事實上,Java虛擬機器的運行效能應當足以開發大多數我們會接觸到的應用程式),而選擇使用Java來避免無形中的錯誤,藉以提昇開發時的生產力。但如果你所開發的程式極需執行效能,例如多媒體視訊或音訊的解碼器,便可能採用像C或C++之類的程式語言,甚至使用組合語言來撰寫,在這種應用情境下,開發的生產力不是關鍵,因為執行效能已經成了必要的門檻。所以,選擇開發時採用的程式語言,其實只是取與捨的判斷,優劣好壞不過都只是相對的特性,而沒有絕對的論斷方式。
程式語言也有流行的情況,你或許觀察到新問世的程式語言有時能引起一陣風潮,讓程式員們爭相追逐。但不可諱言的是,即使一個程式語言能引起短暫的流行,但每個程式語言都需要經過殘酷的考驗才得以生存下來。而歷時多年尚能存活至今的程式語言,無一不具備其賴以生存的重要特性。與其爭辯程式語言的好與壞,不如放開成見了解每個程式語言的特性,適合做什麼,以及不適合做什麼,才能客觀的於其中進行選擇。
優秀的程式員應當是程式語言中立的(programming language neutral),也就是說,你或許會專長、熟悉於若干個程式語言,但因為開發上的需要,你可以很容易學會、適應其他的程式語言。也就是說,當你從事開發時,並不會被特定的程式語言所限制住。最起碼,你必須熟悉多個程式語言並且能夠自在的運用。主流的程式語言思維模式不出幾類,例如程序式、物件導向式、函數式、或是混合模式等等,同一類的語言間即使語言語法或特性有所差異,但畢竟是源自於相同的血統,觸類旁通並不是件難事。例如C++、Java以及C#,皆為一脈相承,尤其是Java和C#,二者相互模仿,熟悉Java的程式員,必能在極短時間熟悉C#,甚至應該不經太多的訓練,便能輕易的讀懂C#的程式碼。能以多種程式語言編寫,已然成了優秀程式員的判斷標準之一。
當你具備了以多種語言編寫程式的能力,就能夠(也應該)依照需求而非喜好來選擇程式語言,你也不會固定只採用一種程式語言來撰寫所有的程式。這已經是一個多語言開發的時代了。當你開發某個服務和產品時,時常會需要動用到多個程式語言。或許你會利用Java來開發伺服器端,利用C#開發客戶端的GUI部份,但利用C++開發客戶端的核心程式庫。選擇採用一個程式語言,是因為它最適合應用在你要應用的地方,而不是因為你最喜愛它。
那麼應當如何選擇開發所用的程式語言呢?通常這是一整個開發團隊針對要開發的專案而做的決策,因此必須考慮到所有團隊成員的技能特性。程式語言的選擇不單是個人化的,更是團隊化的。主要都會依據專案本身的特性及目標來決定欲使用的程式語言。你首先可能會考慮到客戶選用的平台,或者產品所設定的平台,有些程式語言基本上只在某些平台上可用,例如.NET上的程式語言,實務上只能在Windows的作業系統上執行。
除了平台之外,你會考慮到執行效能,有些系統元件必須滿足執行時間或者服務規模的限制,對於這些元件必須選用效能表現足堪滿足的程式語言。開發的生產力也是很重要的考量因素。專案都有其執行的時間期限,面對時程緊迫的網站應用程式,你或許會考慮以Ruby程式語言搭配RoR應用程式框架的方式來做為程式語言的主軸,善用RoR的高生產力。在提供.NET的Windows平台上,選擇C#之類的程式語言來開發GUI,生產力多半能勝過VC++搭配MFC的方式。此外,開發程式的穩定度、支援的程式庫豐富與否、或者是否提供了你所需的程式庫,都是選用時必須考慮的條件。
在同一系統中混用一種以上程式語言的情況已經愈來愈常見,這使得我們更能輕易的善用每個語言的優點,規避掉持定語言的缺點。採用多語言開發時,還必須考慮所選用的程式語言是否能夠相互整合,整合的方式、介面是否滿足需求及限制條件。例如使用Java做為主要的開發語言,又想使用C語言撰寫需要高效能的模組時,就必須考慮使用JNI(Java Native Interface)做為二者界接的方式,但使用JNI又會失去平台的可攜性,決策者就必須於其中取捨。
當兩個備選的程式語言條件勢均力敵或是相差不遠時,應該要選擇自己所熟悉的。對程式語言的熟悉度能夠讓你避免許多因為陌生而造成的不必要陷阱,也可以避免掉許多無謂的時間浪費。
許多程式員會擔心某個語言是否會退流行,或者是擔心沒有趕上最新流行的程式語言會讓自己淘汰。我的看法是,與其擔心沒有跟上最新的流行,不如想想如何掌握不同程式語言的共通之處,也就是本文中所提到的,主流的程式語言思維模式其實不脫幾類,而且甚至以物件導向與程序式混合的程式語言為主。熟悉這種表達方式,要操作不同的程式語言,其實只是語法以及細節的獨特語言特性不同,跨多語言其實不構成太大的問題。關鍵往往不會是在語法,而是在你怎麼做抽象層次上的設計。
與其爭辯程式語言孰優孰劣,不如更自在、更恰到好處的運用多種程式語言於你的開發之中。優秀的程式員應當是程式語言中立、能同時以多種程式語言從事編寫的。
Blogged with Flock
Posted at 04:08下午 九月 07, 2008 by Chien-Hsing Wang in General | 迴響[0]
星期日 九月 07, 2008
