摘要:根據(jù)適配器類與適配者類的關(guān)系不同,適配器模式可分為對(duì)象適配器和類適配器兩種,在對(duì)象適配器模式中,適配器與適配者之間是關(guān)聯(lián)聚合關(guān)系在類適配器模式中,適配器與適配者之間是繼承或?qū)崿F(xiàn)關(guān)系。
1、不同國(guó)家的插座是有區(qū)別的,如果我們?nèi)?guó)外旅游,需要帶上國(guó)外的插頭轉(zhuǎn)換器,來(lái)能兼容國(guó)外的插座;
2、手機(jī)的耳機(jī)孔有圓頭和扁頭,如果扁頭的耳機(jī)孔想接圓頭的耳機(jī)就需要一個(gè)耳機(jī)的轉(zhuǎn)換器;
上述所說(shuō)的轉(zhuǎn)換器,其實(shí)就是適配器;它是用來(lái)做兼容的;
適配器模式(Adapter Pattern):將一個(gè)接口轉(zhuǎn)換成客戶希望的另一個(gè)接口,使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)。適配器模式既可以作為類結(jié)構(gòu)型模式,也可以作為對(duì)象結(jié)構(gòu)型模式。
在適配器模式中,我們通過(guò)增加一個(gè)新的適配器類來(lái)解決接口不兼容的問(wèn)題,使得原本沒(méi)有任何關(guān)系的類可以協(xié)同工作。
根據(jù)適配器類與適配者類的關(guān)系不同,適配器模式可分為對(duì)象適配器和類適配器兩種,在對(duì)象適配器模式中,適配器與適配者之間是關(guān)聯(lián)(聚合)關(guān)系;在類適配器模式中,適配器與適配者之間是繼承(或?qū)崿F(xiàn))關(guān)系。
Target(目標(biāo)抽象類):目標(biāo)抽象類定義客戶所需接口,可以是一個(gè)抽象類或接口,也可以是具體類。
Adapter(適配器類):適配器可以調(diào)用另一個(gè)接口,作為一個(gè)轉(zhuǎn)換器,對(duì)Adaptee和Target進(jìn)行適配,適配器類是適配器模式的核心,在對(duì)象適配器中,它通過(guò)繼承Target并關(guān)聯(lián)一個(gè)Adaptee對(duì)象使二者產(chǎn)生聯(lián)系。
Adaptee(適配者類—適配接口):適配者即被適配的角色,它定義了一個(gè)已經(jīng)存在的接口,這個(gè)接口需要適配,適配者類一般是一個(gè)具體類,包含了客戶希望使用的業(yè)務(wù)方法,在某些情況下可能沒(méi)有適配者類的源代碼。
缺省適配器模式(Default Adapter Pattern):當(dāng)不需要實(shí)現(xiàn)一個(gè)接口所提供的所有方法時(shí),可先設(shè)計(jì)一個(gè)抽象類實(shí)現(xiàn)該接口,并為接口中每個(gè)方法提供一個(gè)默認(rèn)實(shí)現(xiàn)(空方法),那么該抽象類的子類可以選擇性地覆蓋父類的某些方法來(lái)實(shí)現(xiàn)需求,它適用于不想使用一個(gè)接口中的所有方法的情況,又稱為單接口適配器模式。
以生活中充電器為例,充電器本身相當(dāng)于適配者 (Adapter),220V 交流電相當(dāng)于被適配者,我們的目標(biāo)(target) 想把220V交流電轉(zhuǎn)成5V直流電
要適配的類,即需要將220v電壓轉(zhuǎn)化為5v電壓
//中國(guó)的電壓220Vpublic class ChinaPower{ private final Integer outPut=220; public Integer getOutPut() { return outPut; }}
適配器接口,只負(fù)責(zé)定義轉(zhuǎn)化需要使用的業(yè)務(wù)邏輯方法,具體實(shí)現(xiàn)交由適配器類完成
//將電壓轉(zhuǎn)化為5v---適配接口public interface TransTo5V{ Integer transTo5V();}
適配器類,繼承了ChinaPower,并實(shí)現(xiàn)了適配器接口,負(fù)責(zé)實(shí)現(xiàn)講220v電壓轉(zhuǎn)化為5v的具體業(yè)務(wù)邏輯代碼實(shí)現(xiàn)
//適配器類---實(shí)現(xiàn)適配器接口public class ChinaAdapter extends ChinaPower implements TransTo5V{ //將220v電壓轉(zhuǎn)換為5v的 @Override public Integer transTo5V() { //獲得被適配類,即我們需要將220v電壓轉(zhuǎn)化為5v返回 Integer output=super.getOutPut(); //進(jìn)行電壓轉(zhuǎn)換操作 return output/44; }}
Phone類,需要用到適配器進(jìn)行兼容,這樣才可以充電
//手機(jī)需要5v的電壓進(jìn)行充電public class Phone{ //通過(guò)適配器獲得5v的電壓 public void charging(ChinaAdapter chinaAdapter) { if(5==chinaAdapter.transTo5V()) { System.out.println("得到5v,充電中..."); } else { System.out.println("電壓過(guò)高,手機(jī)壓力過(guò)大"); } }}
充電測(cè)試
public class test{ @Test public void test() { Phone p=new Phone(); p.charging(new ChinaAdapter()); }}
還是以上面的例子為例,這一次適配器類不再繼承ChinaPower ,而是以聚合的方式來(lái)代替繼承,符合設(shè)計(jì)模式中的"合成復(fù)用原則";java是單繼承機(jī)制,這樣可以保留對(duì)象繼承權(quán);
我們只需要修改適配器類即可:
//適配器類---實(shí)現(xiàn)適配器接口public class ChinaAdapter implements TransTo5V{ private ChinaPower chinaPower; //通過(guò)構(gòu)造器,完成賦值 public ChinaAdapter(ChinaPower chinaPower) { this.chinaPower=chinaPower; } //將220v電壓轉(zhuǎn)換為5v的 @Override public Integer transTo5V() { //獲得被適配類,即我們需要將220v電壓轉(zhuǎn)化為5v返回 Integer output=chinaPower.getOutPut(); //進(jìn)行電壓轉(zhuǎn)換操作 return output/44; }}
測(cè)試
public class test{ @Test public void test() { Phone p=new Phone(); p.charging(new ChinaAdapter(new ChinaPower())); }}
對(duì)象適配器和類適配器其實(shí)算是同一種思想,只不過(guò)實(shí)現(xiàn)方式不同。根據(jù)合成復(fù)用原則,使用組合替代繼承, 所以它解決了類適配器必須繼承被適配者的局限性問(wèn)題;
定義一個(gè)適配器接口:
public interface InterfaceTest { public void m1(); public void m2(); public void m3(); public void m4();}
抽象類 AbsAdapter 將 InterfaceTest 的方法進(jìn)行默認(rèn)實(shí)現(xiàn),當(dāng)子類需要使用適配器接口中的某個(gè)方法,而不是全部方法時(shí),就可以通過(guò)繼承抽象類,來(lái)完成對(duì)需要使用的特定方法重寫操作即可,無(wú)需實(shí)現(xiàn)適配器接口里面的全部方法
public abstract class AbsAdapter implements InterfaceTest { //默認(rèn)實(shí)現(xiàn) public void m1() {} public void m2() {} public void m3() {} public void m4() {}}
Client 調(diào)用接口,重寫適配器抽象類方法
public class Client { public static void main(String[] args) { AbsAdapter absAdapter = new AbsAdapter() { //只需要去覆蓋我們 需要使用 接口方法 @Override public void m1() { System.out.println("使用了m1的方法"); } }; absAdapter.m1(); }}
一個(gè)頂層接口Power
public interface Power{ Integer getOutPut();}
分支一: 中國(guó)的220v電壓
//中國(guó)的電壓220Vpublic class ChinaPower implements Power{ private final Integer outPut=220; @Override public Integer getOutPut() { return outPut; }}
分支二: 日本的110v電壓
//日本電壓110vpublic class JapenPower implements Power{ private final Integer output=110; @Override public Integer getOutPut() { return output; }}
失配器接口–DC5Adapter
//適配器接口public interface DC5Adapter{ boolean support(Power power); Integer transTo5V(Power power);}
適配器類—ChinaAdapter–只負(fù)責(zé)將220v電壓進(jìn)行轉(zhuǎn)換的工作
//該適配器負(fù)責(zé)將中國(guó)的220v電壓轉(zhuǎn)化為5v的電壓public class ChinaAdapter implements DC5Adapter{ //當(dāng)前適配器只負(fù)責(zé)將220v電壓轉(zhuǎn)化為5v的功能實(shí)現(xiàn) private static Integer voltage=220; //判斷當(dāng)前適配器能否勝任傳入power電壓的轉(zhuǎn)化職責(zé) @Override public boolean support(Power power) { if(power.getOutPut().equals(voltage)) return true; return false; } //將220v電壓轉(zhuǎn)換為5v的 @Override public Integer transTo5V(Power power) { //獲得被適配類,即我們需要將220v電壓轉(zhuǎn)化為5v返回 Integer output=power.getOutPut(); //進(jìn)行電壓轉(zhuǎn)換操作 return output/44; }}
適配器類—JapenAdapter–只負(fù)責(zé)將110v電壓進(jìn)行轉(zhuǎn)換的工作
//該適配器負(fù)責(zé)將日本的110v電壓轉(zhuǎn)化為5v的電壓public class JapenAdapter implements DC5Adapter{ //當(dāng)前適配器只負(fù)責(zé)將110v電壓轉(zhuǎn)化為5v的功能實(shí)現(xiàn) private static Integer voltage=110; //判斷是否支持將日本110v電壓轉(zhuǎn)化為5v電壓的操作 @Override public boolean support(Power power) { if(power.getOutPut()==voltage) return true; return false; } //將110v電壓轉(zhuǎn)換為5v的 @Override public Integer transTo5V(Power power) { //獲得被適配類,即我們需要將110v電壓轉(zhuǎn)化為5v返回 Integer output=power.getOutPut(); //進(jìn)行電壓轉(zhuǎn)換操作 return output/22; }}
//手機(jī)需要5v的電壓進(jìn)行充電public class FindAdapter{ //存放所有適配器的set集合 private static final Set<DC5Adapter> DC5Adapters=new HashSet<>(); //通過(guò)靜態(tài)代碼塊進(jìn)行初始化操作 static { DC5Adapters.add(new ChinaAdapter()); DC5Adapters.add(new JapenAdapter()); } // 根據(jù)電壓找合適的變壓器 public DC5Adapter getPowerAdapter(Power power) { DC5Adapter dc5Adapter=null; for(DC5Adapter da:DC5Adapters) { //如果遍歷到當(dāng)前電壓合適的變壓器就直接退出遍歷 if(da.support(power)) { dc5Adapter=da; break; } } //如果遍歷完所有的變壓器都沒(méi)有找到合適的,就拋出異常 if(dc5Adapter==null) { throw new IllegalArgumentException("未能找到合適的變壓器"); } //返回找到的合適的變壓器 return dc5Adapter; }}
public class test{ @Test public void test() { //找尋合適的變壓器是第一步 FindAdapter fa=new FindAdapter(); //找尋可以將220v轉(zhuǎn)化為5v的變壓器,即適配器 DC5Adapter adapter = fa.getPowerAdapter(new ChinaPower()); //輸出當(dāng)前變壓器轉(zhuǎn)化之后的電壓 System.out.println(adapter.transTo5V(new ChinaPower())); }}
具體來(lái)說(shuō),類適配器模式還有如下優(yōu)點(diǎn):
SpringMVM 中的 HandlerAdapter(上圖的第4步), 就使用了適配器模式;
Spring MVC中的適配器模式主要用于執(zhí)行目標(biāo) Controller
中的請(qǐng)求處理方法。
在Spring MVC中,DispatcherServlet
作為用戶,HandlerAdapter
作為期望接口(適配器接口),具體的適配器實(shí)現(xiàn)類用于對(duì)目標(biāo)類進(jìn)行適配,Controller
作為需要適配的類。
為什么要在 Spring MVC 中使用適配器模式?Spring MVC 中的 Controller
種類眾多,不同類型的 Controller
通過(guò)不同的方法來(lái)對(duì)請(qǐng)求進(jìn)行處理。如果不利用適配器模式的話,DispatcherServlet
直接獲取對(duì)應(yīng)類型的 Controller
,需要的自行來(lái)判斷,像下面這段代碼一樣:
if(mappedHandler.getHandler() instanceof MultiActionController){ ((MultiActionController)mappedHandler.getHandler()).xxx }else if(mappedHandler.getHandler() instanceof XXX){ ... }else if(...){ ... }
這樣假設(shè)如果我們?cè)黾右粋€(gè) HardController
,就要在代碼中加入一行 if(mappedHandler.getHandler() instanceof HardController)
,這種形式就使得程序難以維護(hù),也違反了設(shè)計(jì)模式中的開(kāi)閉原則 – 對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。
我們來(lái)看看源碼,首先是適配器接口 HandlerAdapter
//適配器接口public interface HandlerAdapter { //判斷當(dāng)前的controller請(qǐng)求是否能被當(dāng)前的適配器類處理 boolean supports(Object var1); //只有當(dāng)支持處理當(dāng)前請(qǐng)求后,才會(huì)執(zhí)行下面的處理請(qǐng)求方法,返回一個(gè)ModelAndView對(duì)象 ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception; long getLastModified(HttpServletRequest var1, Object var2);}
現(xiàn)該接口的適配器每一個(gè) Controller
都有一個(gè)適配器與之對(duì)應(yīng),這樣的話,每自定義一個(gè) Controller
需要定義一個(gè)實(shí)現(xiàn) HandlerAdapter
的適配器。
springmvc 中提供的 Controller
實(shí)現(xiàn)類有如下:
springmvc 中提供的 HandlerAdapter
實(shí)現(xiàn)類如下
HttpRequestHandlerAdapter 這個(gè)適配器代碼如下:
//不同的適配器類實(shí)現(xiàn)不同的功能//當(dāng)前的HttpRequestHandlerAdapter 適配器類,只負(fù)責(zé)處理關(guān)于HttpRequest相關(guān)的請(qǐng)求public class HttpRequestHandlerAdapter implements HandlerAdapter { public HttpRequestHandlerAdapter() { } //判斷當(dāng)前的controller請(qǐng)求是否是HttpRequestHandler類型的 //當(dāng)前適配器只支持處理當(dāng)前類型的handler public boolean supports(Object handler) { return handler instanceof HttpRequestHandler; } //如果驗(yàn)證支持,會(huì)調(diào)用下面這個(gè)方法進(jìn)行具體邏輯處理 public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //先進(jìn)行強(qiáng)制類型轉(zhuǎn)換,轉(zhuǎn)換為指定的handler類型,然后就可以調(diào)用該類型處理對(duì)應(yīng)請(qǐng)求的方法了 //調(diào)用HttpRequestHandler的handleRequest處理對(duì)應(yīng)的請(qǐng)求 ((HttpRequestHandler)handler).handleRequest(request, response); return null; } public long getLastModified(HttpServletRequest request, Object handler) { return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L; }}
當(dāng)Spring容器啟動(dòng)后,會(huì)將所有定義好的適配器對(duì)象存放在一個(gè)List集合中,當(dāng)一個(gè)請(qǐng)求來(lái)臨時(shí),DispatcherServlet
會(huì)通過(guò) handler
的類型找到對(duì)應(yīng)適配器,并將該適配器對(duì)象返回給用戶,然后就可以統(tǒng)一通過(guò)適配器的 hanle()
方法來(lái)調(diào)用 Controller
中的用于處理請(qǐng)求的方法。
public class DispatcherServlet extends FrameworkServlet {//用于存放所有HandlerAdapter適配器類的list集合 private List<HandlerAdapter> handlerAdapters; //初始化handlerAdapters private void initHandlerAdapters(ApplicationContext context) { //..省略... } // 遍歷所有的 HandlerAdapters,通過(guò) supports 判斷找到匹配的適配器 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } if (ha.supports(handler)) { return ha; } } } // 分發(fā)請(qǐng)求,請(qǐng)求需要找到匹配的適配器來(lái)處理 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request;
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/118905.html
摘要:原文地址設(shè)計(jì)模式十適配器模式在設(shè)計(jì)模式七設(shè)計(jì)模式分類中我們提到過(guò)結(jié)構(gòu)設(shè)計(jì)模式,結(jié)構(gòu)設(shè)計(jì)模式專注于設(shè)計(jì)對(duì)象和實(shí)例的構(gòu)建組合過(guò)程。適配器模式在不修改現(xiàn)有代碼的基礎(chǔ)上,保留了架構(gòu)。 原文地址:PHP設(shè)計(jì)模式(十):適配器模式 Introduction 在PHP設(shè)計(jì)模式(七):設(shè)計(jì)模式分類中我們提到過(guò)結(jié)構(gòu)設(shè)計(jì)模式(Structural patterns),結(jié)構(gòu)設(shè)計(jì)模式專注于設(shè)計(jì)對(duì)象(Objec...
摘要:本文參考于設(shè)計(jì)模式課程設(shè)計(jì)模式之適配器模式設(shè)計(jì)模式是一套被反復(fù)使用的多數(shù)人知曉的經(jīng)過(guò)分類編目的代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。第一個(gè)設(shè)計(jì)模式是適配器模式??偟膩?lái)說(shuō)適配器就是的模式,與修飾模式直接無(wú)感使用不同,適配器模式使用對(duì)象變?yōu)椤? 本文參考于:設(shè)計(jì)模式課程設(shè)計(jì)模式之適配器模式 設(shè)計(jì)模式是一套被反復(fù)使用的、多數(shù)人知曉的、經(jīng)過(guò)分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式是為了重用代碼、讓代碼更容易被...
摘要:與其它模式的異同適配器模式不會(huì)改變?cè)薪涌?,這一點(diǎn)與裝飾者模式和代理模式類似。代理模式適配器模式與代理模式最相似,同樣都是創(chuàng)建一個(gè)新對(duì)象包裝一次,實(shí)現(xiàn)對(duì)本體的調(diào)用。外觀模式外觀模式與適配器模式最大的區(qū)別,是定義了一個(gè)新的接口。 showImg(https://segmentfault.com/img/bVbul8d?w=800&h=600); 適配器模式:將一個(gè)類(對(duì)象)的接口(方法或...
摘要:適配器模式橋接模式過(guò)濾器模式組合模式裝飾器模式外觀模式享元模式代理模式行為型模式這些設(shè)計(jì)模式特別關(guān)注對(duì)象之間的通信。對(duì)象適配器另外一種適配器模式是對(duì)象適配器,它不是使用多繼承或繼承再實(shí)現(xiàn)的方式,而是使用直接關(guān)聯(lián),或者稱為委托的方式。 設(shè)計(jì)模式匯總 創(chuàng)建型模式 這些設(shè)計(jì)模式提供了一種在創(chuàng)建對(duì)象的同時(shí)隱藏創(chuàng)建邏輯的方式,而不是使用新的運(yùn)算符直接實(shí)例化對(duì)象。這使得程序在判斷針對(duì)某個(gè)給定實(shí)例需...
閱讀 2827·2021-10-12 10:12
閱讀 2502·2021-09-02 15:41
閱讀 2651·2019-08-30 15:55
閱讀 1490·2019-08-30 13:05
閱讀 2575·2019-08-29 11:21
閱讀 3628·2019-08-28 17:53
閱讀 3157·2019-08-26 13:39
閱讀 867·2019-08-26 11:50