反向溝通 (IoC : Inversion Control )
2.5 框架的重要功能:提供預設行為
doc.Setter( new Integer());
return doc;
}}
其掌握了應用程式物件之生命週期(Lifecycle),其含有物件參考(Reference) 參考到應用程式之物件,也就是它「包含」(Contain)了應用程式之物件。所以,
這種框架又通稱為Container。此外,在建立物件之刻,也呼叫應用物件的 Setter() 函數,建立出Document 與 Integer 兩個物件之相依關係(Depencency)。換句話說,
應用物件之間的相依關係之建立是掌握在框架手中,由框架主動呼叫應用程式而 建立的,這讓應用程式不必誕生其它應用物件,也不必費心管理應用物件之間的 相依性。而是由框架替應用程式『注入』相依關係,免除了應用物件之間的相依 關係,如同:注射流感疫苗,而免除了流感。這通稱為相依性注射(Dependency Injection)。
2.5 框架的重要功能:提供預設行為
2.5.1 預設行為之意義
框架裡的函數內容,通常扮演「預設函數」的角色,表達了慣例之行為。慣 例是自動化科技的基本觀念,也是軟體設計的重要觀念。拿汽車自動排擋做例子 吧﹗自動排擋的優點是:汽車會「自動地」依照速度而換擋,亦即會依慣例來維 持汽車的平穩。這還不夠,若由司機駕駛更佳﹗例如您只要告訴計程車司機:
「到士林夜市」,司機會依照其經驗習慣而選取路線,讓您舒適抵達夜市。更重 要的是,您可特別指示司機,他會按照您的意思而「修正」其慣例。因之慣例的 重要特色為:
● 讓使用者更加輕鬆愉快。
例如上述汽車的三個層次是:
第 2 章 應用框架魅力的泉源:反向溝通 49
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
汽車
司機 乘客
因為汽車會自動依慣例換擋,司機就輕鬆多了。也因為司機會依慣例選 擇理想的路線,乘客不必操心。
● 慣例是可修正的。
慣例只適合一般狀況,若遇特殊狀況發生,應立即修正之。例如波音 747 客機會依照慣例起降,但遭遇特殊狀況(如碰到一大群鴿子),飛行員會立 即修正之。這飛行員的判斷凌駕於慣例之上,達到修正之目的。在電腦軟體 上,也具有三個層次:
電腦硬體
作業系統
應用程式
作業系統包含了各式各樣的慣例函數,自動化地指揮硬體,其降低了應 用程式之負擔。Linux/Windows 等作業系統已有所改進了。在事件驅動觀念 中,作業系統會不斷與應用程式溝通,不斷修正其慣例,裨對外界的事件提 供迅速反應與服務。如下圖:
50 Android應用框架原理與程式設計36 技
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
電腦硬體
作業系統
應用程式 抽象類別
具體類別
抽象類別的預設函數扮演「備胎」角色,當子類別並未覆寫(Override)該函數 時,就會使用備胎。一旦將抽象類別移入框架中,框架就提供預設行為了。
2.5.2 以 Java 程式闡述預設行為
在 Java 裡,預設行為通常表現達於父類別的函數裡,讓子類別自由決定要不 要覆寫(Override)它,如果覆寫它,就會執行子類別的函數;反之。如果不覆寫 它,就會執行父類別的預先所寫的函數。請看下述的預設函數之範例:
// Employee.java package _objects;
public abstract class Employee {
public abstract void SetFee(float basic_fee, float disc);
public abstract float GetTotal();
public abstract void display();
}
// SalesPerson.java package _objects;
public abstract class SalesPerson extends Employee{
第 2 章 應用框架魅力的泉源:反向溝通 51
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
protected String name, sex;
protected float BasicFee, Discount;
public SalesPerson(String na, String sx) { name = na; sex = sx; } public void SetFee(float basic_fee, float disc){
BasicFee = basic_fee;
Discount = disc; } public void display() {
System.out.println(name + ", Fee: " + this.GetTotal());
}}
// SalesSecretary.java package _objects;
public class SalesSecretary extends SalesPerson{
public SalesSecretary(String na, String sx) { super(na, sx); } public float GetTotal() {return BasicFee * Discount - 100; } }
// JMain.java import _objects.*;
public class JMain {
public static void main(String[] args) {
Employee linda = new SalesSecretary("Linda Fan", "Female");
linda.SetFee(2500f, 0.7f);
就轉而執行 SalesPerson 類別預設的 display()函數:
public void display() {
System.out.println(name + ", Fee: " + this.GetTotal());
}
執行到指令:this.GetTotal();
就轉而執行 SalesSecretary 類別的 GetTotal()函數:
public float GetTotal() {
return BasicFee * Discount – 100;
}
52 Android應用框架原理與程式設計36 技
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
這程式顯示了「抽象類別 + 預設函數」的美妙組合,如下圖所示:
JMain main() { linda.display() }
SalesPerson display() { this.GetTotal() }
(反向呼叫)
GetTotal() { …….
}
SalesSecretary
雖然 main()函數仍為程式的啟動者,但主要的處理過程是在 SalesPerson 的 display()函數內。是它決定呼叫 GetTotal()的。子類別 SalesSecretary 扮演配角,其 GetTotal()只是供 SalesPerson 的 display()函數來呼叫之。前面也提供,因為抽象類 別掌握主控權,複雜的指令皆擺在抽象類別中,因而大幅簡化了具體類別開發者 的負擔。◆
第3 章 如何打造應用框架 53
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯