设计模式之适配器模式与桥接模式详解和应用

Posted 赵广陆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之适配器模式与桥接模式详解和应用相关的知识,希望对你有一定的参考价值。

目录


1 适配器模式

1.1 定义

适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。

适配器模式的英文翻译是 Adapter Design Pattern。顾名思义,这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。

示意图

**生活场景:**电源插转换头、手机充电转换头、显示器转接头。

你可以创建一个适配器。 这是一个特殊的对象, 能够转换对象接口, 使其能与其他对象进行交互

适配器模式通过封装对象将复杂的转换过程藏于幕后。 被封装的对象甚至察觉不到适配器的存在。

适配器不仅可以转换不同格式的数据, 其还有助于采用不同接口的对象之间的合作。 它的运作方式如下:

  1. 适配器实现与其中一个现有对象兼容的接口。
  2. 现有对象可以使用该接口安全地调用适配器方法。
  3. 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。

有时你甚至可以创建一个双向适配器来实现双向转换调用。

1.2 应用场景

  1. 封装有缺陷的接口设计
  2. 统一多个类的接口设计
  3. 替换依赖的外部系统
  4. 兼容老版本接口
  5. 适配不同格式的数据

1.3 适配器角色

Adaptee 是一组不兼容 ITarget 接口定义的接口。

ITarget 表示要转化成的接口定义。

Adapter/Adaptor 将 Adaptee 转化成一组符合 ITarget 接口定义的接口。

适配器有3中形式:类适配器、对象适配器、接口适配器

1.4 类适配器

类适配器: 基于继承。

做法:让Adaptor实现ITarget接口,并且继承Adaptee,这样Adaptor就具备ITarget和Adaptee的特性,就可以将两者进行转化。

UML类图:

// 类适配器: 基于继承

// ITarget 表示要转化成的接口定义
public interface ITarget 
  void f1();
  void f2();
  void fc();


// Adaptee 是一组不兼容 ITarget 接口定义的接口
public class Adaptee 
  public void fa()  //... 
  public void fb()  //... 
  public void fc()  //... 


// Adaptor 将 Adaptee 转化成一组符合 ITarget 接口定义的接口      
public class Adaptor extends Adaptee implements ITarget 
  public void f1() 
    super.fa();
  
  
  public void f2() 
    //...重新实现f2()...
  
  
  // 这里fc()不需要实现,直接继承自Adaptee,这是跟对象适配器最大的不同点

复制

1.5 对象适配器

对象适配器:基于组合。

做法:让Adaptor 实现ITarget 接口,然后内部持有Adaptee实例,然后在ITarget接口规定的方法内转换Adaptee。

URL类图:

// 对象适配器:基于组合

// ITarget 表示要转化成的接口定义
public interface ITarget 
  void f1();
  void f2();
  void fc();


// Adaptee 是一组不兼容 ITarget 接口定义的接口
public class Adaptee 
  public void fa()  //... 
  public void fb()  //... 
  public void fc()  //... 


// Adaptor 将 Adaptee 转化成一组符合 ITarget 接口定义的接口            
public class Adaptor implements ITarget 
  private Adaptee adaptee;
  
  public Adaptor(Adaptee adaptee) 
    this.adaptee = adaptee;
  
  
  public void f1() 
    adaptee.fa(); //委托给Adaptee
  
  
  public void f2() 
    //...重新实现f2()...
  
  
  public void fc() 
    adaptee.fc();
  

复制

1.5 接口适配器

使用接口适配器让我们只实现我们所需要的接口方法

public class CD  //这个类来自外部sdk,我们无权修改它的代码
  //...
  public static void staticFunction1()  //... 
  
  public void uglyNamingFunction2()  //... 

  public void tooManyParamsFunction3(int paramA, int paramB, ...)  //... 
  
  public void lowPerformanceFunction4()  //... 


// 使用适配器模式进行重构
public class ITarget 
  void function1();
  void function2();
  void fucntion3(ParamsWrapperDefinition paramsWrapper);
  void function4();
  //...

// 注意:适配器类的命名不一定非得末尾带Adaptor
public class CDAdaptor extends CD implements ITarget 
  //...
  public void function1() 
     super.staticFunction1();
  
  
  public void function2() 
    super.uglyNamingFucntion2();
  
  
  public void function3(ParamsWrapperDefinition paramsWrapper) 
     super.tooManyParamsFunction3(paramsWrapper.getParamA(), ...);
  
  
  public void function4() 
    //...reimplement it...
  

1.6 实战

重构第三方登录自由适配场景

首先创建统一登陆结果ResultMsg类:

@Data
public class ResultMsg 

    private int code;
    private String msg;
    private Object data;

    public ResultMsg(int code, String msg, Object data) 
        this.code = code;
        this.msg = msg;
        this.data = data;
    

假设老系统的的登录逻辑PasswordService:

public class PassportService 

    /**
     * 注册方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg regist(String username,String password)
        return  new ResultMsg(200,"注册成功",new Member());
    

    /**
     * 登录的方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username,String password)
        return null;
    

遵循开闭原则,老代码我们不修改。开启代码重构之路,创建Member类:

@Data
public class Member 

    private String username;
    private String password;
    private String mid;
    private String info;

运行稳定的代码不改动。创建ITarget角色IPassportForThird接口。

public interface IPassportForThird 

    ResultMsg loginForQQ(String openId);

    ResultMsg loginForWechat(String openId);

    ResultMsg loginForToken(String token);

    ResultMsg loginForTelphone(String phone, String code);

创建适配器兼容Adaptor角色PassportForThirdAdapter类

public class PassportForThirdAdapter implements IPassportForThird 

    public ResultMsg loginForQQ(String openId) 
        return processLogin(openId, LoginForQQAdapter.class);
    

    public ResultMsg loginForWechat(String openId) 
        return processLogin(openId, LoginForWechatAdapter.class);
    

    public ResultMsg loginForToken(String token) 
        return processLogin(token, LoginForTokenAdapter.class);
    

    public ResultMsg loginForTelphone(String phone, String code) 
        return processLogin(phone, LoginForTelAdapter.class);
    

    private ResultMsg processLogin(String id,Class<? extends ILoginAdapter> clazz)
        try 
            ILoginAdapter adapter = clazz.newInstance();
            if (adapter.support(adapter))
                return adapter.login(id,adapter);
            
         catch (Exception e) 
            e.printStackTrace();
        
        return null;
    

根据不同登录方式,创建不同登录Adaptor。首先,创建LoginAdapter接口:

public interface ILoginAdapter 
    boolean support(Object object);
    ResultMsg login(String id,Object adapter);

创建一个抽象类AbstraceAdapter继承PassportService原有功能,同时实现ILoginAdapter接口,然后分别实现不同的登录适配。

QQ登录

public class LoginForQQAdapter extends AbstraceAdapter
    public boolean support(Object adapter) 
        return adapter instanceof LoginForQQAdapter;
    

    public ResultMsg login(String id, Object adapter) 
        if(!support(adapter))return null;
        //accesseToken
        //time
        return super.loginForRegist(id,null);
    

微信登录

public class LoginForWechatAdapter extends AbstraceAdapter
    public boolean support(Object adapter) 
        return adapter instanceof LoginForWechatAdapter;
    

    public ResultMsg login(String id, Object adapter) 
        return super.loginForRegist(id,null);
    

手机登录

public class LoginForTelAdapter extends AbstraceAdapter
    public boolean support(Object adapter) 
        return adapter instanceof LoginForTelAdapter;
    

    public ResultMsg login(String id, Object adapter) 
        return super.loginForRegist(id,null);
    

Token登录

public class LoginForTokenAdapter extends AbstraceAdapter 
    public boolean support(Object adapter) 
        return adapter instanceof LoginForTokenAdapter;
    

    public ResultMsg login(String id, Object adapter) 
        return super.loginForRegist(id,null);
    

创建适配器PassportForThirdAdapter类,实现目标接口IPassportForThird兼容。

public class PassportForThirdAdapter implements IPassportForThird 

    public ResultMsg loginForQQ(String openId) 
        return processLogin(openId, LoginForQQAdapter.class);
    

    public ResultMsg loginForWechat(String openId) 
        return processLogin(openId, LoginForWechatAdapter.class);
    

    public ResultMsg loginForToken(String token) 
        return processLogin(token, LoginForTokenAdapter.class);
    

    public ResultMsg loginForTelphone(String phone, String code) 
        return processLogin(phone, LoginForTelAdapter.class);
    

    private ResultMsg processLogin(String id,Class<? extends ILoginAdapter> clazz)
        try 
            ILoginAdapter adapter = clazz.newInstance();
            if (adapter.support(adapter))
                return adapter.login(id,adapter);
            
         catch (Exception e) 
            e.printStackTrace();
        
        return null;
    

客户端测试代码

public class Test 
    public static void main(String[] args) 
        IPassportForThird adapter = new PassportForThirdAdapter();
        adapter.loginForQQ("sdfasdfasfasfas");
    

类图

我们为每一个适配器加上了support()方法,用来判断是否兼容,参数是Object,来源于接口。ILoginAdapter接口是为了代码规范。上面的代码综合了策略模式、简单工厂和适配器模式。

1.7 源码

Spring AOP中的AdvisorAdapter。有三个实现类MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter。

顶层接口AdvisorAdapter类

public interface AdvisorAdapter 
    boolean supportsAdvice(Advice var1);

    MethodInterceptor getInterceptor(Advisor var1);

MethodBeforeAdviceAdapter实现。其余两个不贴了。

class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable 
    AfterReturningAdviceAdapter() 
    

    public boolean supportsAdvice(Advice advice) 
        return advice instanceof AfterReturningAdvice;
    

    public MethodInterceptor getInterceptor(Advisor advisor) 
        AfterReturningAdvice advice = (AfterReturningAdvice)advisor.getAdvice();
        return new AfterReturningAdviceInterceptor(advice);
    

Spring 根据不同的AOP配置确定使用相应的Advice。

下面看SpringMVC的HandlerAdapter类,它也有多个子类。

类图:

他的适配关键代码在DispatcherServlet的doDispatch()方法中,我们看源码:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception 
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try 
        try 
            ModelAndView mv = null;
            Object dispatchException = null;

            try 
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                mappedHandler = this.getHandler(processedRequest);
                if (mappedHandler == null) 
                    this.noHandlerFound(processedRequest, response);
                    return;
                

                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) 
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (this.logger.isDebugEnabled()) 
                        this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    

                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) 
                        return;
                    
                

                if (!mappedHandler.applyPreHandle(processedRequest, response)) 
                    return;
                

                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) 
                    return;
                

                this.applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
             catch (Exception var20) 
                dispatchException = var20;
             catch (Throwable var21) 
                dispatchException = new NestedServletException("Handler dispatch failed", var21);
            

            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
         catch (Exception var22) 
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
         catch (Throwable var23) 
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
        

     finally 
        if (asyncManager.isConcurrentHandlingStarted()) 
            if (mappedHandler != null) 
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            
         else if (multipartRequestParsed) 
            this.cleanupMultipart(processedRequest);
        

    

doDispatch方法调用了getHandlerAdaper方法

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception 
    if (this.handlerMappings != null) 
        Iterator var2 =

以上是关于设计模式之适配器模式与桥接模式详解和应用的主要内容,如果未能解决你的问题,请参考以下文章

GoF 23 种设计模式之适配器模式和桥接模式

GoF 23 种设计模式之适配器模式和桥接模式

Java设计模式之四 ----- 适配器模式和桥接模式

Java进阶篇设计模式之四 -----适配器模式和桥接模式

什么时候使用桥接模式?它与适配器模式有何不同?

Java设计模式之五大创建型模式(附实例和详解)