设计模式 | 适配器模式

写在前面:

适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。

根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。

角色

Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

 

案例背景

我们国家的民用电都是 220V,日本是 110V,而我们的手机充电一般需要 5V,这时候要充电,就需要一个电压适配器,将 220V 或者 100V 的输入电压变换为 5V 输出。

定义输出电压的产品:

AC.java

public interface AC {
    int output();
}

产品接口产生中国和日本的两种子产品

ACChina.java

public class ACChina implements AC {

    @Override
    public int output() {
        return 220;
    }
}

ACJapan.java

public class ACJapan implements AC {
    @Override
    public int output() {
        return 110;
    }
}

由于适配器只能适应一种产品,也就是说 中国和日本的两种电压 需要两个适配器来解决,so,又产生了电压适配器产品。

ACAdapter.java

public interface ACAdapter {
    boolean support(AC ac); //是否支持此种电压适配

    int outputDC5V(AC ac); //适配操作
}
中国适配电压:ChinaACAdapter.java
public class ChinaACAdapter implements ACAdapter {
    @Override
    public boolean support(AC ac) {
        return ac.output() == 220;
    }

    @Override
    public int outputDC5V(AC ac) {
        return ac.output()/44;
    }
}

日本适配电压:JapanACAdapter.java

public class JapanACAdapter implements ACAdapter {
    @Override
    public boolean support(AC ac) {
        return ac.output() == 110;
    }

    @Override
    public int outputDC5V(AC ac) {
        return ac.output()/22;
    }
}

适配操作:

@Test
public void tt(){

       ACAdapter acAdapter = new ChinaACAdapter();
       ACChina acChina = new ACChina();
       if(acAdapter.support(acChina)){
           System.out.println(acAdapter.outputDC5V(acChina));
       }

}

success。

总结:

中国的电压使用中国的适配器,产生5V交流电。

 

接下来,结合Spring AOP,看看适配器模式的实际运用。

切面编程(也可叫通知)大致分为 前置通知(BeforeAdvice),后置通知(AfterAdvice),环绕通知(ThrowsAdvice)。

这些通知的最底层产品是Advice。我们着重介绍介绍前置通知,其他的原理相同。

Advice.java

public interface Advice {
}

BeforeAdvice.java

public interface BeforeAdvice extends Advice {
}

MethodBeforeAdvice.java

public interface MethodBeforeAdvice extends BeforeAdvice {
    void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable;
}

注意:在MethodBeforeAdvice里面就定义了前置通知的方法及参数,需要利用反射区调用。

 

接下来就到了 MethodBeforeAdvice的实现类也就是前置通知的具体操作了。

AspectJMethodBeforeAdvice.java
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
    public AspectJMethodBeforeAdvice(Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }

    public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
        this.invokeAdviceMethod(this.getJoinPointMatch(), (Object)null, (Throwable)null);
    }

    public boolean isBeforeAdvice() {
        return true;
    }

    public boolean isAfterAdvice() {
        return false;
    }
}

## 不知道你们注意到没有,他的判断方法包括 isBeforeAdvice 和 isAfterAdvice。

再然后,就是前置通知适配器了,因为要有一个适配器去适配拦截,然后再方法执行前,做一个通知。

MethodBeforeAdviceInterceptor.java
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
    private final MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        return mi.proceed();
    }
}

没错,就是一个拦截器,自定义注解了解一波,一般的注解都要利用反射+拦截器或者过滤器去做。可见,spring的做法是拦截器。当拦截到方法的上面有@before的时候,自然会调用我们这个前置通知适配器。他们帮我们做一些前置通知,以及目标方法的invoke()。然后返回结果。所以我理解的spring aop 就是 动态代理技术的使用。而动态代理的使用依靠的就是 反射与适配器模式,这里面说的可能过于绝对。但是,你懂的 就好啦。日后再聊。

你可能感兴趣的:(aop,spring,适配器模式,动态代理,反射,设计模式,aop)