适配器模式以及对应框架源代码的使用

前言

小编最近看了很多写设计模式的文章,发现网上已经很好的写出了一些关于设计模式的例子,也总结了对应模式的优缺点。但不知道各位有没有发现其实很少有文章写在框架或项目中的引用,是否和小编一样,看完了对应的设计模式不知道怎么应用到框架中,或者生搬硬套或者没有必要的使用。所以小编最近会在对应的例子上,侧重说明该模式在框架中的引用,并贴出相应的源代码。供各位学习,也请各位提出疑问,当然如果有错误之处也请各位指出。

定义

适配器模式:它是使原本接口不兼容的对象能够相互合作。下面画张图解释一下。
在这里插入图片描述
这边不知道小编说清楚没有,图也比较抽象。举个生活中的例子,比方说我们有3.5mm的耳机,但是我们的手机上只有type-c接口,那我们就用不来这个耳机了,但是你又type-c接口转3.5mm的转换器,那你就可以使用了,转换器就是我们软件中的适配器。(题外话:当然你也可以直接买个type-c接口的耳机,但这样成本比较大,对于软件也一样,所以引入了适配器。)

需求

这边小编举一个例子,在项目开发中也使用过,我们目前项目中使用的restful接口,很多传输的数据结构基本上是json格式,这时我们对接了一个外部项目,外部项目比较老旧,它需要xml格式传输,当然他给我们也是xml格式的,那我们这边就需要做转换,外部系统传入我们系统则从xml转到json,我们系统转入他们系统则从json转换成xml。那我们就做个适配器做这个转换功能。(题外话:当然可以使用工具类就可以实现了,相互转换的数据的工具类,但这个工具类肯定会有很多地方调用,同样也可以写个方法统一在数据传出和传入的地方调用工具类即可。不要太在意啊)
同样不可能贴出项目代码,而且如同题外话中一样,可能很多小伙伴认为并不需要。不过同样我先贴出代码。

接口从外部到内部 xml转json

public interface DataJsonConversion {
    /**
     * 数据转换
     *
     * @param msg 数据
     * @return 转换后数据
     */
    String dataTransferJson(String msg);
}

接口从内部到外部 json转xml

public interface DataXmlConversion {
    /**
     * 数据转换
     *
     * @param msg 数据
     * @return 转换后数据
     */
    String dataTransferXml(String msg);
}

双向数据转换适配器(这个有点像一个手机既可以被充电还可以像其他手机充电一样)

public class DataTypeConversion implements DataJsonConversion, DataXmlConversion {

    private DataJsonConversion jsonConversion;
    private DataXmlConversion xmlConversion;

    public DataTypeConversion(DataJsonConversion jsonConversion) {
        this.jsonConversion = jsonConversion;
    }

    public DataTypeConversion(DataXmlConversion xmlConversion) {
        this.xmlConversion = xmlConversion;
    }

    public DataTypeConversion(DataJsonConversion jsonConversion, DataXmlConversion xmlConversion) {
        this.jsonConversion = jsonConversion;
        this.xmlConversion = xmlConversion;
    }

    @Override
    public String dataTransferJson(String msg) {
        return jsonConversion.dataTransferJson(msg);
    }

    @Override
    public String dataTransferXml(String msg) {
        return xmlConversion.dataTransferXml(msg);
    }
}

测试代码

public class AdapterTest {
    @Test
    public void dataTypeTest(){
        DataJsonConversion jsonConversion = (msg)-> {
            System.out.println("转换成json格式了");
            return "json";
        };

        DataXmlConversion xmlConversion = (msg) ->{
            System.out.println("转换成xml格式了");
            return "xml";
        };

        DataJsonConversion dataTypeJson = new DataTypeConversion(jsonConversion);
        String xml = dataTypeJson.dataTransferJson("xml");
        Assert.assertEquals("json", xml);
        DataXmlConversion dataTypeXml = new DataTypeConversion(xmlConversion);
        String json = dataTypeXml.dataTransferXml("json");
        Assert.assertEquals("xml", json);

        DataTypeConversion dataTwoType = new DataTypeConversion(jsonConversion,xmlConversion);
        dataTwoType.dataTransferJson("xml");
        dataTwoType.dataTransferXml("json");
    }
}

测试结果

xml转换成json格式了
json转换成xml格式了

上面是双向适配。,可能小伙伴并不觉得比较有用。那下面我们讲讲适配器经典案例中框架代码中,从dubbo,tomcat和mybatis。

在源码框架中的使用

这边主要讲的日志框架的使用,我们只平常开发中会选择相应的日志框架如jcl,log4j,log4j2,logback,jul,slf4j等,那我们再引入第三方框架的时候,他们怎么知道我们使用的是什么日志框架,并且也能打印框架里面的日志信息。这里我们就引入了适配器模式,我们在自己的项目框架拓展也可以使用,废话不多说,上图上代码:
dubbo框架日志结构:
在这里插入图片描述

下面是最主要的代码以slf4j为例


/**
 * 日志输出器工厂
 * 
 * @author william.liangf
 */
public class LoggerFactory {

	private LoggerFactory() {
	}

	private static volatile LoggerAdapter LOGGER_ADAPTER;
	
	private static final ConcurrentMap<String, FailsafeLogger> LOGGERS = new ConcurrentHashMap<String, FailsafeLogger>();

	// 查找常用的日志框架
	static {
	    String logger = System.getProperty("dubbo.application.logger");
	    if ("slf4j".equals(logger)) {
    		setLoggerAdapter(new Slf4jLoggerAdapter());
    	} else if ("jcl".equals(logger)) {
    		setLoggerAdapter(new JclLoggerAdapter());
    	} else if ("log4j".equals(logger)) {
    		setLoggerAdapter(new Log4jLoggerAdapter());
    	} else if ("jdk".equals(logger)) {
    		setLoggerAdapter(new JdkLoggerAdapter());
    	} else {
    		try {
    			setLoggerAdapter(new Log4jLoggerAdapter());
            } catch (Throwable e1) {
                try {
                	setLoggerAdapter(new Slf4jLoggerAdapter());
                } catch (Throwable e2) {
                    try {
                    	setLoggerAdapter(new JclLoggerAdapter());
                    } catch (Throwable e3) {
                        setLoggerAdapter(new JdkLoggerAdapter());
                    }
                }
            }
    	}
	}
	
	/**
	 * 获取日志输出器
	 * 
	 * @param key
	 *            分类键
	 * @return 日志输出器, 后验条件: 不返回null.
	 */
	public static Logger getLogger(Class<?> key) {
		FailsafeLogger logger = LOGGERS.get(key.getName());
		if (logger == null) {
			LOGGERS.putIfAbsent(key.getName(), new FailsafeLogger(LOGGER_ADAPTER.getLogger(key)));
			logger = LOGGERS.get(key.getName());
		}
		return logger;
	}


slf4jAdapter

public class Slf4jLoggerAdapter implements LoggerAdapter {
    
	public Logger getLogger(String key) {
		return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(key));
	}

    public Logger getLogger(Class<?> key) {
        return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(key));
    }
}

真正slf4j的适配器

public class Slf4jLogger implements Logger, Serializable {

	private static final long serialVersionUID = 1L;

	private final org.slf4j.Logger logger;

	public Slf4jLogger(org.slf4j.Logger logger) {
		this.logger = logger;
	}

}

这边小编不知道为什么那个adapter的作用,可能dubbo的作者才清楚,我们只是知道了源代码后,对其作出疑问,其实可以不需要adapter。直接扩展。接下来还有mybatis和tomcat的日志框架留个大家去看一下,其实实现差不多,但是mybatis更加简洁并且代码比较好看。希望大家有这个兴趣去学习一下。

总结

该模式的主要优点如下:
客户端通过适配器可以透明地调用目标接口。
复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
在很多业务场景中符合开闭原则。

其缺点是:
适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

感谢以及参考

感谢源码阅读网,鲁班大叔的讲解及案例
本文借鉴了相关文章
url : http://c.biancheng.net/view/1361.html
`

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木兮君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值