1.適配器模式簡(jiǎn)介
適配器模式:將一個(gè)類的轉(zhuǎn)接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口,適配器模式使得原來由于接口不兼容的而不能的工作的哪些類可以工作,主要作用就是兼容
應(yīng)用場(chǎng)景: 編碼解碼、 一拖三充電頭、HDMI 轉(zhuǎn) VGA
在spring的體現(xiàn): Spring AOP 模塊對(duì) BeforeAdvice、 AfterAdvice、 ThrowsAdvice 三種通知類型的支持實(shí)際上是借助適配器模式來實(shí)現(xiàn)的, 這樣的好處是使得框架允許用戶向框架中加入自己想要支持的任何一種通知類型, 上述三種通知類型是 Spring AOP 模塊定義的, 它們是 AOP 聯(lián)盟定義的 Advice 的子類型。 在spring中 基本adapter結(jié)尾都是適配器
先看一下適配器模式的一種方式對(duì)象適配器的uml圖

可以看到Adapter適配器包含了Adeptee適配者的引用,該適配器的作用把a(bǔ)daptee適配者轉(zhuǎn)換成target接口,實(shí)現(xiàn)了接口的轉(zhuǎn)換問題。
2.適配器模式分類
有三種分類:
-
類適配器(通過引用適配者進(jìn)行組合實(shí)現(xiàn)) -
對(duì)象適配器(通過繼承適配者進(jìn)行實(shí)現(xiàn)) -
接口適配器(通過抽象類來實(shí)現(xiàn)適配)
前二者在實(shí)現(xiàn)上有些許區(qū)別,作用一樣,第三個(gè)接口適配器差別較大。
3.適配器的實(shí)例講解
-
(1)類適配器模式
原理:通過繼承來實(shí)現(xiàn)適配器功能。
當(dāng)我們要訪問的接口A中沒有我們想要的方法 ,卻在另一個(gè)接口B中發(fā)現(xiàn)了合適的方法,我們又不能改變?cè)L問接口A,在這種情況下,我們可以定義一個(gè)適配器p來進(jìn)行中轉(zhuǎn),這個(gè)適配器p要實(shí)現(xiàn)我們?cè)L問的接口A,這樣我們就能繼續(xù)訪問當(dāng)前接口A中的方法(雖然它目前不是我們的菜),然后再繼承接口B的實(shí)現(xiàn)類,這樣我們可以在適配器P中訪問接口B的方法了,這時(shí)我們?cè)谶m配器P中的接口A方法中直接引用B中的合適方法,這樣就完成了一個(gè)簡(jiǎn)單的類適配器。
下面的例子為:通過把普通登錄的接口轉(zhuǎn)換成微信登錄的接口
登錄返回狀態(tài)的類
/**
* @Project: spring
* @description: 登錄返回的結(jié)果的類
* @author: sunkang
* @create: 2018-09-05 20:55
* @ModificationHistory who when What
**/
public class ResultMsg {
private String code;
private String msg;
private Object data;
public ResultMsg(String code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
普通登錄的接口
/**
* @Project: spring
* @description: 登錄的接口業(yè)務(wù)
* @author: sunkang
* @create: 2018-09-05 20:51
* @ModificationHistory who when What
**/
public interface ISiginSerevice {
ResultMsg login(String username,String password);
}
登錄的具體實(shí)現(xiàn)
* @Description:
* @Param: 登錄的具體實(shí)現(xiàn)
* @return:
* @Author: sunkang
* @Date: 2018/9/5
*/
public class SiginService implements ISiginSerevice {
/**
* 登錄的方法
* @param username
* @param password
* @return
*/
public ResultMsg login(String username,String password){
return new ResultMsg("200","登錄成功",new Object());
}
}
通過微信接口登錄
/**
* @Project: spring
* @description: 通過微信接口登錄
* @author: sunkang
* @create: 2018-09-05 20:58
* @ModificationHistory who when What
**/
public interface ISiginForWebChat {
/**
* 通過微信登錄
* @param openId
* @return
*/
ResultMsg loginForWechat(String openId);
}
類適配器(把原有的登錄方法適配成微信登錄的接口)
/**
* @Project: spring
* @description: 類適配器 目標(biāo)接口為ISiginForWebChat,但是適配者的接口為ISiginService
* @author: sunkang
* @create: 2018-09-05 21:29
* @ModificationHistory who when What
**/
public class ClassSiginForWebChatAdapter extends SiginService implements ISiginForWebChat{
@Override
public ResultMsg loginForWechat(String openId) {
return login(openId,null);
}
}
(2)對(duì)象適配器模式
原理:通過組合來實(shí)現(xiàn)適配器功能。
由于比較簡(jiǎn)單,應(yīng)該是比較清楚的,代碼如下
/**
* @Project: spring
* @description: 對(duì)象適配器 默認(rèn)通過組合來實(shí)現(xiàn)的
* @author: sunkang
* @create: 2018-09-05 21:06
* @ModificationHistory who when What
**/
public class ObjectSiginForWebChatAdapter implements ISiginForWebChat {
private ISiginSerevice siginSerevice;
public ObjectSiginForWebChatAdapter(ISiginSerevice siginSerevice) {
this.siginSerevice = siginSerevice;
}
@Override
public ResultMsg loginForWechat(String openId) {
return siginSerevice.login(openId,null);
}
}
-
(3)接口適配器模式
原理:通過抽象類來實(shí)現(xiàn)適配,這種適配稍別于上面所述的適配。
當(dāng)存在這樣一個(gè)接口,其中定義了N多的方法,而我們現(xiàn)在卻只想使用其中的一個(gè)到幾個(gè)方法,如果我們直接實(shí)現(xiàn)接口,那么我們要對(duì)所有的方法進(jìn)行實(shí)現(xiàn),哪怕我們僅僅是對(duì)不需要的方法進(jìn)行置空(只寫一對(duì)大括號(hào),不做具體方法實(shí)現(xiàn))也會(huì)導(dǎo)致這個(gè)類變得臃腫,調(diào)用也不方便,這時(shí)我們可以使用一個(gè)抽象類作為中間件,即適配器,用這個(gè)抽象類實(shí)現(xiàn)接口,而在抽象類中所有的方法都進(jìn)行置空,那么我們?cè)趧?chuàng)建抽象類的繼承類,而且重寫我們需要使用的那幾個(gè)方法即可。
比如現(xiàn)在登錄要支持第三方接口,比如通過qq登錄,通過微信登錄,通過手機(jī)號(hào)和驗(yàn)證碼登錄,但是有的實(shí)現(xiàn)類不是通通用到這些方法,可以用一個(gè)抽象類來實(shí)現(xiàn)接口的所有的方法,作為一個(gè)默認(rèn)的實(shí)現(xiàn),然后具體的實(shí)現(xiàn)類繼承抽>象類,要用到哪個(gè)方法就重寫哪個(gè)方法
第三方登錄的接口
/**
* @Project: spring
* @description: 第三方登錄的接口
* @author: sunkang
* @create: 2018-09-05 21:41
* @ModificationHistory who when What
**/
public interface ISiginForThirdService {
/**
* 通過qq登錄
* @param openId
* @return
*/
ResultMsg loginForQQ(String openId);
/**
* 通過微信登錄
* @param openId
* @return
*/
ResultMsg loginForWechat(String openId);
/**
* 通過手機(jī)號(hào)和驗(yàn)證碼登錄
* @param telphone
* @param code
* @return
*/
ResultMsg loginForTelphone(String telphone,String code);
}
抽象適配器
/**
* @Project: spring
* @description: 抽象適配器 因?yàn)榻涌谔啵杂昧艘粋€(gè)抽象的類的適配器,來默認(rèn)實(shí)現(xiàn)
* @author: sunkang
* @create: 2018-09-05 21:43
* @ModificationHistory who when What
**/
public abstract class SiginForThirdAdapter implements ISiginForThirdService {
@Override
public ResultMsg loginForQQ(String openId) {
return new ResultMsg("200","qq登錄成功",new Object());
}
@Override
public ResultMsg loginForWechat(String openId) {
return new ResultMsg("200","微信登錄成功",new Object());
}
@Override
public ResultMsg loginForTelphone(String telphone, String code) {
return new ResultMsg("200","手機(jī)登錄成功",new Object());
}
}
通過電話號(hào)碼和動(dòng)態(tài)密碼登錄,只需要重寫對(duì)應(yīng)的方法接口
/**
* @Project: spring
* @description: 只需要用電話號(hào)碼登錄
* @author: sunkang
* @create: 2018-09-05 21:44
* @ModificationHistory who when What
**/
public class LoginForTelphoneAdapter extends SiginForThirdAdapter {
public ResultMsg loginForTelphone(String telphone, String code) {
System.out.println("通過電話號(hào)碼登錄");
ResultMsg msg = new ResultMsg("200","qq登錄成功",new Object());
System.out.println("登錄的結(jié)果為"+msg.getMsg());
return msg;
}
}
三種方式的測(cè)試案例
/**
* @Project: spring
* @description: 三種方式實(shí)現(xiàn)的測(cè)試案例
* @author: sunkang
* @create: 2018-09-05 21:17
* @ModificationHistory who when What
**/
public class AdapterTest {
public static void main(String[] args) {
//需要適配的類,要把 ISiginSerevice 變成 ISiginForWebChat
ISiginSerevice siginSerevice = new SiginService();
ISiginForWebChat siginForThreadService = new ObjectSiginForWebChatAdapter(siginSerevice);
//對(duì)象適配器 ,傳的是一個(gè)適配的類,通過組合的方式
ResultMsg resultMsg = siginForThreadService.loginForWechat("1231321ffasfasffad");
System.out.println(resultMsg.getMsg());
//類適配器,通過繼承實(shí)現(xiàn)的
ResultMsg msg = new ClassSiginForWebChatAdapter().loginForWechat("1231321ffasfasffad");
System.out.println(msg.getMsg());
//接口適配器 ,主要實(shí)現(xiàn)了一個(gè)抽象的適配器
//但是接口中有太多方法,我們要使用時(shí)必須實(shí)現(xiàn)接口并實(shí)現(xiàn)其中的所有方法,可以使用抽象類來實(shí)現(xiàn)接口
//并不對(duì)方法進(jìn)行實(shí)現(xiàn)(僅置空),然后我們?cè)倮^承這個(gè)抽象類來通過重寫想用的方法的方式來實(shí)現(xiàn)。這個(gè)抽象類就是適配器
ISiginForThirdService siginForWebChat = new LoginForTelphoneAdapter();
ResultMsg telPoneMsg = siginForWebChat.loginForTelphone("13588304966","1234");
System.out.println(telPoneMsg.getMsg());
}
}
4、適配器模式應(yīng)用場(chǎng)景
類適配器與對(duì)象適配器的使用場(chǎng)景一致,僅僅是實(shí)現(xiàn)手段稍有區(qū)別,二者主要用于如下場(chǎng)景:
- 想要使用一個(gè)已經(jīng)存在的類,但是它卻
不符合現(xiàn)有的接口規(guī)范,導(dǎo)致無法直接去訪問,這時(shí)創(chuàng)建一個(gè)適配器就能間接去訪問這個(gè)類中的方法。 - 我們有一個(gè)類,想將其設(shè)計(jì)為可重用的類(可被多處訪問),我們可以創(chuàng)建適配器來將這個(gè)類來適配其他沒有提供合適接口的類。
以上兩個(gè)場(chǎng)景其實(shí)就是從兩個(gè)角度來描述一類問題,那就是要訪問的方法不在合適的接口里,一個(gè)從接口出發(fā)(被訪問),一個(gè)從訪問出發(fā)(主動(dòng)訪問)。
接口適配器使用場(chǎng)景:
- 想要使用接口中的某個(gè)或某些方法,但是
接口中有太多方法,我們要使用時(shí)必須實(shí)現(xiàn)接口并實(shí)現(xiàn)其中的所有方法,可以使用抽象類來實(shí)現(xiàn)接口,并不對(duì)方法進(jìn)行實(shí)現(xiàn)(僅置空),然后我們?cè)倮^承這個(gè)抽象類來通過重寫想用的方法的方式來實(shí)現(xiàn)。這個(gè)抽象類就是適配器。