Context 物件
記得是在學習 Andoird 程式的時候,第一次接解觸到「Context」這的單字,在一些書本不是翻作「上下文」或是省略不提。花了許多時間嘗試理解,最後終於在字典外的一個討論串手有了一些些的了解。如果要我翻譯它的話,我會翻作「關連」。在 MVC 框架中,會有一個作為組合所有元素的「容器」,在命名時常會使用「Context」這個單字,它的作用就是讓本來沒有交集的元素,相互之間能夠互動,讓值物件能夠被注入到的需要的物件中,讓發出的事件能夠被偵聽的物件收到。維繫這些元素之間互動關係,就是「Context」的責任,所以我認為翻成「關連」會比較好。
在 MVC 的概念中,每個元素都是獨立的,不知道會知道其它元素存在。因此可以更彈性地抽換、組合來滿足需求。而在這些元素之外,需要有個「容器」來裝它們,由於容器的程式實作通常已經被框架的開發者作完了,通常只需要繼承並覆寫需要改變方法,或是 new 出物件後,指定需要包含的類別有哪些,它們之間的關連就會被建立好。
在 Java 裡的 spring 框架,甚至只需要在設定檔指定套件的範圍,就能夠直接作「掃描檔案,判斷類別是否為 spring 元件」的工作,增加新元素變得更容易,也不容易漏了什麼關連。雖然這並不是 spring 框架的特有的能力,不過是我在其它程式語言的 MVC 框架中沒看到的特色。
依懶注入,事件偵聽
MVC 框架中的資料交換,通常包含兩種:資料和事件。容器的工作,就是讓資料能夠分享到所有需要它的物件中,並且當事件發出時,通知所有對這個事件有興趣的物件。達到撰寫程式的時候,只需要思考如何使用資料,以及在事件發生時要作哪些工作,不用多考慮資料與事件的來源為何,這些事情,框架所構成的「Context」會處理。不同的程式語言,MVC 框架在實作的方式也有所不同,在這裡就不作細談。原理上相同的地方在於,當需要資料的時候,容器會幫你確保值物件已經產生,並且放到對應的屬性中,這就是依賴注入。事件的作法有的語言也是用和值物件的方式,將需要通知的物件注注入,也有其它語言利用既有的事件(Event)機制;無論是哪一種,都是確保當事件發生時,能夠通知人,也能夠收到有興趣的通知。
在實作上,有個原則叫作「對介面撰碼,而非實作」,意思是說定義了取得資料或被人呼叫的方法在介面中,讓依賴以這個介面為條件注入。這樣子就能夠彈性的更變成其它同樣實作這個介面的類別,達到更高的彈性。另一個角度來說,在單元測試時,也可以更輕易地實作出假邏輯、假方法,省下建立一樣工作環境、資料需要的時間。
容器知道元素,但是元素不該知道容器的存在
容器知道元素,這是很自然的事情。但是其中的元素不知道容器的存在?這有什麼道理?從剛開始接觸 MVC 框架到前陣子,這個問題一直在我腦中沒有得到解答。如果真的那麼強制不能讓元素知道容器的話,那麼為什麼在 Java 的框架中,可以藉由實作某些介面,在框架建置當中,參與取得 Context 物件,甚至改變建置過程?
到最近我才知道,這些作法只是特例。是為了一些特殊的情形,是在無法使用其它方式達到需求的時候才被使用。在絕大多數的情形下,元素不該知道容器存在,雖然可能已經大量使用了這個框架特有標籤來註記需要注入的元素。
我認為,背後的原因當然不是因為「要換成另一個框架的時候比較容易」,一個好用的框架幹嘛去換它啊?真正的原因是:「使用 MVC 框架的目的,就是為了讓元素、類別之間的偶合降低」,而讓元素知道 Context 物件的存在,會造成偶合度提高的風險,違反了一開始的初衷。
容器不容易被模擬
容器是由框架作者實作,程式人員再進行擴充、設定。實作框架是為了簡化程式開發,所以框架的作者大多不要求程式人員要了解框架實作的細節,所以一但框架被元素知道,為了要在單元測試下輸入所有必要的元素,導致容器本身也需要模擬,就得實際 new 一個容器元件,或是了解一些方法的實作細節,這許多時候其實是不必要的。既使我們可以直接拿專案程式使用的容器,拿到到單元測試之中運作。當然在程式執行的角度上是可行的,不過在測試的過程,也把專案裡許多測試不需要的元素引用進來。當發現問題的時候,可能是其它和測試無關的元件發生問題。這樣,就背離了單元測試希望在「單純環境」測試的原則。
沒有留言:
張貼留言