Adaptor
适配器模式,顾名思义,将原来不兼容的接口转为可兼容的接口。
参考USB转接口,某旧系统有一组API(typec接口),没办法用在新的业务中,通过适配器模式(USB转接口),转为新业务可以兼容使用的API
两种适配器的实现思路:类适配器、对象适配器,一个基于继承,一个基于组合
// 类适配器: 基于继承
public interface ITarget {
void f1();
void f2();
void fc();
}
public class Adaptee {
public void fa() { //... }
public void fb() { //... }
public void fc() { //... }
}
public class Adaptor extends Adaptee implements ITarget {
public void f1() {
super.fa();
}
public void f2() {
//...重新实现f2()...
}
// 这里fc()不需要实现,直接继承自Adaptee,这是跟对象适配器最大的不同点
}
// 对象适配器:基于组合
public interface ITarget {
void f1();
void f2();
void fc();
}
public class Adaptee {
public void fa() { //... }
public void fb() { //... }
public void fc() { //... }
}
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();
}
}
- 类适配器利用了继承的优势,可以复用部分代码
- 对象适配器因为使用了组合,灵活性上更好
比如业务中需要加入敏感词过滤功能,并且希望使用多个这样的外部系统以提高过滤能力,但由于这些系统接口不统一,如果用最简单的过程式写法,当后序有新的敏感词系统加入时,可能会修改到旧代码,增加了出错风险,维护成本变大
public class ASensitiveWordsFilter { // A敏感词过滤系统提供的接口
//text是原始文本,函数输出用***替换敏感词之后的文本
public String filterSexyWords(String text) {
// ...
}
public String filterPoliticalWords(String text) {
// ...
}
}
public class BSensitiveWordsFilter { // B敏感词过滤系统提供的接口
public String filter(String text) {
//...
}
}
public class CSensitiveWordsFilter { // C敏感词过滤系统提供的接口
public String filter(String text, String mask) {
//...
}
}
// 未使用适配器模式之前的代码:代码的可测试性、扩展性不好
public class RiskManagement {
private ASensitiveWordsFilter aFilter = new ASensitiveWordsFilter();
private BSensitiveWordsFilter bFilter = new BSensitiveWordsFilter();
private CSensitiveWordsFilter cFilter = new CSensitiveWordsFilter();
public String filterSensitiveWords(String text) {
String maskedText = aFilter.filterSexyWords(text);
maskedText = aFilter.filterPoliticalWords(maskedText);
maskedText = bFilter.filter(maskedText);
maskedText = cFilter.filter(maskedText, "***");
return maskedText;
}
}
// 使用适配器模式进行改造
public interface ISensitiveWordsFilter { // 统一接口定义
String filter(String text);
}
public class ASensitiveWordsFilterAdaptor implements ISensitiveWordsFilter {
private ASensitiveWordsFilter aFilter;
public String filter(String text) {
String maskedText = aFilter.filterSexyWords(text);
maskedText = aFilter.filterPoliticalWords(maskedText);
return maskedText;
}
}
//...省略BSensitiveWordsFilterAdaptor、CSensitiveWordsFilterAdaptor...
// 扩展性更好,更加符合开闭原则,如果添加一个新的敏感词过滤系统,
// 这个类完全不需要改动;而且基于接口而非实现编程,代码的可测试性更好。
public class RiskManagement {
private List<ISensitiveWordsFilter> filters = new ArrayList<>();
public void addSensitiveWordsFilter(ISensitiveWordsFilter filter) {
filters.add(filter);
}
public String filterSensitiveWords(String text) {
String maskedText = text;
for (ISensitiveWordsFilter filter : filters) {
maskedText = filter.filter(maskedText);
}
return maskedText;
}
}
比如有个外部的系统设计的不好,有大量静态方法,测试性不友好,可以通过适配器转成非静态方法
public class CD { //这个类来自外部sdk,我们无权修改它的代码
//...
public static void staticFunction1() { //... }
public void uglyNamingFunction2() { //... }
public void tooManyParamsFunction3(int paramA, int paramB, ...) { //... }
public void lowPerformanceFunction4() { //... }
}
// 使用适配器模式进行重构
public interface 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...
}
}
当项目中依赖一个外部系统A,后序要替换成一个相同功能的系统B时,由于A、B接口不兼容,直接替换的话,可能需要修改大量的项目代码。可以用适配器模式,在保证原项目使用外部系统API不变的情况下,替换为B
// 外部系统A
public interface IA {
//...
void fa();
}
public class A implements IA {
//...
public void fa() { //... }
}
// 在我们的项目中,外部系统A的使用示例
public class Demo {
private IA a;
public Demo(IA a) {
this.a = a;
}
//...
}
Demo d = new Demo(new A());
// 将外部系统A替换成外部系统B
public class BAdaptor implemnts IA {
private B b;
public BAdaptor(B b) {
this.b= b;
}
public void fa() {
//...
b.fb();
}
}
// 借助BAdaptor,Demo的代码中,调用IA接口的地方都无需改动,
// 只需要将BAdaptor如下注入到Demo即可。
Demo d = new Demo(new BAdaptor(new B()));
Java日志系统有很多比如log4j等,但没有统一的接口,直到Slf4j出现
Slf4j是一个日志框架,定义了一套日志功能常用到的API接口定义,并没有具体实现。希望日志系统遵循该日志框架,这样业务系统都基于该框架使用日志功能,无需关心具体日志功能实现,也可以轻松替换其他的具体日志模块
Slf4j框架中除了核心的APOI接口定义为,还有适配器的应用。因为在Slf4j出现之前已经有其他日志系统了,其他日志系统的开发者可能并没有经历将日志系统进行升级为符合Slf4j的规范,而且,即使愿意升级,使用其他日志系统的业务项目也未见的愿意更新。为了解决以上问题,Slf4j运用适配器模式,加入了其他日志系统API到Slf4j转换的适配器
// slf4j统一的接口定义
package org.slf4j;
public interface Logger {
public boolean isTraceEnabled();
public void trace(String msg);
public void trace(String format, Object arg);
public void trace(String format, Object arg1, Object arg2);
public void trace(String format, Object[] argArray);
public void trace(String msg, Throwable t);
public boolean isDebugEnabled();
public void debug(String msg);
public void debug(String format, Object arg);
public void debug(String format, Object arg1, Object arg2)
public void debug(String format, Object[] argArray)
public void debug(String msg, Throwable t);
//...省略info、warn、error等一堆接口
}
// log4j日志框架的适配器
// Log4jLoggerAdapter实现了LocationAwareLogger接口,
// 其中LocationAwareLogger继承自Logger接口,
// 也就相当于Log4jLoggerAdapter实现了Logger接口。
package org.slf4j.impl;
public final class Log4jLoggerAdapter extends MarkerIgnoringBase
implements LocationAwareLogger, Serializable {
final transient org.apache.log4j.Logger logger; // log4j
public boolean isDebugEnabled() {
return logger.isDebugEnabled();
}
public void debug(String msg) {
logger.log(FQCN, Level.DEBUG, msg, null);
}
public void debug(String format, Object arg) {
if (logger.isDebugEnabled()) {
FormattingTuple ft = MessageFormatter.format(format, arg);
logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
}
}
public void debug(String format, Object arg1, Object arg2) {
if (logger.isDebugEnabled()) {
FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
}
}
public void debug(String format, Object[] argArray) {
if (logger.isDebugEnabled()) {
FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
}
}
public void debug(String msg, Throwable t) {
logger.log(FQCN, Level.DEBUG, msg, t);
}
//...省略一堆接口的实现...
}