• 沒有找到結果。

框架的重要功能:提供預設行為

反向溝通 (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

¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯