摸清架構,便可輕鬆掌握全貌
在本文中,我們的重點放在:要了解一個系統,最好是採取由上至下的方式。先試著捕捉系統架構性的觀念,不要過早鑽進細節,因為那通常對於你瞭解全貌,沒有多大的幫助。閱讀程式碼不需要從第一行讀起,我們的目的並不是在於讀遍每一段程式碼。
基於許多原因,程式人需要閱讀其他人所寫成的程式碼。而對程式設計2.0時代的程式人來說,最正面的價值在於,能讀懂別人程式的人,才有能力從中萃取自己所需的程式,藉以提高生產力。
閱讀程式碼的目的,在於瞭解全貌而非細節
想要讀懂別人程式碼的根本基礎,便是瞭解對方所用的程式語言及命名慣例。有了這個基礎之後,才算是具備了基本的閱讀能力。正如我之前提到的──想要讀懂法文寫成的小說,總不能連法文都不懂吧。閱讀程式碼和閱讀文學作品,都需要了解撰寫所用的語言及作者習用的語彙。
但我們在閱讀文學作品通常是採循序的方式,也就是從第一頁開始,一行一行地讀下去,依循作者為你鋪陳的步調,逐漸進到他為你準備好的世界裡。
閱讀程式碼卻大大不同。我們很少從第一行開始讀起,因為除非它是很簡單的單執行緒程式,否則很少這麼做。因為要是這麼做,就很難瞭解整個系統的全貌。
是的,我們這邊提到了一個重點,閱讀程式碼的目的在於瞭解系統的全貌,而不是在於只是為了地毯式的讀遍每一段程式碼。
就拿物件導向程式語言所寫成的系統來說,整個系統被拆解、分析成為一個個獨立的類別。閱讀個別類別的程式碼,或許可以明白每項類別物件個別的行為。但對於各類別物件之間如何交互影響、如何協同工作,又很容易陷入盲人摸象的困境。這是因為各類別的程式碼,只描述個別物件的行為,而片段的閱讀就只能造就片面的認識。
由上而下釐清架構後,便可輕易理解組成關係
如果你想要跳脫困境,不想浪費大量時間閱讀程式碼,卻始終只能捕捉到對系統片段認識,就必須轉換到另一種觀點來看待系統。從個別的類別行為著手,是由下至上(Bottom-Up)的方法;在閱讀程式碼時,卻應該先採由上至下(Top-Down)的方式。對程式碼的閱讀來說,由上至下意謂著,你得先了解整個系統架構。
系統的架構是整個系統的骨幹、支柱。它表現出系統最突出的特徵。知道系統架構究竟屬於那一種類型,通常大大有益於瞭解系統的個別組成之間的靜態及動態關係。
有些系統因為所用的技術或框架的關係,決定了最上層的架構。例如,採用Java Servlet/JSP技術的應用系統,最外層的架構便是以J2EE(或起碼J2EE中的Web Container)為根本。
使用Java Servlet/JSP技術時,決定了某些組成之間的關係。例如,Web Container依據web.xml的內容載入所有的Servlets、Listeners、以及Filters。每當Context發生事件(例如初始化)時,它便會通知Listener類別。每當它收到來自客戶端的請求時,便會依循設定的所有Filter Chain,讓每個Filter都有機會檢查並處理此一請求,最後再將請求導至用來處理該請求的Servlet。
當我們明白某個系統採用這樣的架構時,便可以很容易地知道各個組成之間的關係。即使我們還不知道究竟有多少Servlets,但我們會知道,每當收到一個請求時,總是會有個相對應的Servlet來處理它。當想要關注某個請求如何處理時,我應該去找出這個請求對應的Servlet。
瞭解架構,必須要加上層次感
同樣的,以Java寫成的Web應用程式中,也許會應用諸如Struts之類的MVC框架,以及像Hibernate這樣的資料存取框架。它們都可以視為最主要的架構下的較次級架構。而各個應用系統,甚至有可能在Struts及Hibernate之下,建立自有的更次級的架構。
也就是說,當我們談到「架構」這樣的觀念時,必須要有層次感。而不論是那一層級的架構,都會定義出各自的角色,以及角色間的關係。對閱讀者來說,相較於直接切入最細微的單一角色行為,不如瞭解某個特定的架構中,究竟存在多少角色,以及這些角色之間的互動模式,比較能夠幫助我們瞭解整個系統的運作方式。
這是一個很重要的關鍵,當你試著進到最細節處之前,應該先試著找出參與的角色,及他們之間的關係。例如,對事件驅動式的架構而言,有3個很重要的角色。一個是事件處理的分派器(Event Dispatcher)、一個是事件產生者(Event Generator)、另一個則是事件處理器(Event Handler)。
事件產生器產生事件,並送至事件分派器,而事件分派器負責找出各事件相對應的事件處理器,並且轉交該事件,並命令事件處理器加以處理。像Windows的GUI應用程式,便是採用事件驅動式的架構。
當你知道此類的應用程式皆為事件驅動式的架構時,你便可以進一步得知,在這樣的架構下會有3種主要的角色。雖然也許還不清楚整個系統中,究竟會需要處理多少事件的類型,但對你而言,已經建立了對系統全貌最概觀的認識。
雖然你還不清楚所有的細節,但諸如確切會有那些事件類型之類的資訊,在此刻還不重要──不要忘了,我們採取的是由上而下的方式,要先摸清楚主建築結構,至於壁紙的花色怎麼處理,那是到了尾聲時才會做的事。
探索架構的第一件事:找出系統如何初始化
有經驗的程式人,對於時常被運用的架構都很熟悉。常常只需要瞧上幾眼,就能明白一個系統所用的架構,自然就能夠直接聯想到其中會存在的角色,以及角色間的關係。
然而,並不是每個系統所用的架構,都是大眾所熟悉,或是一眼能夠望穿的。這時候,你需要探索。目標同樣要放在界定其中的角色、以及角色間的靜態、動態關係。
不論某個系統所採用的架構是否為大部分人所熟知的,在試著探索一個系統的長相時,我們應該找出來幾個答案,瞭解在它所用的架構下,下列這件事是如何被完成的:一、系統如何初始化,二、與這個系統相接的其他系統(或使用者)有那些,而相接的介面又是什麼;三、系統如何反應各種事件,四、系統如何處理各種異常及錯誤。
系統如何初始化是很重要的一件事,因為初始化是為了接下來的所有事物而做的準備。從初始化的方式、內容,能知道系統做了什麼準備,對於系統會有什麼行為展現,也就能得窺一二了。
之所以要了解與系統相接的其他系統(或使用者),為的是要界定出系統的邊界。其他的系統可能會提供輸入給我們所探索的系統,也可能接收來自這系統的輸出,瞭解這邊界所在,才能確定系統的外觀。
而系統所反應的事件類型、以及如何反應,基本上就代表著系統本身的主要行為模式。最後,我們必須瞭解系統處理異常及錯誤的方式,這同樣也是系統的重要行為,但容易被忽略。
之前,我們提到必須先具備一個系統的語言基礎,才能夠進一步加以閱讀,而在本文中,我們的重點放在:要了解一個系統,最好是採取由上至下的方式。先試著捕捉系統架構性的觀念,不要過早鑽進細節,因為那通常對於你瞭解全貌,沒有多大的幫助。