配接器模式
- 配接器概念介紹
- 介紹
- 角色
- 作業原理
- 3種配接器模式
- 類配接器模式演示
- 物件配接器模式
- 物件配接器的優點
- 介面配接器模式
- 綜合小案例---使用類配接器模式
- power--帶轉換的電壓
- adapter--配接器
- FindAdapter--尋找合適的配接器
- 測驗
- 配接器模式總結
- 主要優點
- 主要缺點
- 適用場景
- spring MVC中的配接器模式
- springMVC處理請求流程
- 請求處理方法中配接器模式部分原始碼探究
- 總結
- 參考文章
配接器概念介紹
1、不同國家的插座是有區別的,如果我們去國外旅游,需要帶上國外的插頭轉換器,來能兼容國外的插座;
2、手機的耳機孔有圓頭和扁頭,如果扁頭的耳機孔想接圓頭的耳機就需要一個耳機的轉換器;

上述所說的轉換器,其實就是配接器;它是用來做兼容的;
介紹
配接器模式(Adapter Pattern):將一個介面轉換成客戶希望的另一個介面,使介面不兼容的那些類可以一起作業,其別名為包裝器(Wrapper),配接器模式既可以作為類結構型模式,也可以作為物件結構型模式,
在配接器模式中,我們通過增加一個新的配接器類來解決介面不兼容的問題,使得原本沒有任何關系的類可以協同作業,
根據配接器類與適配者類的關系不同,配接器模式可分為物件配接器和類配接器兩種,在物件配接器模式中,配接器與適配者之間是關聯(聚合)關系;在類配接器模式中,配接器與適配者之間是繼承(或實作)關系,
角色
Target(目標抽象類):目標抽象類定義客戶所需介面,可以是一個抽象類或介面,也可以是具體類,
Adapter(配接器類):配接器可以呼叫另一個介面,作為一個轉換器,對Adaptee和Target進行適配,配接器類是配接器模式的核心,在物件配接器中,它通過繼承Target并關聯一個Adaptee物件使二者產生聯系,
Adaptee(適配者類—適配介面):適配者即被適配的角色,它定義了一個已經存在的介面,這個介面需要適配,適配者類一般是一個具體類,包含了客戶希望使用的業務方法,在某些情況下可能沒有適配者類的源代碼,
預設配接器模式(Default Adapter Pattern):當不需要實作一個介面所提供的所有方法時,可先設計一個抽象類實作該介面,并為介面中每個方法提供一個默認實作(空方法),那么該抽象類的子類可以選擇性地覆寫父類的某些方法來實作需求,它適用于不想使用一個介面中的所有方法的情況,又稱為單介面配接器模式,
作業原理
- 配接器模式:將一個類的介面轉換成另一種介面,讓原本介面不兼容的類可以兼容;
- 從用戶的角度看不到被適配者;
- 用戶呼叫配接器轉化出來的目標介面方法,配接器再呼叫被適配者的相關介面方法;
3種配接器模式
- 類配接器模式
- 物件配接器模式
- 介面配接器模式
類配接器模式演示
以生活中充電器為例,充電器本身相當于適配者 (Adapter),220V 交流電相當于被適配者,我們的目標(target) 想把220V交流電轉成5V直流電
要適配的類,即需要將220v電壓轉化為5v電壓
//中國的電壓220V
public class ChinaPower
{
private final Integer outPut=220;
public Integer getOutPut() {
return outPut;
}
}
配接器介面,只負責定義轉化需要使用的業務邏輯方法,具體實作交由配接器類完成
//將電壓轉化為5v---適配介面
public interface TransTo5V
{
Integer transTo5V();
}
配接器類,繼承了ChinaPower,并實作了配接器介面,負責實作講220v電壓轉化為5v的具體業務邏輯代碼實作
//配接器類---實作配接器介面
public class ChinaAdapter extends ChinaPower implements TransTo5V
{
//將220v電壓轉換為5v的
@Override
public Integer transTo5V()
{
//獲得被適配類,即我們需要將220v電壓轉化為5v回傳
Integer output=super.getOutPut();
//進行電壓轉換操作
return output/44;
}
}
Phone類,需要用到配接器進行兼容,這樣才可以充電
//手機需要5v的電壓進行充電
public class Phone
{
//通過配接器獲得5v的電壓
public void charging(ChinaAdapter chinaAdapter)
{
if(5==chinaAdapter.transTo5V())
{
System.out.println("得到5v,充電中...");
}
else
{
System.out.println("電壓過高,手機壓力過大");
}
}
}
充電測驗
public class test
{
@Test
public void test()
{
Phone p=new Phone();
p.charging(new ChinaAdapter());
}
}

物件配接器模式
還是以上面的例子為例,這一次配接器類不再繼承ChinaPower ,而是以聚合的方式來代替繼承,符合設計模式中的"合成復用原則";java是單繼承機制,這樣可以保留物件繼承權;
我們只需要修改配接器類即可:
//配接器類---實作配接器介面
public class ChinaAdapter implements TransTo5V
{
private ChinaPower chinaPower;
//通過構造器,完成賦值
public ChinaAdapter(ChinaPower chinaPower)
{
this.chinaPower=chinaPower;
}
//將220v電壓轉換為5v的
@Override
public Integer transTo5V()
{
//獲得被適配類,即我們需要將220v電壓轉化為5v回傳
Integer output=chinaPower.getOutPut();
//進行電壓轉換操作
return output/44;
}
}
測驗
public class test
{
@Test
public void test()
{
Phone p=new Phone();
p.charging(new ChinaAdapter(new ChinaPower()));
}
}

物件配接器的優點
物件配接器和類配接器其實算是同一種思想,只不過實作方式不同,根據合成復用原則,使用組合替代繼承, 所以它解決了類配接器必須繼承被適配者的局限性問題;
介面配接器模式
- 介面配接器模式(Default Adapter Pattern),也叫預設配接器模式;
- 核心思路:當不需要全部實作介面提供的方法時,可先設計一個抽象類實作介面,并為該介面中每個方法提供一個默認實作(空方法),該抽象類的子類可有選擇地覆寫父類的某些方法來實作需求;
- 適用于一個介面不想使用其所有的方法的情況;
定義一個配接器介面:
public interface InterfaceTest {
public void m1();
public void m2();
public void m3();
public void m4();
}
抽象類 AbsAdapter 將 InterfaceTest 的方法進行默認實作,當子類需要使用配接器介面中的某個方法,而不是全部方法時,就可以通過繼承抽象類,來完成對需要使用的特定方法重寫操作即可,無需實作配接器介面里面的全部方法
public abstract class AbsAdapter implements InterfaceTest {
//默認實作
public void m1() {}
public void m2() {}
public void m3() {}
public void m4() {}
}
Client 呼叫介面,重寫配接器抽象類方法
public class Client {
public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter() {
//只需要去覆寫我們 需要使用 介面方法
@Override
public void m1() {
System.out.println("使用了m1的方法");
}
};
absAdapter.m1();
}
}
綜合小案例—使用類配接器模式
power–帶轉換的電壓
一個頂層介面Power
public interface Power
{
Integer getOutPut();
}
分支一: 中國的220v電壓
//中國的電壓220V
public class ChinaPower implements Power
{
private final Integer outPut=220;
@Override
public Integer getOutPut() {
return outPut;
}
}
分支二: 日本的110v電壓
//日本電壓110v
public class JapenPower implements Power
{
private final Integer output=110;
@Override
public Integer getOutPut() {
return output;
}
}
adapter–配接器
失配器介面–DC5Adapter
//配接器介面
public interface DC5Adapter
{
boolean support(Power power);
Integer transTo5V(Power power);
}
配接器類—ChinaAdapter–只負責將220v電壓進行轉換的作業
//該配接器負責將中國的220v電壓轉化為5v的電壓
public class ChinaAdapter implements DC5Adapter
{
//當前配接器只負責將220v電壓轉化為5v的功能實作
private static Integer voltage=220;
//判斷當前配接器能否勝任傳入power電壓的轉化職責
@Override
public boolean support(Power power)
{
if(power.getOutPut().equals(voltage))
return true;
return false;
}
//將220v電壓轉換為5v的
@Override
public Integer transTo5V(Power power)
{
//獲得被適配類,即我們需要將220v電壓轉化為5v回傳
Integer output=power.getOutPut();
//進行電壓轉換操作
return output/44;
}
}
配接器類—JapenAdapter–只負責將110v電壓進行轉換的作業
//該配接器負責將日本的110v電壓轉化為5v的電壓
public class JapenAdapter implements DC5Adapter
{
//當前配接器只負責將110v電壓轉化為5v的功能實作
private static Integer voltage=110;
//判斷是否支持將日本110v電壓轉化為5v電壓的操作
@Override
public boolean support(Power power) {
if(power.getOutPut()==voltage)
return true;
return false;
}
//將110v電壓轉換為5v的
@Override
public Integer transTo5V(Power power)
{
//獲得被適配類,即我們需要將110v電壓轉化為5v回傳
Integer output=power.getOutPut();
//進行電壓轉換操作
return output/22;
}
}
FindAdapter–尋找合適的配接器
//手機需要5v的電壓進行充電
public class FindAdapter
{
//存放所有配接器的set集合
private static final Set<DC5Adapter> DC5Adapters=new HashSet<>();
//通過靜態代碼塊進行初始化操作
static
{
DC5Adapters.add(new ChinaAdapter());
DC5Adapters.add(new JapenAdapter());
}
// 根據電壓找合適的變壓器
public DC5Adapter getPowerAdapter(Power power)
{
DC5Adapter dc5Adapter=null;
for(DC5Adapter da:DC5Adapters)
{
//如果遍歷到當前電壓合適的變壓器就直接退出遍歷
if(da.support(power))
{
dc5Adapter=da;
break;
}
}
//如果遍歷完所有的變壓器都沒有找到合適的,就拋出例外
if(dc5Adapter==null)
{
throw new IllegalArgumentException("未能找到合適的變壓器");
}
//回傳找到的合適的變壓器
return dc5Adapter;
}
}
測驗
public class test
{
@Test
public void test()
{
//找尋合適的變壓器是第一步
FindAdapter fa=new FindAdapter();
//找尋可以將220v轉化為5v的變壓器,即配接器
DC5Adapter adapter = fa.getPowerAdapter(new ChinaPower());
//輸出當前變壓器轉化之后的電壓
System.out.println(adapter.transTo5V(new ChinaPower()));
}
}

配接器模式總結
主要優點
- 將目標類和適配者類解耦,通過引入一個配接器類來重用現有的適配者類,無須修改原有結構,
- 增加了類的透明性和復用性,將具體的業務實作程序封裝在適配者類中,對于客戶端類而言是透明的,而且提高了適配者的復用性,同一個適配者類可以在多個不同的系統中復用,
- 靈活性和擴展性都非常好,通過使用組態檔,可以很方便地更換配接器,也可以在不修改原有代碼的基礎上增加新的配接器類,完全符合“開閉原則”,
具體來說,類配接器模式還有如下優點:
- 由于配接器類是適配者類(配接器介面或配接器介面實作的抽象類)的子類,因此可以在配接器類中置換一些適配者(配接器介面或配接器介面實作的抽象類)的方法,使得配接器的靈活性更強,
- 一個物件配接器可以把多個不同的適配者(配接器介面或配接器介面實作的抽象類)適配到同一個目標;
- 可以適配一個適配者的子類,由于配接器和適配者(配接器介面或配接器介面實作的抽象類)之間是關聯關系,根據“里氏代換原則”,適配者(配接器介面或配接器介面實作的抽象類)的子類也可通過該配接器進行適配,
主要缺點
- 對于Java、C#等不支持多重類繼承的語言,一次最多只能適配一個適配者類(配接器介面或配接器介面實作的抽象類),不能同時適配多個適配者;
- 適配者類不能為最終類,如在Java中不能為final類,C#中不能為sealed類;
- 在Java、C#等語言中,類配接器模式中的目標抽象類只能為介面,不能為類,其使用有一定的局限性,
- 與類配接器模式相比,要在配接器中置換適配者類的某些方法比較麻煩,如果一定要置換掉適配者類的一個或多個方法,可以先做一個適配者類的子類,將適配者類的方法置換掉,然后再把適配者類的子類當做真正的適配者進行適配,實作程序較為復雜,
適用場景
- 系統需要使用一些現有的類,而這些類的介面(如方法名)不符合系統的需要,甚至沒有這些類的源代碼,
- 想創建一個可以重復使用的類,用于與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起作業,
spring MVC中的配接器模式
springMVC處理請求流程

- 第1步 用戶發送請求至前端控制器DispatcherServlet;
- 第2,3步DispatcherServlet收到請求,根據請求url呼叫HandlerMapping處理器映射器找到具體的處理器;生成處理器物件及處理器攔截器(如果有則生成)一并回傳給DispatcherServlet;
- 第4步 DispatcherServlet通過HandlerAdapter處理器配接器呼叫具體的處理器;(這一步用到適配者模式)
- 第5,6步 執行處理器(Controller,也叫后端控制器),回傳ModelAndView;
- 第7步 HandlerAdapter將controller執行結果ModelAndView回傳給DispatcherServlet
- 第8步DispatcherServlet將ModelAndView傳給ViewReslover視圖決議器(但是如果加上@responsebody注解,則回傳值不通過viewResolver,而是直接回傳object);
- 第9步 ViewReslover決議后回傳具體View;
- 第10步 DispatcherServlet對View進行渲染視圖(即將模型資料填充至視圖中);
- 第11步 DispatcherServlet回應用戶,
SpringMVM 中的 HandlerAdapter(上圖的第4步), 就使用了配接器模式;
請求處理方法中配接器模式部分原始碼探究

Spring MVC中的配接器模式主要用于執行目標 Controller 中的請求處理方法,
在Spring MVC中,DispatcherServlet 作為用戶,HandlerAdapter 作為期望介面(配接器介面),具體的配接器實作類用于對目標類進行適配,Controller 作為需要適配的類,
為什么要在 Spring MVC 中使用配接器模式?Spring MVC 中的 Controller 種類眾多,不同型別的 Controller 通過不同的方法來對請求進行處理,如果不利用配接器模式的話,DispatcherServlet 直接獲取對應型別的 Controller,需要的自行來判斷,像下面這段代碼一樣:
if(mappedHandler.getHandler() instanceof MultiActionController){
((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
...
}else if(...){
...
}
這樣假設如果我們增加一個 HardController,就要在代碼中加入一行 if(mappedHandler.getHandler() instanceof HardController),這種形式就使得程式難以維護,也違反了設計模式中的開閉原則 – 對擴展開放,對修改關閉,
我們來看看原始碼,首先是配接器介面 HandlerAdapter
//配接器介面
public interface HandlerAdapter
{
//判斷當前的controller請求是否能被當前的配接器類處理
boolean supports(Object var1);
//只有當支持處理當前請求后,才會執行下面的處理請求方法,回傳一個ModelAndView物件
ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
long getLastModified(HttpServletRequest var1, Object var2);
}
現該介面的配接器每一個 Controller 都有一個配接器與之對應,這樣的話,每自定義一個 Controller 需要定義一個實作 HandlerAdapter 的配接器,
springmvc 中提供的 Controller 實作類有如下:

springmvc 中提供的 HandlerAdapter 實作類如下

HttpRequestHandlerAdapter 這個配接器代碼如下:
//不同的配接器類實作不同的功能
//當前的HttpRequestHandlerAdapter 配接器類,只負責處理關于HttpRequest相關的請求
public class HttpRequestHandlerAdapter implements HandlerAdapter {
public HttpRequestHandlerAdapter() {
}
//判斷當前的controller請求是否是HttpRequestHandler型別的
//當前配接器只支持處理當前型別的handler
public boolean supports(Object handler) {
return handler instanceof HttpRequestHandler;
}
//如果驗證支持,會呼叫下面這個方法進行具體邏輯處理
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//先進行強制型別轉換,轉換為指定的handler型別,然后就可以呼叫該型別處理對應請求的方法了
//呼叫HttpRequestHandler的handleRequest處理對應的請求
((HttpRequestHandler)handler).handleRequest(request, response);
return null;
}
public long getLastModified(HttpServletRequest request, Object handler) {
return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
}
}
當Spring容器啟動后,會將所有定義好的配接器物件存放在一個List集合中,當一個請求來臨時,DispatcherServlet 會通過 handler 的型別找到對應配接器,并將該配接器物件回傳給用戶,然后就可以統一通過配接器的 hanle() 方法來呼叫 Controller 中的用于處理請求的方法,
public class DispatcherServlet extends FrameworkServlet {
//用于存放所有HandlerAdapter配接器類的list集合
private List<HandlerAdapter> handlerAdapters;
//初始化handlerAdapters
private void initHandlerAdapters(ApplicationContext context) {
//..省略...
}
// 遍歷所有的 HandlerAdapters,通過 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;
}
}
}
// 分發請求,請求需要找到匹配的配接器來處理
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
//找到能處理當前processedRequest,即request請求的handler
mappedHandler = getHandler(processedRequest);
// 確定當前handler匹配的配接器類.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
ha.getLastModified(request, mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
// ...省略...
}
通過配接器模式我們將所有的 controller 統一交給 HandlerAdapter 處理,免去了寫大量的 if-else 陳述句對 Controller 進行判斷,也更利于擴展新的 Controller 型別,
總結

使用 HandlerAdapter 的原因分析:
如果處理器的型別不同,有多重實作方式,那么呼叫方式就不是確定的,如果直接呼叫 Controller 方法,就得不斷使用 if else 來進行判斷是哪一種子類然后執行,那么如果后面要擴展 Controller,就得修改原來的代碼,這樣違背了 OCP 原則;
說明:
- Spring定義了一個適配介面,使得每一種Controller有一種對應的配接器實作類;
- 配接器代替 controller執行相應的方法;
- 擴展Controller時,只需要增加一個配接器類就完成了SpringMVC的擴展;
參考文章
設計模式 8 - 配接器模式與springmvc原始碼分析
設計模式 | 配接器模式及典型應用
配接器模式(SpringMVC原始碼分析)
設計模式 | 配接器模式及典型應用
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/296863.html
標籤:其他
