适配器模式对于大家来说算是比较常见的用的比较多的模式了,主要功能是将类的接口转换为客户期望的另一个接口,使用适配器避免了由于接口不兼容而无法一起工作
在软件工程中,适配器模式是一种软件设计模式,它允许将现有类的接口用作另一个接口。它通常用于使现有类在不修改源代码的情况下与其他类一起工作。
一些现实生活中的例子
- 存储卡上有一些图片,你需要把它们传输到电脑上面,为了传输它们,你需要某种与计算机端口兼容的适配器,以便可以将存储卡附加到计算机上
- 电源适配器,一个三角插头不能连接到一个双孔插座上,它需要使用一个电源适配器,使它与双孔插座兼容
- 另一个例子是翻译人员将一个人说的话翻译给另一个人
适配器模式有三种类型
类适配器实现适配器的接口,Adapter 类继承 Adaptee(被适配类),同时实现Target 接口,根据需要选择并创建任意一种符合需求的子类,来实现具体功能;
对象适配器不使用继承的方式而是使用直接关联,或者称为委托的方式;
接口适配器的核心在于中间适配器,用于实现默认的接口方法,由子类来决定使用哪些方法,让代码更加清晰整洁;
对象适配器
对象适配器的重点在于对象,是通过在直接包含Adaptee类来实现的,当需要调用特殊功能的时候直接使用Adapter中包含的那个Adaptee对象来调用特殊功能的方法即可。
一个只会使用划艇而不会操作渔船的船长
首先我们有接口 划艇(RowingBoat) 和 渔船(FishingBoat)
public interface RowingBoat {
void row();
}
public class FishingBoat {
private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class);
public void sail() {
LOGGER.info("The fishing boat is sailing");
}
}
船长类实现 划艇(RowingBoat) 接口,使其能够移动
public class Captain implements RowingBoat {
private RowingBoat rowingBoat;
public Captain(RowingBoat rowingBoat) { this.rowingBoat = rowingBoat; }
@Override
public void row() { rowingBoat.row(); }
}
现在假设海盗来了,我们的船长需要逃跑,但是只有渔船可以使用,我们需要创建一个适配器来允许船长使用它的划艇技能操作渔船
public class FishingBoatAdapter implements RowingBoat {
private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoatAdapter.class);
private FishingBoat boat;
public FishingBoatAdapter() { boat = new FishingBoat(); }
@Override
public void row() { boat.sail(); }
}
现在 船长可以用渔船来躲避海盗了
Captain captain = new Captain(new FishingBoatAdapter());
captain.row();
类适配器示
类适配器的重点在于类,是通过构造一个继承Adaptee类来实现适配器的功能; 因为Java不支持多继承,所以只能通过接口的方法来实现多继承
// 标准接口
public interface Target {
void play();
}
// 已实现play的功能
public class RowingBoat implements Target {
private static final Logger LOGGER = LoggerFactory.getLogger(RowingBoat.class);
@Override
public void play() { LOGGER.info("滑动划艇...."); }
}
// 新的功能,摩托艇
public class Motorboat {
private static final Logger LOGGER = LoggerFactory.getLogger(Motorboat.class);
public void play() { LOGGER.info("驾驶摩托艇...."); }
}
适配器类,继承自Motorboat,并调用父类的play方法。配合标准接口实现功能转换
public class Adapter extends Motorboat implements Target{
public Adapter() { }
public void play() { super.play(); }
}
public class App {
public static void main(String[] args) {
Target target1 = new RowingBoat();
target1.play();
Target target = new Adapter();
target.play();
}
}
接口适配器示例
接口适配器应用场景,假设Target接口中有很多待实现的方法。
public interface Target {
void motorboat();
void rowingBoat();
void fishingBoat();
// ......
}
一般我们实现Target接口,默认需要实现接口中所有的方法。即使是我们用不到的。
使用接口适配器可以很好的解决这个问题。定义一个抽象接口类对Target接口的所有方法默认空实现
public abstract class Adapter implements Target {
@Override
public void motorboat() { }
@Override
public void rowingBoat() { }
@Override
public void fishingBoat() { }
public void run(){
motorboat();
rowingBoat();
fishingBoat();
}
}
子类继承适配器类只需要覆盖需要实现的方法
public class Assembly1 extends Adapter {
private static final Logger LOGGER = LoggerFactory.getLogger(Assembly1.class);
@Override
public void fishingBoat() { LOGGER.info("驾驶渔船..."); }
}
复写另外一组方法
public class Assembly2 extends Adapter {
private static final Logger LOGGER = LoggerFactory.getLogger(Assembly2.class);
@Override
public void motorboat() { LOGGER.info("驾驶摩托艇...."); }
@Override
public void rowingBoat() { LOGGER.info("滑动划艇...."); }
}
public class App {
public static void main(String[] args) {
Adapter a1 = new Assembly1();
a1.run();
Adapter a2 = new Assembly2();
a2.run();
}
}
适配器模式的优缺点优
优点
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无需修改原有结构。
- 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一适配者类可以在多个不同的系统中复用。
- 灵活性和扩展性都非常好,通过使用配置文件,可以很方便的更换适配器,也可以在不修改原有代码的基础上 增加新的适配器,完全复合开闭原则。
缺点
- 一次最多只能适配一个适配者类,不能同时适配多个适配者。
- 目标抽象类只能为接口,不能为类,其使用有一定的局限性。
- 适配者类不能为最终类
适配器模式的实际应用
在SpringMVC中有一些不同类型的controller,并且实现方式也不一样,由于实现方式不一样调用的方式也不一样,如果直接调用controller方法,那么代码应该写成这样
if(mappedHandler.getHandler() instanceof MultiActionController){
((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
...
}else if(...){
...
}
...
这样假设如果我们增加一个HardController,就要在代码中加入一行 if (mappedHandler.getHandler() instanceof HardController) 这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 — 对扩展开放,对修改关闭。
因此Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替controller执行相应的方法。这样在扩展Controller 时,只需要增加一个适配器类就完成了SpringMVC的扩展了,真的是很精巧的做法!
废话不多说还是上代码吧,为了看得清楚,就自己实现一套代码来模拟springMVC, 直接贴Spring源码容易降低关注点。
// 定义一个Adapter接口
public interface HandlerAdapter {
public boolean supports(Object handler);
public void handle(Object handler);
}
// 以下是三种Controller实现
public interface Controller {
}
public class HttpController implements Controller{
public void doHttpHandler(){
System.out.println("http...");
}
}
public class SimpleController implements Controller{
public void doSimplerHandler(){
System.out.println("simple...");
}
}
public class AnnotationController implements Controller{
public void doAnnotationHandler(){
System.out.println("annotation...");
}
}
// 下面编写适配器类
public class SimpleHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((SimpleController)handler).doSimplerHandler();
}
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
}
public class HttpHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((HttpController)handler).doHttpHandler();
}
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
}
public class AnnotationHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((AnnotationController)handler).doAnnotationHandler();
}
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
}
//模拟一个DispatcherServlet
public class DispatchServlet {
public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();
public DispatchServlet(){
handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
handlerAdapters.add(new SimpleHandlerAdapter());
}
public void doDispatch(){
// 此处模拟SpringMVC从request取handler的对象
// 不论实现何种Controller,适配器总能通过适配得到想要的结果
// HttpController controller = new HttpController();
// AnnotationController controller = new AnnotationController();
SimpleController controller = new SimpleController();
//得到对应适配器
HandlerAdapter adapter = getHandler(controller);
//通过适配器执行对应的controller对应方法
adapter.handle(controller);
}
public HandlerAdapter getHandler(Controller controller){
for(HandlerAdapter adapter: this.handlerAdapters){
if(adapter.supports(controller)){
return adapter;
}
}
return null;
}
public static void main(String[] args){
new DispatchServlet().doDispatch();
}
}
使用了适配器模式的地方还有许多,比如Java中的Arrays.asList()、Collections.list() 等等。感兴趣的小伙伴可以了解一下。