1. 程式人生 > >Java模式之介面卡模式

Java模式之介面卡模式

    今天看java的I/O部分時,又看到了介面卡模式。於是就重溫了一下,感覺應該總結下來了,以備日後使用。也方便大家共同探討。文章部分內容摘抄整理自部落格:http://blog.csdn.net/elegant_shadow/article/details/5006175和http://www.cnblogs.com/java-my-life/archive/2012/04/13/2442795.html。

首先,所謂的介面卡模式在閻巨集博士的《JAVA與模式》一書中開頭是這樣定義介面卡(Adapter)模式的:

介面卡模式把一個類的介面變換成客戶端所期待的另一種介面,從而使原本因介面不匹配而無法在一起工作的兩個類能夠在一起工作。

正如生活中常聽到電源介面卡相似,膝上型電腦的電源介面卡將220v的市電經過轉換變成了電腦可利用的電壓。

現在,在程式設計世界中,假設一個場景,我們有一個Humen類(源),該類擁有name、age、sex屬性以及speak方法,面對一份程式設計師的工作(目標)要求speak、code、workOvertime(加班),顯然能力不足。為了滿足這個需求,我們可以使用介面卡模式將源適配到目標。具體程式碼如下:

源類的程式碼:

public class Humen {
	
	private String name;
	private String sex;
	private int age;
	
	public void speak(){
		System.out.println("Humen can speak . We're the son of God !");
	}
        //setter getter 省略
	
}
目標介面的程式碼:
public interface CodingJob {
	
	public abstract void speak();
	public abstract void code();
	public abstract void workOvertime();

}

介面卡的程式碼:

public class MaleCoder extends Humen implements CodingJob {

	@Override
	public void code() {
		System.out.println("再改需求死給你看。");
	}

	@Override
	public void workOvertime() {
		System.out.println("加班麼,女生當男生用,男生當畜生用。");
	}


}

看過程式碼之後可能會有人質疑,直接修改Humen類豈不是更簡潔明瞭?在這裡我的理解是,很多業務場景是不允許或者不適合直接修改源類的,而且修改源類會導致其結構遭到破壞,不符合java高內聚低耦合的程式設計思想。所以我認為介面卡模式主要用於兩種情況:(1)系統需要使用現有的類,但是現有的類又不能完全符合需要。(2)將彼此沒有太大關聯的類引進來一起完成某項工作(指物件適配,下文有介紹)。就像現實中,一臺膝上型電腦,出差帶到日本去可以換個110v的電源介面卡用(當然也可以用原裝電源介面卡再接一個插座轉換器,此處不考慮),而若是將國內的220v電源介面卡出廠時就整合進筆記本的話,不僅電腦現有硬體結構佈局要改變,而且將面臨無法使用日本110v電壓供電的窘境(同樣忽略插座轉換器)。

另外,上述程式碼闡明的是介面卡模式的一種,即類介面卡,顧名思義,介面卡類(男程式設計師類)繼承自源類,由於java採用的是單繼承,也就是說,這個介面卡類將只能為源類Humen類服務。另外一種則是物件介面卡,與類介面卡的區別是,不採用繼承源類的方式,而是把“源”作為一個構造引數傳入介面卡中,程式碼如下:

public class NewCoder implements CodingJob {
	
	Humen humen;
	
	public NewCoder(Humen humen){
		this.humen=humen;
	}

	@Override
	public void speak() {
		humen.speak();
	}

	@Override
	public void code() {
		
	}

	@Override
	public void workOvertime() {
		
	}

}

概念大概就是這樣,現在來對這兩種適配模式做個比對:

類的適配模式用於單一源的適配,由於它的源的單一化,程式碼實現不用寫選擇邏輯,很清晰;而物件的適配模式則可用於多源的適配,彌補了類適配模式的不足,使得原本用類適配模式需要寫很多介面卡的情況不復存在。缺點是,由於源的數目可以較多,所以具體的實現條件選擇分支比較多,不太清晰。

類介面卡使用物件繼承的方式,是靜態的定義方式;而物件介面卡使用物件組合的方式,是動態組合的方式。

對於類介面卡,由於介面卡直接繼承自源類,使得介面卡不能和源類的子類一起工作,因為繼承是靜態的關係,當介面卡繼承了源類後,就不可能再去處理  源類的子類了。

而對於物件介面卡,一個介面卡可以把多種不同的源適配到同一個目標。換言之,同一個介面卡可以把源類和它的子類都適配到目標介面。因為物件介面卡採用的是物件組合的關係,只要物件型別正確,是不是子類都無所謂。

對於類介面卡,介面卡可以重定義源類的部分行為,相當於子類覆蓋父類的部分實現方法。

對於物件介面卡,要重定義源類的行為比較困難,這種情況下,需要定義源類的子類來實現重定義,然後讓介面卡組合子類。雖然重定義源類的行為比較困難,但是想要增加一些新的行為則方便的很,而且新增加的行為可同時適用於所有的源。

建議儘量使用物件介面卡的實現方式,多用合成/聚合、少用繼承。當然,具體問題具體分析,根據需要來選用實現方式,最適合的才是最好的。

最後,講講預設介面卡模式使用場景:當你想實現一個介面但又不想實現所有介面方法時,就用預設的介面卡模式。具體做法是在介面和具體實現類中新增一個抽象類,而用抽象類去空實現目標介面的所有方法。而具體的實現類只需要覆蓋其需要完成的方法即可。比如上述程式碼中,我不想實現加班(workOvertime)這個方法,介面卡程式碼如下:

介面類還是上文所示CodingJob介面類。

抽象類程式碼如下:
public abstract class CodingJobWithoutWorkOvertime implements CodingJob {

	@Override
	public void code() {

	}

	@Override
	public void speak() {

	}

	@Override
	public void workOvertime() {

	}

}

實現類程式碼如下:

public class FemaleCoder extends CodingJobWithoutWorkOvertime {
	
	public void speak(){
		System.out.println("上得了廳堂,下得了廚房。");
	}
	public void code(){
		System.out.println("寫的了程式碼,打得過色狼。");
	}
	
}

好了,對於介面卡模式我的理解目前就到這個層次了,還有更多想法的希望不吝賜教。