Java中的面向接口編程


 面向接口編程是很多軟件架構設計理論都倡導的編程方式,學習Java自然少不了這一部分,下面是我在學習過程中整理出來的關於如何在Java中實現面向接口編程的知識。分享出來,有不對之處還請大家指正。

 

 

接口體現的是一種規范和實現分離的設計哲學,充分利用接口可以極好地降低程序各模塊之間的耦合,從而提高系統的可擴展性和可維護性。基於這種原則,通常推薦“面向接口”編程,而不是面向實現類編程,希望通過面向接口編程來降低程序的耦合下面分兩種常用場景來示范“面向接口”編程的優勢。

(一)簡單工廠模式

有一個場景,假設程序中有個Comupter類需要組合一個輸出設備,現在有兩個選擇:直接讓Comupter該類組合一個Printer屬性,或者讓Comupter組合一個Output屬性,那么到底采用哪種方式更好呢?

假設讓Computer組合一個Printer屬性,如果有一天系統需要重構,需要使用BetterPrinter來代替Printer,於是我們需要打開Computer類源代碼進行修改。如果系統中只有一個Computer類組合了Printer屬性還好,如果系統中有100個類組合了Printer屬性,甚至1000個,10000個……將意味着我們要打開100個、1000個、10000類進行修改,這是多么大的工作量!

為了避免這個問題,我們讓Comupter組合一個Output屬性,將Comupter類與Printer類完全分離。Computer對象實際組合的是Printer對象,還是BetterPrinter對象,對Computer而言完全透明。當Printer對象切換到BetterPrinter對象時,系統完全不受影響。下面是這個Computer類定義的代碼。

public class Computer

{

private Output out;

public Computer(Output out)

{

this.out = out;

}

//定義一個模擬獲取字符串輸入的方法

public void keyIn(String msg)

{

out.getData(msg);

}

//定義一個模擬打印的方法

public void print()

{

out.out();

}

}

上面的Computer類已經完全與Printer類分離開,只是與Output接口耦合。Computer不再負責創建Output對象,系統提供一個Output工廠來負責生成Output對象。這個OutputFactory工廠類代碼如下:

public class OutputFactory

{

public Output getOutput()

{

return new Printer();

}

public static void main(String[] args) 

{

OutputFactory of = new OutputFactory();

Computer c = new Computer(of.getOutput());

c.keyIn("瘋狂Java講義");

c.keyIn("輕量級J2EE企業應用實戰");

c.print();

}

}

在該OutputFactory類中包含了一個getOutput方法,該方法返回一個Output實現類的實例,該方法負責創建Output實例,具體創建哪一個實現類的對象由該方法決定(具體由該方法中粗體部分控制,當然也可以增加更復雜的控制邏輯)。如果系統需將Printer改為BetterPrinter實現類,只需要讓BetterPrinter實現Output接口,並改變OutputFactory類中的getOutput方法即可。

 

下面是BetterPrinter實現類的代碼,BetterPrinter只是對原有的Printer進行簡單修改,以模擬系統重構后的改進。

public class BetterPrinter implements Output

{

private String[] printData = new String[MAX_CACHE_LINE * 2];

//用以記錄當前需打印的作業數

private int dataNum = 0;

public void out()

{

//只要還有作業,繼續打印

while(dataNum > 0)

{

System.out.println("高速打印機正在打印:" + printData[0]);

//把作業隊列整體前移一位,並將剩下的作業數減1

System.arraycopy(printData , 1, printData, 0, --dataNum);

}

}

public void getData(String msg)

{

if (dataNum >= MAX_CACHE_LINE * 2)

{

System.out.println("輸出隊列已滿,添加失敗");

}

else

{

//把打印數據添加到隊列里,已保存數據的數量加1。

printData[dataNum++] = msg;

}

}

}

上面的BetterPrinter類也實現了 Output接口,因此也可當成Output對象使用,於是我們只要把OutputFactory工廠類的getOutput方法中粗體部分改為如下代碼:

return new BetterPrinter();

再次運行前面的OutputFactory.java程序,發現系統運行時已經改為BetterPrinter對象,而不再是原來的Printer對象。

通過這種方式,我們把所有生成Output對象的邏輯集中在OutputFactory工廠類中管理,而所有需要使用Output對象的類只需與Output接口耦合,而不是與具體的實現類耦合。即使系統中有很多類使用了Printer對象,只要OutputFactory類的getOutput方法來生成Output對象是BetterPrinter對象,則它們全部都會改為使用BetterPrinter對象,而所有程序無需修改,只需要修改OutputFactory工廠的getOutput的方法實現即可。


(二)命令模式

考慮這樣一種場景:某個方法需要完成某一個行為,但這個行為的具體實現無法確定,必須等到執行該方法時才可以確定。具體一點:假設有個方法需要遍歷某個數組的數組元素,但無法確定在遍歷數組元素時如何處理這些元素,需要在調用該方法時指定具體的處理行為。

這個要求看起來有點奇怪:這個方法需要不僅要普通數據可以變化,甚至還有方法執行體也需要變化,難道我們能把“處理行為”作為一個參數傳入該方法?

對於這樣一個需求,我們必須把“處理行為”作為參數傳入該方法,這個“處理行為”用編程來實現就是一段代碼。那如何把這段代碼傳入該方法呢?

因為Java不允許代碼塊單獨存在,因此我們使用一個Command接口來定義一個方法,用這個方法來封裝“處理行為”。下面是該Command接口代碼。

public interface Command

{

//接口里定義的process方法用於封裝“處理行為”

void process(int[] target);

}

上面的Command接口里定義了一個process方法,這個方法用於封裝“處理行為”,但這個方法沒有方法體——因為現在還無法確定這個處理行為。

下面是需要處理數組的處理類,在這個處理類中包含一個process方法,這個方法無法確定處理數組的處理行為,所以定義該方法時使用了一個Command參數,這個Command參數負責對數組的處理行為。該類的程序代碼如下。

public class ProcessArray

{

public void process(int[] target , Command cmd) 

{

cmd.process(target);

}

}

通過一個Command類,就實現了讓ProcessArray類和具體“處理行為”的分離,程序使用Command接口代表了對數組的處理行為。Command接口也沒有提供真正的處理,只有等到需要調用ProcessArray對象process方法時,才真正傳入一個Command對象,才確定對數組的處理行為。

下面程序示范了對數組的兩種處理方式:

public class TestCommand

{

public static void main(String[] args) 

{

ProcessArray pa = new ProcessArray();

int[] target = {3, -4, 6, 4};

//第一次處理數組,具體處理行為取決於PrintCommand

pa.process(target , new PrintCommand());

System.out.println("------------------");

//第二次處理數組,具體處理行為取決於AddCommand

pa.process(target , new AddCommand());

}

}

下面分別是PrintCommand類和AddCommand類的代碼。

public class PrintCommand implements Command

{

public void process(int[] target)

{

for (int tmp : target )

{

System.out.println("迭代輸出目標數組的元素:" + tmp);

}

}

}

public class AddCommand implements Command

{

public void process(int[] target)

{

int sum = 0;

for (int tmp : target )

{

sum += tmp;

}

System.out.println("數組元素的總和是:" + sum);

}

}

對於PrintCommand和AddCommand兩個實現類而言,實際有意義的部分就是process(int[] target)方法,該方法的方法體就是傳入ProcessArray類里process方法的“處理行為”,通過這種方式就可實現process方法和“處理行為”的分離。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM