iThome 網站首載:找出框架的「框」
股市分析有句話大家耳熟能詳:「好的老師帶你上天堂,不好的老師帶你住套房!」將其中「老師」兩字換為「框架(Framework)」時,好的框架雖然不一定能帶你上天堂,但不好的框架絕對能讓你住套房。框架之所以為框架,是因為它有個「框」(Frame),如不能瞭解這個框帶來的是匹配或限制,只是為了使用框架而引入框架,非但無法節省開發時間,反而還會帶來重重限制。
- 框架流程與典範
假設你已經完成了一個應用程式,在日後承接某個案子時,你發現與上個應用程式在許多設計上有許多相近之處,因而將上個專案作為基礎進行修改並完成設計,爾後又承接了一個類似需求之案子,想到日後也許相似需求會持續出現,你將這三個專案中通用元件與流程抽取出來,為無法通用部份制訂規範,留待實際專案中實作,並透過組態設定來將通用元件與非通用實作銜接起來,
這個情境描述了一個框架的形成過程,也間接說明了程式庫(Library)與框架的不同。在開發第一個應用程式,你決定應用程式的流程,並在過程中呼叫某些程式庫提供的功能,在第二、三個應用程式開發時,你是在既有的流程中修改程式以符合專案需求,在之後使用框架開發的應用程式中,你依照框架的規範實作功能元件,框架會在它既定流程中呼叫你的實作元件。程式庫與框架之別就在於「你呼叫程式庫,而框架呼叫你」。
正因為框架會在既定流程呼叫你的實作元件,框架本身規範的流程就是一個框。框架本身是由各式程式庫組成,若將框架視為一門語言,框架流程就好比語言典範(Paradigm),而框架的程式庫集合就好比語言的語法元素集合。採用一個框架與如同決定採用一門語言,應該思考的重點之一是框架流程與典範形成的框,到底是匹配或限制了應用程式的設計?如果設計上是匹配的,那麼這個框對參與的開發者就是一致的規範,就如同設計模式給予開發者共同的語彙,減少溝通問題並降低錯誤發生的可能性;如果框架的框與設計上不匹配,那開發者就會覺得被束縛,甚至時時想跳脫框架另蹊徑,在各自認為適當的地方修補功能,造成許多框架或後續維護者所能控制外的流程。
即便是支援相同架構,也可能會有不同流程實作的各式框架,甚至同一框架的不同版本,流程上也會呈現差異。舉例來說,Struts在1.x與2.x之間,呈現極大的流程差異,雖然根本上兩者是不同的框架,然而驗證了同為支援MVC/Model 2架構的框架,流程也會有實作上的不同;如果使用的是快速開發框架,像是Play、Rails或Django,這類框架大多透過裁剪他們認為不適合的設計來降低開發的複雜度,你得全面暸解,並確定你願意遵守它全套作法,那麼就會用得很高興,不然就會很不爽!
- 組態、慣例與文件
在我先前專欄《框架的本質與附屬》中談過「在通用化的過程中...必須能在套用框架時,根據差異性個別進行組態,而使用框架時也必須遵守所提供程式庫的組裝規則」,組態設定方式也是框架的框。就可設定的項目來說,組態選項過少的框架容易面臨彈性不足,過於強調組態彈性的框架,則易於面對組態上的繁瑣,光是瞭解各種設定就能令開發者疲於奔命,令框架在使用上未蒙其利先受其害,過於複雜的組態,往往也代表著框架本身就過於複雜。
近來較新或持續活躍的框架,都提供或加入了慣例優於組態(Convention over configuration)的特性,也就是在不指定某些組態值時,按照多數開發者慣例或最佳實踐(Best practice)提供預設值,因而可簡化框架組態設定又不失之彈性;然而慣例也是個框,只有在多數慣例都符合開發者預期時,才能從中得到好處,否則,光是要瞭解框架提供的慣例為何,也會是個痛苦的過程。慣例只能減少開發者選項設定的數量,並不能減少開發者做決定的數量。
無論組態時使用的媒介是XML、JSON、Annotation或以語言實現的DSL(Domain-specific language),組態時就是得照框架規範的方式設定,因而評估框架時,確定其是否擁有豐富且正確的組態說明文件,是絕對必要的,否則組態本身就可能是充滿最多謎題的地方。正如我在《框架的本質與附屬》中談過,組態設定是框架使用時的附屬性問題,不應模糊應用框架的焦點,是否有輔助工具也會是評估的重點,好的工具有時可減輕文件查詢時的負擔。
- 以原型測度框架抽象程度
框架拆開來看,是由許多程式庫組成,並加上流程與特定領域的語義,程式庫本身封裝、抽象化許多實作細節,由程式庫組成的框架就是高度抽象化後的產品。高度抽象化之目的,是希望減少細節的重複實作,促進應用程式開發產能,然而有時就是需要碰觸某些細節,如果框架的抽象層次與想碰觸的細節差異過大,開發者就會時時想繞過框架自行實作,那這也是個有不必要框框的框架了。
使用一個沒有使用任何框架,但有著類似架構與流程,以底層API自行開發的原型程式,試著將打算採用的框架套用上去,是個測度框架抽象程度是否符合需求的方式,也有助於觀察框架是否帶來了功能實作時的簡化。例如,使用Servlet/JSP開發一個MVC/Model 2架構的原型程式,然後試著用Spring MVC、Struts 2等支援MVC/Model 2架構的框架來重構它,就能測試出框架的抽象程度是否符合預期。也許原本是以Filter實作請求攔截,現在改用Struts 2的攔截器(Interceptor)看看是否勝任,在重構時,也許發現經常需要的是HttpSession,而不是Struts 2抽取HttpSession屬性後的Map物件,類似這類的過程,就能感受到哪些細節被封裝,並能評估框架的替代方案是否能解決需求。
- 專案需求與開發者能力
在評估框架是增加產能或造成限制時,找出框架的框是確實必要的,框的另一面就是專案需求,開發者必須兩邊都能辨識,才能知道兩者是否匹配,而這也表示了,參與專案的開發者能力,也是不在框架中的框。若將框架流程與典範、組態、慣例與文件、抽象程度的測度等綜合起來,就可瞭解不同框架都會有各自不同的複雜度與學習曲線,沒有能力跨越這複雜度與學習曲線的開發者,運用框架也會是一場災難。
開發者能力不見得是已熟悉將採用的框架,而是確實學會該框架的基礎能力,這邊使用了「確實」兩字,是因為框架提供的抽象程度,有可能令框架使用者在不知原理的情況下,依樣畫葫蘆兜出一個應用程式,然而在沒有範例可比照,或框架沒有直接提供功能下,就雙手一攤什麼事就做不了。確實學會框架的基礎能力,通常指是否理解框架底層細節的能力,能否以底層技術實作出類似架構的原型程式可以是個指標,因為這類開發者較能辨別框架是否符合或限制了設計,也有能力在框架規範下進行擴充。
然而許多時候開發者能力在採用框架是被忽略的考量,甚至經常是基於商業考量下選擇了某些框架,也許是採購案中得搭配使用的框架,或是得標廠商慣用的框架,基於商業考量下忽略開發者能力而採用的框架,在後續往往造成維護上極大的負擔,最極端情況下,案子完成後接手維護的開發者,對框架定位、架構、流程、底層完全沒有概念,在接手維護專案前,驟然面對陡峭學習曲線而不知如何適從,最後造成負產能或傷害後續維護性的工作。