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方法和“處理行為”的分離。

轉載于:https://www.cnblogs.com/android-blogs/p/5542153.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/257104.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/257104.shtml
英文地址,請注明出處:http://en.pswp.cn/news/257104.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux-正則表達式學習(精)

正則表達式30分鐘入門教程 來園子之前寫的一篇正則表達式教程,部分翻譯自codeproject的The 30 Minute Regex Tutorial。 由于評論里有過長的URL,所以本頁排版比較混亂,推薦你到原處查看,看完了如果有問題,再到這里來提出. 一些要說的話: 如果你沒有正則表…

學習筆記(08):Python網絡編程并發編程-實現服務端可以對多個客戶端提供服務

立即學習:https://edu.csdn.net/course/play/24458/296237?utm_sourceblogtoedu 鏈接循環,一個服務器服務多個客戶端, 思路1:服務器一個一個地去服務客服端,等服務完一個客戶端后,再去服務下一個客戶端。 弊端&#…

在win10 或者win7系統下裝雙系統ubuntu16.04教程

1、制作u盤啟動,網上有教程推薦使用軟碟通 2、我的是聯想電腦,用分區助手將你的硬盤劃分出來一塊空白的,記得主分區不要超過4個,要不然你劃分出來的空白區裝系統是無用狀態(分空白硬盤網上有教程) 3、將u盤…

Flume sink=avro rpc connection error

要求 conf 文件 a1.sourcesr1 a1.sinksk1 a1.channelsc1a1.sources.r1.typeavro a1.sources.r1.bindmaster a1.sources.r1.port9999a1.sinks.k1.typeavro a1.sinks.k1.hostnameslave1 a1.sinks.k1.port7777a1.channels.c1.typememory a1.channels.c1.capacity1000 a1.channels.…

【騰許Bugly干貨分享】“HTTPS”安全在哪里?

背景 最近基于興趣學學習了下 HTTPS 相關的知識,在此記錄下學習心得。 在上網獲取信息的過程中,我們接觸最多的信息加密傳輸方式也莫過于 HTTPS 了。每當訪問一個站點,瀏覽器的地址欄中出現綠色圖標時,意味著該站點支持 HTTPS 信息…

CCNP精粹系列之十八--路由映射實戰二,博主推薦文章

路由映射實戰二 本篇博文和上一篇是緊密結合的,只是在上個試驗的基礎上作了改動,達到其他的試驗效果。試驗二:在R1上增加一個網段,并發布路由。這里采用三種方法。 如下是第一種,是在試驗一的基礎上直接增加一個網段&a…

HDU 1599 find the mincost route

Floyd可解。求最短。在路上來回。使用Floyd 而在 三同時不 找出最短。然后更新。沒有推理啟動&#xff01;INF。一堆負面結果溢出。 #include<cstdio> #include<cstring> #include<string> #include<queue> #include<algorithm> #include<map…

學習筆記(09):Python網絡編程并發編程-模擬ssh遠程執行命令-代碼實現

立即學習:https://edu.csdn.net/course/play/24458/296239?utm_sourceblogtoedu 1.服務器端&#xff1a;接收客戶端發送的命令,subprocess.POPE()函數可用于產生一個子進程&#xff0c;并且返回子進程的結果 import socket import subprocessphone socket.socket(socket.AF…

C++中兩個數交換不引進中間變量的方法

int a8,b2; 二進制的a1000,b0010; aa方法一&#xff1a;使用異或思想&#xff08;最高級方法&#xff09; aa^b; ba^b; aa^b 方法二&#xff1a;使用加法加法&#xff08;高級方法&#xff09; aab; ba-b; aa-b; 方法三&#xff1a;引進中間變量&#xff08;一般方法&#xff0…

【JUC】JDK1.8源碼分析之ConcurrentLinkedQueue(五)

一、前言 接著前面的分析&#xff0c;接下來分析ConcurrentLinkedQueue&#xff0c;ConcurerntLinkedQueue一個基于鏈接節點的無界線程安全隊列。此隊列按照 FIFO&#xff08;先進先出&#xff09;原則對元素進行排序。隊列的頭部是隊列中時間最長的元素。隊列的尾部 是隊列中時…

學習筆記(10):Python網絡編程并發編程-粘包現象

立即學習:https://edu.csdn.net/course/play/24458/296240?utm_sourceblogtoedu粘包現象&#xff1a;服務器接收到客戶端的命令后&#xff0c;進行執行得到結果后&#xff0c;再發送回給客戶端&#xff0c;在這個過程中如果服務器返回的結果的字節數會大于客戶端所接收最大字節…

某法院HP-P4500存儲數據恢復案例

好久沒出來寫博客了&#xff0c;過年來了一直很忙&#xff0c;尤其是最近&#xff0c;忙著做了好幾個大單子。先是一個醫院50TB的HP-EVA4400&#xff0c;接著是一個法院12TB的HP-P4500&#xff0c;前幾天還有做了一個某游樂城12TB的VMware VMFS虛擬機恢復。雖然忙點&#xff0c…

數組指針與指針數組的區別

1、數組指針 定義&#xff1a;數組指針式一個指向一維數組的指針變量&#xff0c;定義數組指針的格式為&#xff1a; int (*p) [5] 數據類型 &#xff08;*指針名&#xff09; [常量表達式] 數組元素為整形&#xff0c;*p的兩側圓括號不能省略 2、指針數組 定義&#xff1a…

[thinkphp] 是如何輸出一個頁面的

表面上看&#xff0c;TP輸出一個頁面很簡單&#xff1a;$this->display(); 實際上是怎么回事呢&#xff1f;$this->display(); 這個display()方法是定義在ThinkPHP/Library/Think/Controller.class.php這個文件中的 protected function display($templateFile,$charset,$…

關于反射blog

非常好的Java反射例子 瘋狂java 在學習編程的過程中&#xff0c;我覺得不止要獲得課本的知識&#xff0c;更多的是通過學習技術知識提高解決問題的能力&#xff0c;這樣我們才能走在最前方&#xff0c;更多Java學習&#xff0c;請瀏覽瘋狂java官網。Java反射在我們Java學習的…

學習筆記(11):Python網絡編程并發編程-粘包底層原理分析

立即學習:https://edu.csdn.net/course/play/24458/296241?utm_sourceblogtoedu1.send和recv底層分析 1&#xff09;不管是recv還是send都不是直接接收對方數據或者發送給對方數據&#xff0c;而是對自己的操作系統內存進行操作&#xff1b; 2&#xff09;客戶端與服務端并不是…

切面編程(4)

這篇介紹的是最為常見的切面編程首先介紹的是通過注解Aspect來配置AOP類Component Aspect public class Acsep {//定義切入點Pointcut("execution(* com.test.*.*(..))")//切面公式public void aspect(){ }//執行方法之前Before("aspect()")public void be…

c++存儲類型

1、c中的存儲類型一般有靜態存儲、棧、和自動類型三種&#xff0c;一般默認值是為自動類型auto

多線程編程 (1) -NSThread

多線程編程 (1) -NSThread 每個iOS應用程序都有個專門用來更新顯示UI界面、處理用戶觸摸事件的主線程&#xff0c;因此不能將其他太耗時的操作放在主線程中執行&#xff0c;不然會造成主線程堵塞(出現卡機現象)&#xff0c;帶來極壞的用戶體驗。一般的解決方案就是將那些耗時的…

交叉工具鏈的搭建方法(測試成功)

之前安裝了一個rehat6的linux系統&#xff0c;把交叉編譯搭建給忽視了&#xff0c;結果在編譯uboot的時候出現問題&#xff0c;顯示找不到arm-linux-gcc。于是自己來搭建交 叉編譯環境。出現好多錯。先是解壓時沒在后邊加 -C/&#xff0c;后是直接自己創建了個目錄&#xff0c…