一:生活中一些實例?
???? 簡單說現在家庭中使用的電器一般要求電壓是220V的,但是有的電器使用要求的電壓是110V,怎么辦呢?我們直接在110V電壓電器上直接安裝一個變壓器這樣不就行了嗎?這樣我們不就可以使用110V電壓的電器了嗎?
??? 相信很多人都知道恒大足球隊吧,他們隊里有一些外援,他們語言不同,他們是怎么交流了,按照我們想,讓對內的球員都會說中文不就好了。這樣對內的交流問題不就解決了。
二:上面實現功能中會出現什么問題呢?
???? 如果使用電器中出現其他電壓電器,或者有很多110V電壓的電器,我們就需要每次都添加一個變壓器,給沒有這個轉換電壓功能,每一次都需要添加一次方法。這樣做的,每一次都需要我們去添加方法,修改里面原代碼,這樣做違反開閉原則,那么我們要如何做呢?我們可以在外面是先一個接口,然后將轉化方法放在里面,我們需要時候就去調用,這樣不是會很方便。
??? 同樣的如果隊里球員都學習中文,第一不說中文學期很難,第二學習也很占用時間,這樣會影響球員的休息還有訓練。但是如果我們給球員帶一個翻譯器呢?是不是這樣問題就解決了呢?
三:再設計模式中:適配器模式
在適配器模式中如何解決電壓問題呢?
?? 創建接口:
定義變壓器類:
定義改變適配器的類:
定義變壓器地輸出110V:
定義輸出220V電壓類:
這樣我們就在原有類的基礎上,實現調用,也解決每次都需要添加麻煩。也讓兩個不能在一起工作在一起工作。
四:適配器模式定義:
適配器模式(Adapter):就是把一個類的接口轉換成客戶所期待的另一種接口。Adapter使得原本由于接口不兼容而不能在一起工作的那些類可以在一起工作。
一) 適配器模式的結構
?????? 適配器模式有類的適配器模式和對象的適配器模式兩種不同的形式。
?????? 類適配器通過繼承,是靜態的定義方式。
?????? 對象適配器通過代理,是動態組合的方式。
類適配器模式
?????? 類適配器模式把適配的類API轉換成目標類的API
??? 從圖中可以看出Adaptee中沒有sampleOperation2()方法,而客戶端則期待這個方法。為了使客戶端能夠使用這個Apaptee類,所以提供一個中間環節,即類Adapter,也就是這個類把Adaptee與Target類銜接起來。Adapter與Apaptee是繼承關系,這就決定是類適配器模式。
模式中角色:
?目標(Target) 角色:目標角色,期待得到的接口。
?源(Adapee)角色: 適配者角色,現在需要適配的接口。
?適配器(Adapter)角色:適配器角色,適配器類是本模式核心。適配器把源接口轉換成目標接口。顯然這一角色不可能是接口,而必須是具體類。
代碼樣例:
目標角色:
源(Adapee)角色:
適配器角色Adapter:
對象適配器模式
與類的適配器模式一樣,對像的適配器模式把被適配器的類的API轉換成目標類的API,與類適配器不同的是,對象適配器模式不使用繼承關系連接到Adaptee類,使用了委派關系連接到了Adaptee類。
從圖中可以看出Adaptee類中沒有sampleOperation2()方法,客戶端是期待這個方法的。為了使客戶端能夠使用Adaptee類,我們需要提供一個包裝(Wrapper類)Adapter。這個包裝類包裝一個Adatee的實例,從而能使的包裝類能把Adaptee的API與Target類的API銜接起來。Adater與Adaptee是委派關系,這就決定是對象適配器模式
目標角色:
源角色:
適配器角色:
我們了解適配器模式,那么我們看看實際項目中我們應當怎么使用呢?
例如公司購買了一個驗證客戶信息的離架產品類InfoValidation,但是賣方沒有提供源碼。此類只提供用于檢查客戶輸入的信息,包含驗證姓名、地址、電話區號、手機號等功能。現在公司需要增加一個驗證社會安全號(SSN)的功能,這里我們不就可以使用類適配器來做嗎?這里我們首先想一下類適配器結構,然后來構建這里所要實現結構。
結構部分:
下面是我們代碼實現部分:
根據我們以上結構來一步一步實現我們代碼部分:這樣就會非常簡單
首先編寫接口部分:
public interface CusInfoValidator {
?public abstract boolean isValidName(String name);
?public abstract boolean isValidAddress(String address);
?public abstract boolean isValidZipCode(String zipCode);
?public abstract boolean isValidCellPhoneNum(String phoneNum);
?public abstract boolean isValidSSNNum(String SSNNum);
}
是不是跟我們結構一樣的
接下來我們再來編寫所繼承類InfoValidation類(被適配器類)
Class InfoValidation? {
??? public abstract boolean isValidName(String name)? {
??????? boolean isValid=true;
???? String ns = name.trim();
???? String nStr = ns.replaceAll("\\b\\s{1,}\\b", "");
???? int len = nStr.length();
???? System.out.println("******Length = " + len);
???? if(len != 0 ){
??????? for(int m=0; m<len; m++){
?????????? if(Character.isDigit(nStr.charAt(m))==true)
????????????? isValid=false;
??????? }
??????? return isValid;
??????? }
??????? else{
??????? return false;
???? }
??? }
?public abstract boolean isValidAddress(String address){代碼省略}
?public abstract boolean isValidZipCode(String zipCode){代碼省略}
?public abstract boolean isValidCellPhoneNum(String phoneNum){代碼省略}
}
實現我們適配器代碼:
class InformationAdapter extends InfoValidation implements CusInfoValidator{
??? public boolean isValidSSNNum(String SSNNum){
??? boolean isValid=true;
?????? String ns = SSNNum.trim();
??? String nStr = ns.replaceAll("\\s{1,}", "");
??? int len = nStr.length();
??? if ( (nStr.charAt(3) == '-') && (nStr.charAt(6) == '-') && (len==11) ) {
?????? for(int m=0; m<len; m++){
?????? if(? (m != 3) && (m !=6) && ( Character.isDigit(nStr.charAt(m))==false) ){
????????? isValid=false;
?????? }
?????? }
?????? return isValid;
??? }
??? else{
??? return false;
??? }
?}
}
這樣我們就在原有功能上添加一個社會安全號SSN驗證功能。通過這個實例,可能就會有會問:“適配器模式是不是就是給源角色增加新的方法!”,這樣說也對但是不全面。
在適配器模式定義中主要講的是讓接口變換成客戶端所期待一種接口,也就是說‘適配器模式’可以用于增加新的方法,但主要還是轉換接口。例如下面一般是我們人物信息例子:
上面結構途中在Person類中是沒有getDepartment() 方法,在適配器改變Person類構造函數的參數,添加了demartment參數,適配器是改變是接口。我們這個設計目的不是增加方法,而是改變接口。
五: 我們又該怎么權衡類配置器和對象適配器呢,它們又有什么特點呢?
??? 在上面我們寫到類適配器是適用對象繼承的方式,是靜態的方式,而對象適配使用時對象組合方式,是動態組合方式。
??? 對于類適配器,由于適配器直接繼承了Adaptee,使得適配器不能和Adaptee的子類一起工作,因為繼承是靜態的關系,當適配器繼承了Adaptee后,就不可能再去處理? Adaptee的子類了。
對于對象適配器,一個適配器可以把多種不同的源適配到同一個目標。換言之,同一個適配器可以把源類和它的子類都適配到目標接口。因為對象適配器采用的是對象組合的關系,只要對象類型正確,是不是子類都無所謂。
? 對于類適配器,適配器可以重定義Adaptee的部分行為,相當于子類覆蓋父類的部分實現方法。
對于對象適配器,要重定義Adaptee的行為比較困難,這種情況下,需要定義Adaptee的子類來實現重定義,然后讓適配器組合子類。雖然重定義Adaptee的行為比較困難,但是想要增加一些新的行為則方便的很,而且新增加的行為可同時適用于所有的源。
對于類適配器,僅僅引入了一個對象,并不需要額外的引用來間接得到Adaptee。
對于對象適配器,需要額外的引用來間接得到Adaptee。
建議盡量使用對象適配器的實現方式,多用合成/聚合、少用繼承。當然,具體問題具體分析,根據需要來選用實現方式,最適合的才是最好的。
六:那么又有哪些適配器模式的又有哪些優缺點呢?
優點:
?? 1.將目標類和適配者類解耦,通過一入一個適配器類來重用現有的適配者類,而無需要修改原有代碼。
?? 2.增加了類的透明度和復用性,將具體的實現封裝在適配者類中,對于客戶端類來說是透明的,而且提高了適配者的復用性。
?? 3.靈活性和擴展性都非常好,通過使用配置文件可以很方便的更換適配器,也可以在不修改原有代碼基礎上增加新的適配器類,完全符合“開閉原則”。
缺點:
過多的使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是A接口,其實內部被適配成了B接口的實現,一個系統如果太多出現這種情況,無異于一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。
七:我們又該怎樣使用適配器呢?
? 使用場景:
?1).系統中需要使用現有類,而這個類接口不符合系統的需要,也就是不兼容
?2).想要建立一個可重復使用的類,用于關聯彼此沒有太大聯系的一些類
?3).需要一個統一的輸出接口,而輸入端類型不確定
八:適配器模式總結
??? 適配器模式可以重用一個現有的類,滿足客戶需求,將客戶端的調用轉化為現有方法調用。
??? 類適配器:客戶端的需求通過接口表達出來,可以創建一個實現該接口的適配類,適配類同時還要繼承現有類。
??? 對象適配器:客戶端沒有指定接口,創建一個新的適配器類,實現繼承客戶端類,在該類中維護一個現有的類實例對象作為成員變量。