適配器模式
適配器模式(Adapter Pattern):將一個接口轉換成客戶但願的另外一個接口,使接口不兼容的那些類能夠一塊兒工做,其別名爲包裝器(Wrapper)。適配器模式既能夠做爲類結構型模式,也能夠做爲對象結構型模式。css
在適配器模式中,咱們經過增長一個新的適配器類來解決接口不兼容的問題,使得本來沒有任何關係的類能夠協同工做。java
根據適配器類與適配者類的關係不一樣,適配器模式可分爲對象適配器和類適配器兩種,在對象適配器模式中,適配器與適配者之間是關聯關係;在類適配器模式中,適配器與適配者之間是繼承(或實現)關係。spring
角色
Target(目標抽象類):目標抽象類定義客戶所需接口,能夠是一個抽象類或接口,也能夠是具體類。sql
Adapter(適配器類):適配器能夠調用另外一個接口,做爲一個轉換器,對Adaptee和Target進行適配,適配器類是適配器模式的核心,在對象適配器中,它經過繼承Target並關聯一個Adaptee對象使兩者產生聯繫。數據庫
Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經存在的接口,這個接口須要適配,適配者類通常是一個具體類,包含了客戶但願使用的業務方法,在某些狀況下可能沒有適配者類的源代碼。設計模式
缺省適配器模式(Default Adapter Pattern):當不須要實現一個接口所提供的全部方法時,可先設計一個抽象類實現該接口,併爲接口中每一個方法提供一個默認實現(空方法),那麼該抽象類的子類能夠選擇性地覆蓋父類的某些方法來實現需求,它適用於不想使用一個接口中的全部方法的狀況,又稱爲單接口適配器模式。缺省適配器模式是適配器模式的一種變體,其應用也較爲普遍。在JDK類庫的事件處理包java.awt.event中普遍使用了缺省適配器模式,如WindowAdapter、KeyAdapter、MouseAdapter等。微信
示例
類適配器
首先有一個已存在的將被適配的類mvc
public class Adaptee {
public void adapteeRequest() {
System.out.println("被適配者的方法");
}
}
定義一個目標接口app
public interface Target {
void request();
}
怎麼才能夠在目標接口中的 request()
調用 Adaptee
的 adapteeRequest()
方法呢?框架
若是直接實現 Target
是不行的
public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("concreteTarget目標方法");
}
}
若是經過一個適配器類,實現 Target
接口,同時繼承了 Adaptee
類,而後在實現的 request()
方法中調用父類的 adapteeRequest()
便可實現
public class Adapter extends Adaptee implements Target{
@Override
public 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();
@Override
public 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;
@Override
public int outputAC() {
return output;
}
}
public class AC220 implements AC {
public final int output = 220;
@Override
public 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;
@Override
public boolean support(AC ac) {
return (voltage == ac.outputAC());
}
@Override
public 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;
@Override
public boolean support(AC ac) {
return (voltage == ac.outputAC());
}
@Override
public 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);
// 去日本旅遊,電壓是 110V
AC 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 {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
@SuppressWarnings("serial")
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof AfterReturningAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
}
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof ThrowsAdvice);
}
@Override
public 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的一致性異常,目前支持以下數據庫(DB二、DERBY、H二、HSQL、INFORMIX、MYSQL、ORACLE、POSTGRESQL、SQL_SERVER、SYBASE)
public interface JpaVendorAdapter
{
// 返回一個具體的持久層提供者
public abstract PersistenceProvider getPersistenceProvider();
// 返回持久層提供者的包名
public abstract String getPersistenceProviderRootPackage();
// 返回持久層提供者的屬性
public abstract Map<String, ?> getJpaPropertyMap();
// 返回JpaDialect
public 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;
//初始化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;
// 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.適配器模式
推薦閱讀
設計模式 | 簡單工廠模式及典型應用
設計模式 | 工廠方法模式及典型應用
設計模式 | 抽象工廠模式及典型應用
設計模式 | 建造者模式及典型應用
設計模式 | 原型模式及典型應用
設計模式 | 外觀模式及典型應用
設計模式 | 裝飾者模式及典型應用
點擊[閱讀原文]可訪問個人我的博客:http://laijianfeng.org
本文分享自微信公衆號 - 小旋鋒(whirlysBigData)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。