程式扎記: [ OSGi In Action ] Introducing OSGi : Mastering modularity (1)

標籤

2011年7月10日 星期日

[ OSGi In Action ] Introducing OSGi : Mastering modularity (1)

前言 : 
這裡的主題包含 : 
* 了解什麼是模組(modularity) , 以及為什麼需要它.
* 使用 metadata 來描述 OSGi 的 bundles
* 說明如何使用 bundle metadata 來管理代碼的Visibility
* 示範如何使用 bundles 來建立一個程式

我們知道 OSGi framework 主要是由 module, lifecycle 與 service 三個 layers 組成, 這裡我們將重點放在 module layer. 首先我們會大約說明一下 modularity 的特性以及使用它將會對我們 designing, building 以及 maintaining Java 程式造成什麼影響. 接著我們會開始介紹 OSGi module metadata 並透過簡單範例來進行說明. 

什麼是 Modularity ? 
什麼是 modularity 呢? 簡單的說他就是一塊塊邏輯上獨立的程式代碼並代表著某種功能(Log, GUI etc), 透過這些獨立的 modularity, 我們可以組成一個程式. 而那些雖然獨立卻又相互合作的程式區塊我們便稱它為 module. 那這些 module 如何相互合作呢? 透過將其他 modules 用到的 API exports 出來與將不需要對其他 modules 開放的 classes/APIs 隱藏便是 module 被設計出來的特性/功能之一. 下圖的 Module 包含了 Class1-5, 但是只對 Class3 與 Class4 進行 export, 這樣其他 Modules 便看得到也能夠使用 Class3,4. 但是因為 Class1,2,5 未開放, 所以其它 Modules 是看不到也不能使用. 因為這樣的特性, 迫使我們在進行程式開發並須先對功能進行切割並透過 Module 進行互相 Import/Export. 而這樣帶來什麼好處呢? 後面會有詳細的說明. 
 
Figure 2.1 A module defines a logical boundary. The module itself is explicitly in control of which classes are completely encapsulated and which are exposed for external use. 

- Modularity vs. object orientation 
物件導向的開發概念其實也與 Module 想表達的大同小異, 都希望將程式的開發能夠進行某種程度的切割成比較小的模組或功能後 (separation of concerns), 再進行開發. 除了可以彼此獨立並行開發外, 也可以在某種程度上降低耦合與開發的難度. 而 Modularity 便是這一想法的最好實現. 

接著假設你需要開發某個程式的某個功能. 你可能會產出一個或多個 classes, 甚至你的 classes 也用到了其它專案的 classes 或是 JRE 的 classes, 當你開發完成後, 一個複雜的 classes 關聯圖也隨之產生. 沒有人知道到底某個 class 需要那些外部的 class, 唯一的途徑就是從 source code level 去推敲, 好消息是這些關係你必須在 compilation time 找出來, 否則會 compilation error.(這樣你就知道少了什麼 class), 壞消息是就算你 compile 成功, 如果在 execution time 需要的 class 如果沒有出現在 class path 上, 你還是會得到 “java.lang.NoClassDefFoundError”! 問題是沒人來維護這樣複雜的 classes 關聯圖. 

因此我們需要一個工具/平臺來幫我們管理類別的Import/Export 關係. 而 Module 封裝了類別, 並且允許你定義 Import/Export 關係, 下圖 Figure 2.2 說明了這種可能性 : 
 
Figure 2.2 Classes have explicit dependencies due to the references contained in the code. Modules have implicit dependencies due to the code they contain. 

當你在開發 Java 時, 你在設計 class 時可以想成 “programming in the small”, 你在意的是這個 class 的功能實現而並不在義它在整個程式扮演著什麼角色. 當你將一個個的 classes 組成一個 module 時, 可以想成 “programming in the large”, 因為 module 是程式組成的一個邏輯單位, 你必須考慮到其他 modules 會用到這個 module 什麼(export), 這個 module 會用到其它 modules 什麼(import). 因此你必須關注在這些 modules 的關係. 

有了以上的說明與概念後, 我們來看一下書上對 MODULE 的定義 : 
MODULE A set of logically encapsulated implementation classes, an optional public API based on a subset of the implementation classes, and a set of dependencies on external code.

從上面定義看來, Module 不就是一個 classes 的合體, 那個我們常用的 jar 有什麼差別? 在回答這個問題前, 我們先來看看何謂 Logical/Physical modularity : 
Logical vs. physical modularity 
In OSGi, these two concepts are largely conflated; a logical module is referred to as a bundle, and so is the physical module (that is, the JAR file). Even though these two concepts are nearly synonymous in OSGi, for modularity in general they aren’t, because it’s possible to have logical modularity without physical modularity or to package multiple logical modules into a single physical module. Physical modules are sometimes also referred to as deployment modules or deployment units.

OSGi 的 module layer 其實就是在講上述的 modularity 的特性. 但它不是完全適用於所有的程式開發, 在採用 modularity 之前也許可以先問問自己為什麼需要 modularity. 

為什麼 Modularity ? 
當程式的規模與複雜度不斷的成長時, modularity 變成了良藥. 透過 divide and conquer 策略, 除了可以降低開發的複雜度外, 也可以提升聚合度(Cohesion)與降低耦合性(Coupling), 底下是聚合與耦合的說明 : 
- Cohesion 
Cohesion measures how closely aligned a module’s classes are with each other and with achieving the module’s intended functionality. You shouldstrive for high cohesion in your modules. For example, a module shouldn’t address many different concerns (network communication, persistence, XML parsing, and so on): it should focus on a single concern.

- Coupling 
Coupling, on the other hand, refers to how tightly bound or dependent different modules are on each other. You should strive for low coupling among your modules. For example, you don’t want every module to depend on all other modules.

透過OSGi 提供的 modularity, 你可以建立 “高聚合, 低耦合” 的程式, 並幫助你提高代碼的重用性並降低與其他程式的相依性. 除此之外你還可以解決在 Java 中缺發 modularity 所遇到的問題, 更不用煩惱到底 class path 少了什麼 Jar 檔或是遇到引用 Jar 檔順序問題引起的屏蔽效應. 

Modularizing 的一個簡單範例 : 
底下我們將透過一個 GUI 程式來說明從一個簡單的可執行 Jar 檔如何轉換成運行在 OSGi module layer 的程式. 因為原有的 GUI 程式有遵從物件導向概念的面向介面開發, 所以在進行轉換過程並不會遇到太多問題, 底下是原始 Jar 檔的內容說明 : 
 

原始Jar 檔中重要的類別說明如下 : 
 

接著我們來看看這個GUI 能做些什麼. 當你在 JToolBar 上選擇某一個圖形後, 接著在 JPanel 上點擊後便會出現對應該圖形的形狀 (圓, 方, 三角形). 而JToolBar 上的圖形都是類別ShapeComponent (繼承自 JComponent), 而 ShapeComponent  SimpleShape 相關聯, 透過 SimpleShape 可以在 JPanel 進行繪圖. 下圖 Figure 2.4 為該 GUI 的示意圖 : 
 
Figure 2.4 The paint program is a simple Swing application. 

類別 PaintFrame 為程式入口類別, 底下圖 figure 2.5 為相關類別的 UML 類別關係圖 : 
 
Figure 2.5 Paint program class relationships 

姑且我們稱目前的原始可執行 Jar 檔為版本 1.0.0. 我們的目標就是將原始的 Jar 檔根據一定流程與分析拆解成互相獨立卻又互相合作的 modules (有可能分析完後只需要一個 module). 而如何寫好一個 GUI 程式不在我們這邊討論範圍. 而分解完後的程式 (由 module 組成) 將具有更好的高聚合與低耦合的特性. 

一開始我們可以簡單的分解 GUI 為 1. Export 出去的 Interface, 2. Interface 的實作, 3. 主程式. 至於怎麼切割並沒有一定的規則. 但原則就是切割後的 modules 彼此互相獨立卻又相輔相成, 並且可以重複使用代碼與切換實作的彈性. 因為 OSGi export 出去的單位是 Package, 而 SimpleShape interface 比較像是 Export 出去的 Interface, 所以我們便將他從 package org.foo.paint 搬到 package org.foo.shape. 接著根據我們剛剛的分析可以知道下面三個 packages 便代表三個互相獨立卻又互相合作的 modules : 
* org.foo.shape—The public API for creating shapes
* org.foo.shape.impl—Various shape implementations
* org.foo.paint—The application implementation

接著我們便可以將這三個 packages 包裝成三個 Jar 檔(physical modularity). 但是這樣還不夠成為 OSGi 的 bundle (logical modularity), 在開始建立 OSGi 的 bundle 前, 我們先來認識一下何謂 bundle. 

Bundle 介紹 : 
在開始使用 OSGi 的功能前, 你必須先了解什麼是 bundle. 而 bundle 也是 OSGi 實現 modularity 最基本的概念, 它的定義如下 : 
BUNDLE A physical unit of modularity in the form of a JAR file containing code, resources, and metadata, where the boundary of the JAR file also serves as the encapsulation boundary for logical modularity at execution time.

一個 bundle 的示意圖可以參考下圖 figure 2.6. 而 bundle 與 一般 Jar 檔最大的差別在於許多 OSGi 獨有的 metadata, 而這些 metadata 都放在 META-INF/MANIFEST.MF 裡. 在這邊我們稱它為 manifest file. 先前我們有提到 physical modularity 與 logical modularity 的概念, 而 bundle 則是這兩個概念的合體. 底下我們就 bundle 在 physical modularity 與 logical modularity 上的意義進行說明. 
 
Figure 2.6 A bundle can contain all the usual artifacts you expect in a standard JAR file. The only major difference is that the manifest file contains information describing the bundle’s modular characteristics. 

- The bundle’s role in physical modularity 
從 physical modularity 來看 bundle 的意義, 簡單來說就是一個容器, 任何在這個 bundle 存在的 classes, resources甚至於 manifest file 都是唯一屬於這個 bundle 的 成員. 透過適當的 metadata 定義, bundle 將能在 OSGi 的環境下運作良好. 底下圖 Figure 2.7 為示意圖 : 
 

- The bundle’s role in logical modularity 
在 logical modularity 來看 bundle 的意義的話, 簡單來說就是該 bundle 與 其他 bundles 的關係. 透過 metadata 你可以 export 出 public 的 API 或是 classes 給外部的 bundles 使用, 也可以 import 進來外部 module export 出來的 API 或 class. 而這樣的關係都可以在 manifest file 中定義. 透過這樣的 export/import 關係形成了所謂的 logical boundary. 有點類似 Java 中的 public, private 與 protected. 被 exported 出去的就是 public, 而沒有被 exported 的相對於外部的 bundle 就是 private. 請參考 figure 2.8 示意圖 : 
 
Figure 2.8 Packages (and therefore the classes in them) contained in a bundle are private to that bundle unless explicitly exposed, allowing them to be shared with other bundles. 


Supplement :
[ OSGi In Action ] Introducing OSGi : Mastering modularity (1)
[ OSGi In Action ] Introducing OSGi : Mastering modularity (2)
[ OSGi In Action ] Introducing OSGi : Mastering modularity (3)
Source code for osgi-in-action

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!