GENGEN
主页
vuepress
  • GIT命令
  • python+django
  • vue cli搭建项目
  • babel es6转换es5
  • docker aliyun配置
  • npm 配置
  • linux 常用命令
  • Ubuntu 下Linux 命令
  • github
  • gitee
  • csdn
  • 关于我
主页
vuepress
  • GIT命令
  • python+django
  • vue cli搭建项目
  • babel es6转换es5
  • docker aliyun配置
  • npm 配置
  • linux 常用命令
  • Ubuntu 下Linux 命令
  • github
  • gitee
  • csdn
  • 关于我
  • java基础

    • JDK8 函数式编程
    • JDK8 新特性之Date-Time
    • Servlet 源码分析
    • ArrayList 源码
    • LinkedList 源码
    • HashMap 源码
    • String 源码
    • BigDecimal 源码
    • java 类的加载
    • Class 源码
    • Synchronized锁升级
    • 事务的传播机制
    • knowledge
  • JAVA WEB

    • Java Servlet
    • 权限设计
    • logback日志的链路追踪
  • DATABASE

    • MySQL EXPLAIN详解
    • MySQL 索引
    • MySQL 表锁、行锁
    • MySQL ACID与transcation
    • 分布式事务
    • MySQL MVCC机制
    • Mysql 乐观锁与悲观锁
    • 分布式锁1 数据库分布式锁
    • 分布式锁2 Redis分布式锁
    • 分布式锁3 ZK分布式锁
  • SpringCloud

    • SpringCloud服务注册中心之Eureka
    • SpringCloud服务注册中心之Zookeeper
    • SpringCloud服务调用之Ribbon
    • SpringCloud服务调用之OpenFeign
    • SpringCloud服务降级之Hystrix
    • SpringCloud服务网关之Gateway
    • SpringCloud Config分布式配置中心
    • SpringCloud服务总线之Bus
    • SpringCloud消息驱动之Stream
    • SpringCloud链路追踪之Sleuth
    • SpringCloud Alibaba Nacos
    • SpringCloud Alibaba Sentinel
  • Spring

    • SpringBoot
    • Spring-data-jpa入门
    • SpringCloud问题
    • dispatcherServlet 源码分析
    • @SpringBootApplication注解内部实现与原理
    • spring启动初始化初始化
  • 中间件

    • 分布式协调服务器Zookeeper
    • 服务治理Dubbo
    • 分布式配置管理平台Apollo
    • 消息中间件框架Kafka
    • 分布式调度平台ElasticJob
    • 可视化分析工具Kibana
    • ElacticSearch 基础
    • ElacticSearch进阶
    • ElacticSearch集成
  • 环境部署

    • 应用容器引擎Docker
    • DockerCompose服务编排
    • 负载均衡Nginx
    • Nginx的安装配置
    • K8S基础
  • 代码片段

    • listener 监听模式
    • spingboot 整合redis
    • XSS过滤
    • profile的使用
    • ConfigurationProperties注解
  • 设计模式

    • 工厂模式
    • 单例模式
    • 装饰者模式
    • 适配器模式
    • 模板方法模式
    • 观察者模式
  • 读书笔记

    • 《Spring in Action 4》 读书笔记
    • 《高性能mysql》 读书笔记
  • NoSQL

    • Redis基础
    • Redis高级
    • Redis集群
    • Redis应用
  • MQ

    • rabbitMQ基础
    • rabbitMQ高级
    • rabbitMQ集群
  • JVM

    • JVM体系架构概述
    • 堆参数调整
    • GC 分代收集算法
    • JVM 垃圾回收器
    • JVM 相关问题
  • JUC

    • JUC总览
    • volatile关键字
    • CAS
    • ABA问题
    • collections包下线程安全的集合类
    • Lock 锁
    • LockSupport
    • AQS
    • Fork/Join分支框架
    • JUC tools
    • BlockingQueue 阻塞队列
    • Executor 线程池
    • CompletableFuture
    • 死锁以及问题定位分析
  • Shell

    • shell命令
    • shell基础
  • Activiti

    • IDEA下的Activiti HelloWord
    • 流程定义的CRUD
    • 流程实例的执行
    • 流程变量
  • VUE

    • vue基础
    • vue router
    • Vuex
    • Axios 跨域
    • dialog 弹出框使用
    • vue 动态刷新页面
    • vue 封装分页组件
    • vue 动态菜单
    • vue 常用传值
  • Solidity 智能合约

    • Solidity 基础
    • Solidity ERC-20
    • Solidity 101
  • English

    • 时态

设计模式之适配器模式——Adapter

定义

适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作。

适用场景

  • 您想使用现有的类,但它的接口与您需要的接口不匹配
  • 你想创建一个可重用的类,让它与不相关或不可预见的类(即不一定具有兼容接口的类)协作
  • 你需要使用几个现有的子类,但通过对每个子类进行子类化来调整它们的接口是不切实际的。对象适配器可以调整其父类的接口。
  • 大多数使用第三方库的应用程序使用适配器作为应用程序和第三方库之间的中间层,以将应用程序与库分离。如果必须使用另一个库,则只需要新库的适配器,而无需更改应用程序代码。

组件

  • 目标角色(Target):客户端所期待得到的接口
  • 适配器角色(Adapter):适配器类是本模式的核心。适配器把源接口转换成目标接口。(这一角色不可以是接口,而必须是具体类)。
  • 源角色被适配者(Adapee):现在需要被适配的接口

优点

  • 一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
  • 符合设计原则:多用合成/聚合、少用继承,从而减少类之间的耦合

缺点

  • 要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源
  • 需要额外的引用来间接得到Adaptee。

构造场景

假设你有一台电脑,能接收5V电压。我国电压220V,日本电压110V,需要适配器支持电脑在两个国家都能用。

  • Adapee
public interface PowerAdaptee {
    /**
     * 输出电压
     * @return
     */
    int output();
}

//不同电压实现
//中国
public class ChinaPowerAdaptee implements PowerAdaptee{
    private int output = 220;

    @Override
    public int output() {
        System.out.println("电源输出电压:" + output);
        return output;
    }
}

//日本
public class JapanPowerAdaptee implements PowerAdaptee {
    private int output = 110;

    @Override
    public int output() {
        System.out.println("电源输出电压:" + output);
        return output;
    }
}
  • Target
public interface ComputerTarget {
    /**
     * 220v输出5V电压
     * @return
     */
    int output5V();

}
  • Adapter
public class PowerAdapter implements ComputerTarget{

    PowerAdaptee powerAdaptee;

    public PowerAdapter(PowerAdaptee powerAdaptee){
        this.powerAdaptee = powerAdaptee;
    }

    @Override
    public int output5V() {
        return transferOutPut(powerAdaptee);
    }

    /**转换方法*/
    private int transferOutPut(PowerAdaptee adaptee){
        int v ;
        if (adaptee instanceof ChinaPowerAdaptee){
            v = adaptee.output() / 44;
            System.out.println("中国220V电压转化为"+v);
        }else if (adaptee instanceof JapanPowerAdaptee){
            v = adaptee.output() / 22;
            System.out.println("日本110V电压转化为"+v);
        }else {
            throw new IllegalArgumentException("未找到对应适配器");
        }
        return v;
    }

}
  • 测试方法
public class AdapterMain {
    public static void main(String[] args) {
        ComputerTarget chinaTarget = new PowerAdapter(new ChinaPowerAdaptee()) ;
        ComputerTarget japanTarget = new PowerAdapter(new JapanPowerAdaptee()) ;
        chinaTarget.output5V();
        japanTarget.output5V();
    }
}
/**输出:
电源输出电压:220
中国220V电压转化为5
电源输出电压:110
日本110V电压转化为5
*/

例子中看出,客户端所期待的接口Target需要适配器Adapter实现。Adapter中实现转化5V的方法。 接口ComputerTarget中是无参的,PowerAdapter通过构造器把被适配者Adapee注入进去,从而进行转化。

适配器模式在spring AOP中的运用

  • 在spring AOP中,使用Advice(通知)来增强被代理类的功能。

  • Advice的类型有MethodBeforeAdvice、AfterReturningAdvice 、ThrowsAdvice

  • 每个Advice实现都有对应的拦截器MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor 、ThrowsAdviceInterceptor

  • spring需要将每个 Advice封装成对应的拦截器类型,返回给容器。

结合之前的例子,Advice实现类就是对应的Adapee(被适配者),而拦截器就是目标角色Target。把Advice转换成对应的拦截器,就需要用适配器Adapter进行转换。

三个Adaptee如下

//BeforeAdvice 继承 Advice
public interface MethodBeforeAdvice extends BeforeAdvice {

	void before(Method method, Object[] args, Object target) throws Throwable;

}
//AfterAdvice 继承 Advice
public interface AfterReturningAdvice extends AfterAdvice {

	void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;

}

public interface ThrowsAdvice extends AfterAdvice {

}

目标接口 Target ,有两个方法,一个判断 Advice 类型是否匹配,一个是工厂方法,创建对应类型的 Advice 对应的拦截器

public interface AdvisorAdapter {
    boolean supportsAdvice(Advice var1);
    //真正需要的方法是获取对应的拦截器
    MethodInterceptor getInterceptor(Advisor var1);
}

三个Adapter如下:

  • MethodBeforeAdviceAdapter
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);
	}

}
  • AfterReturningAdviceAdapter
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);
	}

}
  • ThrowsAdviceAdapter
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<AdvisorAdapter>(3);


	/**
	 * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
	 */
	public DefaultAdvisorAdapterRegistry() {
		registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}


	@Override
	public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
		if (adviceObject instanceof Advisor) {
			return (Advisor) adviceObject;
		}
		if (!(adviceObject instanceof Advice)) {
			throw new UnknownAdviceTypeException(adviceObject);
		}
		Advice advice = (Advice) adviceObject;
		if (advice instanceof MethodInterceptor) {
			// So well-known it doesn't even need an adapter.
			return new DefaultPointcutAdvisor(advice);
		}
		for (AdvisorAdapter adapter : this.adapters) {
			// Check that it is supported.
			if (adapter.supportsAdvice(advice)) {
				return new DefaultPointcutAdvisor(advice);
			}
		}
		throw new UnknownAdviceTypeException(advice);
	}

	@Override
	public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
		List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
		Advice advice = advisor.getAdvice();
		if (advice instanceof MethodInterceptor) {
			interceptors.add((MethodInterceptor) advice);
		}
		for (AdvisorAdapter adapter : this.adapters) {
			if (adapter.supportsAdvice(advice)) { // 这里调用适配器方法是否属于此`Advice`
				interceptors.add(adapter.getInterceptor(advisor)); // 这里调用适配器方法转换对应拦截器
			}
		}
		if (interceptors.isEmpty()) {
			throw new UnknownAdviceTypeException(advisor.getAdvice());
		}
		return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
	}

	@Override
	public void registerAdvisorAdapter(AdvisorAdapter adapter) {
		this.adapters.add(adapter);
	}

}

getInterceptors的forEach中,调用 supportsAdvice() 方法来判断 Advice 对应的类型,然后调用 getInterceptor() 创建对应类型的拦截器。

Last Updated:
Contributors: 88395515, lidagen
Prev
装饰者模式
Next
模板方法模式