框架的本質與附屬


iThome 網站首載:框架的本質與附屬

框架(Framework)的出發點,在於解決軟體開發過程中重複出現的流程設計,每個框架實作背後必然支持某個架構與流程,這是框架要解決的本質性(Essence)問題,然而框架作為一種工具,本身必然會帶入某些附屬性(Accidents)問題,使用框架必先辨識框架要解決的流程本質,並對即將被導入框架的系統有通盤瞭解,方可評估採用後的價值是否超越隨著框架附屬而來的繁複設定。

框架解決問題間的概念交集

《人月神話》作者Frederick P. Brooks在1986年發表了論文《沒有銀彈:軟體工程的本質性與附屬性》,原文中Accidents並非現代用語中意外或突發之意,而是古希臘哲學家亞里斯多德的用語,在Brooks近作《設計的設計》中針對本質與附屬加以解釋:「若採用現代用語,必要的(essential)和次要的(incidental)應較容易理解」。

Brooks書中亦談到:「軟體建構中本質的部份,指的是概念的智能創作,而附屬的部份,指的是實作程序。」在開發軟體的過程中,有時會發現解決軟體中某些問題時,創造出的方法概念具有某種程度的相似性或交集,若對類似概念重複進行實作,重複耗費的附屬性成本著實是種浪費,因而在實作類似概念時考量了彈性與通用性,使得後續軟體開發中出現類似的智能創作時,可以直接套用先前既有成品,避免一再重複的實作程序,而這個既有的成品就是框架。

倘若將軟體開發中概念的智能創作以圓形表示,若重疊在交集部份的圓越多,抽取該概念的實作程序並通用化為框架的價值就越高,而交集部份的概念智能創作,就是框架中本質的部份;在通用化的過程中,必須考慮交集部份的概念間依舊存在的差異性,因此必須能在套用框架時,根據差異性個別進行組態,而使用框架時也必須遵守所提供程式庫的組裝規則,這類框架使用時必須依循的組態方式與程式庫規範,就是隨著框架而來的附屬性問題。

建立微型框架理解框架本質

對於實際軟體開發中出現的重複流程,嘗試將其一般化並建立微型框架,有益於瞭解軟體本身,亦有助於選擇適用的成熟框架。

舉例而言,若開發軟體過程中有測試的需求,而你必須撰寫測試執行器(Test runner),負責執行測試工程師撰寫的測試案例(Test case)。為了能讓測試工程師獨立作業,你嘗試使用命令(Command)模式,定義並要求測試工程師實作測試介面;為了能讓測試工程師自由組合測試案例,你嘗試使用組合(Composite)模式重新改寫測試執行器,定義並要求測試工程師使用測試套件(Suite);為了能夠收集與呈現技術無關的測試結果,以便之後能以各種方式顯示測試結果,你採用了收集參數(Collecting Parameter)模式;當你進入下一個軟體開發,同樣必須執行測試時,你想到前後兩個軟體的測試概念是類似的,於是將先前為測試而撰寫的實作進行通用化,帶入下個軟體開發之中,這通用化的實作部份,就是測試框架的微型版本。

或許隨著一個又一個的軟體開發,你的測試框架越來越通用,因而想到對外公開釋出,以讓有類似測試需求的軟體可以直接套用;又或許是在自行開發微型測試框架的時候,發現到JUnit測試框架的概念流程與你的微型框架類似但功能更成熟,因而決定直接將JUnit導入軟體開發之中,由於先前自行開發微型框架的經驗,讓你在使用成熟測試框架時能立即掌握核心流程。

類似地,在開發的軟體中有產生報表的需求,格式可能是HTML、PDF或JSON。為了隱藏報表產生器的實作細節,你可能採用工廠(Factory)模式,在傳回報表服務物件的方法中,進行報表產生器、報表服務物件的實例化,以及建立兩者之間的依賴關係,為了更有彈性地設定採用何種報表產生器,於是採用XML設定檔。當你進入下一個軟體開發,發現同樣有物件生成與建立依賴關係的需求,於是將先前撰寫的工廠類別予以通用化,帶入下一個軟體之中;隨著一個又一個的軟體開發,你的微型框架不再只是針對特定物件生成或特定依賴關係建立。有天你發現到,Spring的依賴注入(Dependency Injection)容器(Container)更為成熟,而且整個Spring框架以其為核心整合諸多主流開放原始碼框架,這對你是個極大便利,先前花費時間自行建立微型框架的過程也不是個浪費,因為你的想法概念與Spring框架契合,導入Spring框架會讓你更得心應手。

避免框架附屬困難模糊應用焦點

同樣的方法概念,有可能會有不同的實作,在抽取交集部份實作程序並予以通用時也會有不同成品,也因此同樣的方法概念,可能會有不同的框架實作可供選擇,當同樣方法概念有過多的框架實作可以選擇時,各種框架附屬而來的困難度就會讓開發者疲於奔命,而對想進入該領域的開發者,驟然面對的就是諸多框架加乘在一起而產生的徒峭學習曲線,此時最重要的就是具備辨識框架本質的能力,並避免將重點放在附屬而來的困難。

舉例來說,實現MVC/Model 2概念的框架繁多,有些會將Model 2的概念予以細化,削減不必要的通用性,使其適用於特定領域,通用性被削減,代表了設定組態與相關程式庫的簡化,也就是降低了使用該框架時的附屬性困難,然而此類框架的本質是方才提到的「適用特定領域」,開發者理解或使用此類框架,重點應放在該框架所謂特定領域為何,而不是放在組態與相關程式庫簡化後應如何設定。另一個例子是,近來許多Model 2框架在呈現(View)上結合Ajax元件,使用此類框架除了瞭解其如何支援Model 2流程之外,更要瞭解提供的Ajax元件與Model 2流程的互動模式為何,才有助於同時解決前端的呈現問題。

有些框架一開始會有組態不便或設定複雜的問題,然而這些附屬的困難度可隨著輔助工具的推出、框架的改版、新的組態媒介而降低甚至消失。過於將學習或使用重點放在框架的附屬性困難上,會對理解框架本質造成阻礙,只是學會那些將來可能不再適用的API組裝並不值得炫耀,也不應自認為懂得如何完成複雜組態設定而洋洋自得,畢竟那並非框架存在的真正意義。徵才需求上列出求職者必須熟悉某些框架,並非僅要求應徵者能在系統中設定與組裝框架,而是在尋求可不被框架附屬性困難絆住,瞭解框架本質並可應用在系統中真正解決問題的人才。

將通用框架特化為領域框架

除了掌握要採用框架的核心流程之外,對即將被導入框架的原系統也要有通盤瞭解,如此才能瞭解框架核心與被導入框架的系統在流程上是否符合,也才能挑選合適的框架元件,而不是試圖採用所有的功能。通常一個框架越通用,或者是隨著版本更進而功能越趨龐多時,就會有許多系統中根本用不上的附屬功能,試圖學習框架全部的功能,往往是不可能也是不必要的舉動。

也唯有在同時瞭解框架本質以及被導入框架的系統流程,在面對框架核心流程與系統流程略有不符時,才有能力對框架作出擴充調整,像是整合其他框架或程式庫,或者在必要時將框架某些API予以封裝,成為適用該領域的API,甚至在數個類似的框架調整過程之後,從原有的通用框架沿生出適用特定領域的框架。