Fork me on GitHub

设计模式之适配器模式

前言

小六新买了一个小米6手机,它高高兴兴的拿到新手机,想要插上耳机听歌,但发现手机没有耳机孔,仔细查看说明书之后发现,小米6手机是充电孔耳机孔在一起,在插耳机时需要一个耳机转接器,才能插耳机。我们用程序员的眼观来看,这里相当于增加了一个转接器类用于适配耳机,这就类似于我们今天提到的设计模式—适配器模式。

一、适配器模式实现

适配器模式的类图如下所示:

其中:

  • Client:客户端,调用自己需要的接口Target
  • Target:定义客户端需要的跟特定需求相关的接口
  • Apaptee:已存在接口,通常满足功能需求但与特定需求接口不一致
  • Adapter:适配器,将Adaptee适配为Client需要的Target接口。

适配器模式的主要功能是进行转换匹配,用来复用已有的功能。适配器模式将某个类的接口转换成客户端期望的另一个接口,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

三种适配器模式有各自的应用场景:

  • 类的适配器模式:将一个类转换成满足另一个新接口的类,创建一个新类,继承原有的类,实现新的接口即可。
  • 对象的适配器模式:将一个对象转换成满足另一个新接口的对象,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法即可。
  • 接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

下面针对三种情况进行代码实现:

1、类适配器模式

核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口是Targetable,通过Adapter类,将Source的功能扩展到Targetable里,类图如下所示:

代码如下:

public class Source {   
    public void method1() {  
        System.out.println("this is original method!");  
    }  
}  
public interface Targetable {  
    /* 与原类中的方法相同 */  
    public void method1();  
    /* 新类的方法 */  
    public void method2();  
}

Adapter类继承Source类,实现Targetable接口:

public class Adapter extends Source implements Targetable {  
    @Override  
    public void method2() {  
        System.out.println("this is the targetable method!");  
    }  
}  

这样Targetable接口的实现类就具有了Source类的功能。下面为测试类:

public class AdapterTest {  
    public static void main(String[] args) {  
        Targetable target = new Adapter();  
        target.method1();  
        target.method2();  
    }  
}  

2、对象适配器模式

基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。如图:

只需要修改Adapter类的源码即可:

public class Wrapper implements Targetable {  
    private Source source;  
    public Wrapper(Source source){  
        super();  
        this.source = source;  
    }  
    @Override  
    public void method2() {  
        System.out.println("this is the targetable method!");  
    }    
    @Override  
    public void method1() {  
        source.method1();  
    }  
}

测试类:

public class AdapterTest {     
    public static void main(String[] args) {  
        Source source = new Source();  
        Targetable target = new Wrapper(source);  
        target.method1();  
        target.method2();  
    }  
} 

输出与第一种一样,只是适配的方法不同而已。

3、接口适配器模式

接口的适配器模式是当一个接口中有多个抽象方法,但只需要使用其中某些方法,可借助一个抽象类实现该接口的所有的方法,而我们只需写一个类继承该抽象类,重写需要的方法即可。类图如下:

示例代码如下:

接口Sourceable:

 public interface Sourceable {      
     public void method1();  
     public void method2();  
 } 

抽象类Wrapper2:

public abstract class Wrapper2 implements Sourceable{  
    public void method1(){}  
    public void method2(){}  
}  

实现类:

public class SourceSub1 extends Wrapper2 {  
    public void method1(){  
        System.out.println("the sourceable interface's first Sub1!");  
    }  
} 
public class SourceSub2 extends Wrapper2 {  
    public void method2(){  
        System.out.println("the sourceable interface's second Sub2!");  
    }  
} 

二、适配器模式说明

适配器模式的本质是:转换匹配,复用功能。适配器模式中被适配的接口Adaptee与适配的接口Target没有关系,他们中的方法可以相同,也可以完全不同,适配器模式的实现方式是通过组合对象的方式进行的,将功能委托给被适配的对象进行的。

适配器模式调用的序列图如下所示:

适配器模式的实现有以下几种:

  • 常见适配:适配器类会实现接口,在实现过程中调用待适配的类中的方法

  • 智能适配器:在适配器类中实现接口中定义的新方法,通常来说,适配器类中既可以通过借助继承类中的方法实现高层功能,也可以实现接口中定义的新方法,进行功能扩展。

  • 缺省适配:即对接口的缺省实现,即接口适配器模式。

此外,在适配过程中,可能接口功能的实现需要多个待适配类中的方法交互才能满足需求,即同时适配多个类。适配实现的复杂度取决于待适配类与接口的相似度,相似程度越高,适配类的实现难度越低。

在实际项目过程中,通常会存在两个版本共存的情况,这就是需要使用到双向适配器。示例代码如下:

两个版本的实现代码:

public interface Targetable1 {
	public void produce1();
}
public class Target1 implements Targetable1 {
	@Override
	public void produce1() {
		System.out.println("Targetable1的produce1实现");
	}
}

public interface Targetable2 {
	public void produce2();
}
public class Target2 implements Targetable2 {
	@Override
	public void produce2() {
		System.out.println("Targetable2的produce2实现");
	}
}

适配器类的代码如下:

public class Adapter implements Targetable1, Targetable2 {

    private Targetable1 target1;
    private Targetable2 target2;

    @Override
    public void produce1() {
        target1.produce1();
    }

    @Override
    public void produce2() {
        target2.produce2();
    }
}

实际上,在使用适配器过程中存在一个问题:被适配的对象不兼容Adapter适配器类,这使得适配器类的适用范围受到限制。而双向适配器则解决了这样的问题,可以满足不同客户采用不同方式查看同一不同对象的需求。

三、适配器模式总结

1、应用场景

  • 实现已实现接口的复用,利用已有的代码实现功能。
  • 使用已存在的部分子类,可选用随想适配器,适配子类的父类。

2、优缺点

优点

  • 更好的复用性。适配器模式可复用已实现接口的兼容。
  • 更好的扩展性。实现适配器的过程中可以调用自己开发的功能,实现系统的扩展。

缺点

过多使用适配器,系统会比较混乱,不易理解

参考:文章主要参考《研磨设计模式》一书

posted @ 2019-01-31 21:42  紫焱luis  阅读(436)  评论(0编辑  收藏  举报