日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

设计模式 | 适配器模式及典型应用

發布時間:2024/1/17 asp.net 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 设计模式 | 适配器模式及典型应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

本文介紹適配器模式,源碼分析spring aop, jpa, mvc中的適配器模式

推薦閱讀

設計模式 | 簡單工廠模式及典型應用
設計模式 | 工廠方法模式及典型應用
設計模式 | 抽象工廠模式及典型應用
設計模式 | 建造者模式及典型應用
設計模式 | 原型模式及典型應用
設計模式 | 外觀模式及典型應用
設計模式 | 裝飾者模式及典型應用

更多內容可訪問我的個人博客:laijianfeng.org

關注【小旋鋒】微信公眾號,及時接收博文推送

適配器模式

適配器模式(Adapter Pattern):將一個接口轉換成客戶希望的另一個接口,使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)。適配器模式既可以作為類結構型模式,也可以作為對象結構型模式。

在適配器模式中,我們通過增加一個新的適配器類來解決接口不兼容的問題,使得原本沒有任何關系的類可以協同工作。

根據適配器類與適配者類的關系不同,適配器模式可分為對象適配器和類適配器兩種,在對象適配器模式中,適配器與適配者之間是關聯關系;在類適配器模式中,適配器與適配者之間是繼承(或實現)關系。

角色

Target(目標抽象類):目標抽象類定義客戶所需接口,可以是一個抽象類或接口,也可以是具體類。

Adapter(適配器類):適配器可以調用另一個接口,作為一個轉換器,對Adaptee和Target進行適配,適配器類是適配器模式的核心,在對象適配器中,它通過繼承Target并關聯一個Adaptee對象使二者產生聯系。

Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經存在的接口,這個接口需要適配,適配者類一般是一個具體類,包含了客戶希望使用的業務方法,在某些情況下可能沒有適配者類的源代碼。

缺省適配器模式(Default Adapter Pattern):當不需要實現一個接口所提供的所有方法時,可先設計一個抽象類實現該接口,并為接口中每個方法提供一個默認實現(空方法),那么該抽象類的子類可以選擇性地覆蓋父類的某些方法來實現需求,它適用于不想使用一個接口中的所有方法的情況,又稱為單接口適配器模式。缺省適配器模式是適配器模式的一種變體,其應用也較為廣泛。在JDK類庫的事件處理包java.awt.event中廣泛使用了缺省適配器模式,如WindowAdapter、KeyAdapter、MouseAdapter等。

示例

類適配器

首先有一個已存在的將被適配的類

public class Adaptee {public void adapteeRequest() {System.out.println("被適配者的方法");} } 復制代碼

定義一個目標接口

public interface Target {void request(); } 復制代碼

怎么才可以在目標接口中的 request() 調用 Adaptee 的 adapteeRequest() 方法呢?

如果直接實現 Target 是不行的

public class ConcreteTarget implements Target {@Overridepublic void request() {System.out.println("concreteTarget目標方法");} } 復制代碼

如果通過一個適配器類,實現 Target 接口,同時繼承了 Adaptee 類,然后在實現的 request() 方法中調用父類的 adapteeRequest() 即可實現

public class Adapter extends Adaptee implements Target{@Overridepublic void request() {//...一些操作...super.adapteeRequest();//...一些操作...} } 復制代碼

我們來測試一下

public class Test {public static void main(String[] args) {Target target = new ConcreteTarget();target.request();Target adapterTarget = new Adapter();adapterTarget.request();} } 復制代碼

輸出

concreteTarget目標方法 被適配者的方法 復制代碼

這樣我們即可在新接口 Target 中適配舊的接口或類

對象適配器

對象適配器與類適配器不同之處在于,類適配器通過繼承來完成適配,對象適配器則是通過關聯來完成,這里稍微修改一下 Adapter 類即可將轉變為對象適配器

public class Adapter implements Target{// 適配者是對象適配器的一個屬性private Adaptee adaptee = new Adaptee();@Overridepublic void request() {//...adaptee.adapteeRequest();//...} } 復制代碼

注意這里的 Adapter 是將 Adaptee 作為一個成員屬性,而不是繼承它

電壓適配器

再來一個好理解的例子,我們國家的民用電都是 220V,日本是 110V,而我們的手機充電一般需要 5V,這時候要充電,就需要一個電壓適配器,將 220V 或者 100V 的輸入電壓變換為 5V 輸出

定義輸出交流電接口,輸出220V交流電類和輸出110V交流電類

public interface AC {int outputAC(); }public class AC110 implements AC {public final int output = 110;@Overridepublic int outputAC() {return output;} }public class AC220 implements AC {public final int output = 220;@Overridepublic int outputAC() {return output;} } 復制代碼

適配器接口,其中 support() 方法用于檢查輸入的電壓是否與適配器匹配,outputDC5V() 方法則用于將輸入的電壓變換為 5V 后輸出

public interface DC5Adapter {boolean support(AC ac);int outputDC5V(AC ac); } 復制代碼

實現中國變壓適配器和日本變壓適配器

public class ChinaPowerAdapter implements DC5Adapter {public static final int voltage = 220;@Overridepublic boolean support(AC ac) {return (voltage == ac.outputAC());}@Overridepublic int outputDC5V(AC ac) {int adapterInput = ac.outputAC();//變壓器...int adapterOutput = adapterInput / 44;System.out.println("使用ChinaPowerAdapter變壓適配器,輸入AC:" + adapterInput + "V" + ",輸出DC:" + adapterOutput + "V");return adapterOutput;} }public class JapanPowerAdapter implements DC5Adapter {public static final int voltage = 110;@Overridepublic boolean support(AC ac) {return (voltage == ac.outputAC());}@Overridepublic int outputDC5V(AC ac) {int adapterInput = ac.outputAC();//變壓器...int adapterOutput = adapterInput / 22;System.out.println("使用JapanPowerAdapter變壓適配器,輸入AC:" + adapterInput + "V" + ",輸出DC:" + adapterOutput + "V");return adapterOutput;} } 復制代碼

測試,準備中國變壓適配器和日本變壓適配器各一個,定義一個方法可以根據電壓找到合適的變壓器,然后進行測試

public class Test {private List<DC5Adapter> adapters = new LinkedList<DC5Adapter>();public Test() {this.adapters.add(new ChinaPowerAdapter());this.adapters.add(new JapanPowerAdapter());}// 根據電壓找合適的變壓器public DC5Adapter getPowerAdapter(AC ac) {DC5Adapter adapter = null;for (DC5Adapter ad : this.adapters) {if (ad.support(ac)) {adapter = ad;break;}}if (adapter == null){throw new IllegalArgumentException("沒有找到合適的變壓適配器");}return adapter;}public static void main(String[] args) {Test test = new Test();AC chinaAC = new AC220();DC5Adapter adapter = test.getPowerAdapter(chinaAC);adapter.outputDC5V(chinaAC);// 去日本旅游,電壓是 110VAC japanAC = new AC110();adapter = test.getPowerAdapter(japanAC);adapter.outputDC5V(japanAC);} } 復制代碼

輸出

使用ChinaPowerAdapter變壓適配器,輸入AC:220V,輸出DC:5V 使用JapanPowerAdapter變壓適配器,輸入AC:110V,輸出DC:5V 復制代碼

適配器模式總結

主要優點

  • 將目標類和適配者類解耦,通過引入一個適配器類來重用現有的適配者類,無須修改原有結構。
  • 增加了類的透明性和復用性,將具體的業務實現過程封裝在適配者類中,對于客戶端類而言是透明的,而且提高了適配者的復用性,同一個適配者類可以在多個不同的系統中復用。
  • 靈活性和擴展性都非常好,通過使用配置文件,可以很方便地更換適配器,也可以在不修改原有代碼的基礎上增加新的適配器類,完全符合“開閉原則”。
  • 具體來說,類適配器模式還有如下優點:

    • 由于適配器類是適配者類的子類,因此可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強。

    對象適配器模式還有如下優點:

    • 一個對象適配器可以把多個不同的適配者適配到同一個目標;
    • 可以適配一個適配者的子類,由于適配器和適配者之間是關聯關系,根據“里氏代換原則”,適配者的子類也可通過該適配器進行適配。

    類適配器模式的缺點如下:

  • 對于Java、C#等不支持多重類繼承的語言,一次最多只能適配一個適配者類,不能同時適配多個適配者;
  • 適配者類不能為最終類,如在Java中不能為final類,C#中不能為sealed類;
  • 在Java、C#等語言中,類適配器模式中的目標抽象類只能為接口,不能為類,其使用有一定的局限性。
  • 對象適配器模式的缺點如下:

    • 與類適配器模式相比,要在適配器中置換適配者類的某些方法比較麻煩。如果一定要置換掉適配者類的一個或多個方法,可以先做一個適配者類的子類,將適配者類的方法置換掉,然后再把適配者類的子類當做真正的適配者進行適配,實現過程較為復雜。

    適用場景

    • 系統需要使用一些現有的類,而這些類的接口(如方法名)不符合系統的需要,甚至沒有這些類的源代碼。
    • 想創建一個可以重復使用的類,用于與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。

    源碼分析適配器模式的典型應用

    spring AOP中的適配器模式

    在Spring的Aop中,使用的 Advice(通知) 來增強被代理類的功能。

    Advice的類型有:MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice

    在每個類型 Advice 都有對應的攔截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor

    Spring需要將每個 Advice 都封裝成對應的攔截器類型,返回給容器,所以需要使用適配器模式對 Advice 進行轉換

    三個適配者類 Adaptee 如下:

    public interface MethodBeforeAdvice extends BeforeAdvice {void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable; }public interface AfterReturningAdvice extends AfterAdvice {void afterReturning(@Nullable Object var1, Method var2, Object[] var3, @Nullable Object var4) throws Throwable; }public interface ThrowsAdvice extends AfterAdvice { } 復制代碼

    目標接口 Target,有兩個方法,一個判斷 Advice 類型是否匹配,一個是工廠方法,創建對應類型的 Advice 對應的攔截器

    public interface AdvisorAdapter {boolean supportsAdvice(Advice var1);MethodInterceptor getInterceptor(Advisor var1); } 復制代碼

    三個適配器類 Adapter 分別如下,注意其中的 Advice、Adapter、Interceptor之間的對應關系

    class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {@Overridepublic boolean supportsAdvice(Advice advice) {return (advice instanceof MethodBeforeAdvice);}@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();return new MethodBeforeAdviceInterceptor(advice);} }@SuppressWarnings("serial") class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {@Overridepublic boolean supportsAdvice(Advice advice) {return (advice instanceof AfterReturningAdvice);}@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();return new AfterReturningAdviceInterceptor(advice);} }class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {@Overridepublic boolean supportsAdvice(Advice advice) {return (advice instanceof ThrowsAdvice);}@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {return new ThrowsAdviceInterceptor(advisor.getAdvice());} } 復制代碼

    客戶端 DefaultAdvisorAdapterRegistry

    public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {private final List<AdvisorAdapter> adapters = new ArrayList(3);public DefaultAdvisorAdapterRegistry() {// 這里注冊了適配器this.registerAdvisorAdapter(new MethodBeforeAdviceAdapter());this.registerAdvisorAdapter(new AfterReturningAdviceAdapter());this.registerAdvisorAdapter(new ThrowsAdviceAdapter());}public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList(3);Advice advice = advisor.getAdvice();if (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor)advice);}Iterator var4 = this.adapters.iterator();while(var4.hasNext()) {AdvisorAdapter adapter = (AdvisorAdapter)var4.next();if (adapter.supportsAdvice(advice)) { // 這里調用適配器方法interceptors.add(adapter.getInterceptor(advisor)); // 這里調用適配器方法}}if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());} else {return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[0]);}}// ...省略... } 復制代碼

    這里看 while 循環里,逐個取出注冊的適配器,調用 supportsAdvice() 方法來判斷 Advice 對應的類型,然后調用 getInterceptor() 創建對應類型的攔截器

    這里應該屬于對象適配器模式,關鍵字 instanceof 可看成是 Advice 的方法,不過這里的 Advice 對象是從外部傳進來,而不是成員屬性

    spring JPA中的適配器模式

    在Spring的ORM包中,對于JPA的支持也是采用了適配器模式,首先定義了一個接口的 JpaVendorAdapter,然后不同的持久層框架都實現此接口。

    jpaVendorAdapter:用于設置實現廠商JPA實現的特定屬性,如設置Hibernate的是否自動生成DDL的屬性generateDdl;這些屬性是廠商特定的,因此最好在這里設置;目前Spring提供 HibernateJpaVendorAdapter、OpenJpaVendorAdapter、EclipseLinkJpaVendorAdapter、TopLinkJpaVendorAdapter 四個實現。其中最重要的屬性是 database,用來指定使用的數據庫類型,從而能根據數據庫類型來決定比如如何將數據庫特定異常轉換為Spring的一致性異常,目前支持如下數據庫(DB2、DERBY、H2、HSQL、INFORMIX、MYSQL、ORACLE、POSTGRESQL、SQL_SERVER、SYBASE)

    public interface JpaVendorAdapter {// 返回一個具體的持久層提供者public abstract PersistenceProvider getPersistenceProvider();// 返回持久層提供者的包名public abstract String getPersistenceProviderRootPackage();// 返回持久層提供者的屬性public abstract Map<String, ?> getJpaPropertyMap();// 返回JpaDialectpublic abstract JpaDialect getJpaDialect();// 返回持久層管理器工廠public abstract Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface();// 返回持久層管理器public abstract Class<? extends EntityManager> getEntityManagerInterface();// 自定義回調方法public abstract void postProcessEntityManagerFactory(EntityManagerFactory paramEntityManagerFactory); } 復制代碼

    我們來看其中一個適配器實現類 HibernateJpaVendorAdapter

    public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {//設定持久層提供者private final PersistenceProvider persistenceProvider;//設定持久層方言private final JpaDialect jpaDialect;public HibernateJpaVendorAdapter() {this.persistenceProvider = new HibernatePersistence();this.jpaDialect = new HibernateJpaDialect();}//返回持久層方言public PersistenceProvider getPersistenceProvider() {return this.persistenceProvider;}//返回持久層提供者public String getPersistenceProviderRootPackage() {return "org.hibernate";}//返回JPA的屬性public Map<String, Object> getJpaPropertyMap() {Map jpaProperties = new HashMap();if (getDatabasePlatform() != null) {jpaProperties.put("hibernate.dialect", getDatabasePlatform());} else if (getDatabase() != null) {Class databaseDialectClass = determineDatabaseDialectClass(getDatabase());if (databaseDialectClass != null) {jpaProperties.put("hibernate.dialect",databaseDialectClass.getName());}}if (isGenerateDdl()) {jpaProperties.put("hibernate.hbm2ddl.auto", "update");}if (isShowSql()) {jpaProperties.put("hibernate.show_sql", "true");}return jpaProperties;}//設定數據庫protected Class determineDatabaseDialectClass(Database database) { switch (1.$SwitchMap$org$springframework$orm$jpa$vendor$Database[database.ordinal()]) { case 1: return DB2Dialect.class; case 2: return DerbyDialect.class; case 3: return H2Dialect.class; case 4: return HSQLDialect.class; case 5: return InformixDialect.class; case 6: return MySQLDialect.class; case 7: return Oracle9iDialect.class; case 8: return PostgreSQLDialect.class; case 9: return SQLServerDialect.class; case 10: return SybaseDialect.class; } return null; }//返回JPA方言public JpaDialect getJpaDialect() {return this.jpaDialect;}//返回JPA實體管理器工廠public Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface() {return HibernateEntityManagerFactory.class;}//返回JPA實體管理器public Class<? extends EntityManager> getEntityManagerInterface() {return HibernateEntityManager.class;} } 復制代碼

    配置文件中可以這樣指定

    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="generateDdl" value="false" /> <property name="database" value="HSQL"/> </bean> <bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/> 復制代碼

    spring MVC中的適配器模式

    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 {boolean supports(Object var1);ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;long getLastModified(HttpServletRequest var1, Object var2); } 復制代碼

    現該接口的適配器每一個 Controller 都有一個適配器與之對應,這樣的話,每自定義一個 Controller 需要定義一個實現 HandlerAdapter 的適配器。

    springmvc 中提供的 Controller 實現類有如下

    springmvc 中提供的 HandlerAdapter 實現類如下

    HttpRequestHandlerAdapter 這個適配器代碼如下

    public class HttpRequestHandlerAdapter implements HandlerAdapter {public HttpRequestHandlerAdapter() {}public boolean supports(Object handler) {return handler instanceof HttpRequestHandler;}public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {((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 {private List<HandlerAdapter> handlerAdapters;//初始化handlerAdaptersprivate 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;// Determine handler for the current request.mappedHandler = getHandler(processedRequest);// 確定當前請求的匹配的適配器.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());ha.getLastModified(request, mappedHandler.getHandler());mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}// ...省略... } 復制代碼

    通過適配器模式我們將所有的 controller 統一交給 HandlerAdapter 處理,免去了寫大量的 if-else 語句對 Controller 進行判斷,也更利于擴展新的 Controller 類型。

    參考:
    劉偉:設計模式Java版
    慕課網java設計模式精講 Debug 方式+內存分析
    孤落:Spring MVC中的適配器模式
    ToughMind_:深入淺出設計模式(五):7.適配器模式

    總結

    以上是生活随笔為你收集整理的设计模式 | 适配器模式及典型应用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。