设计模式六-适配器模式

71 阅读4分钟

适配器模式

适配器模式是结构型模式介绍的第一个设计模式

对象适配器

适配器模式是解决接口转化的问题。

就好像是日常生活中使用的插口转化器

image.png

如上所示,假如我们需要使用USB接口的移动硬盘,但是我们的手机只有一个Type-C的接口,现在我们就要使用一个适配器解决接口转化的问题。

示例代码

首先定义一个接口,例如USB接口,如果某个类实现了USB接口便可认为这个类有一个USB的接口来实现USB功能(传输数据)

interface USB{
    public void transferData();
}

接下来创建一个类实现这个接口,例如我们有一个u盘 ,u盘上面恰好有一个usb的接口

class USBDrive implements USB{
​
    @Override
    public void transferData() {
        System.out.println("U盘正在传输数据");
    }
}

然后定义适配器

class USBTypeCAdapter{
    private USB usb;
    public USBTypeCAdapter(USBDrive usb){
        this.usb = usb;
    }
    @Override
    public void transferData() {
        usb.transferData();
    }
}

客户端调用:

public class AdapterPattern {
    public static void main(String[] args) {
        USBDrive usbDrive = new USBDrive();
        USBTypeCAdapter usbTypeCAdapter = new USBTypeCAdapter(usbDrive);
        usbTypeCAdapter.transferData();
    }
}

可以看到,我们最后使用的是usbTypeCAdapter的数据传输,而不是直接使用usbDriver的传输

同时我们可以改造一下,令usbTypeCAdapter实现一个 叫做TypeC的接口。

class USBTypeCAdapter implements TypeC{...}

客户端使用TypeC类型引用接收对象的引用

 public static void main(String[] args) {
        USBDrive usbDrive = new USBDrive();
        TypeC usbTypeCAdapter =  new USBTypeCAdapter(usbDrive);
        usbTypeCAdapter.transferData();
​
    }

我们更可以直观发现,现在我们客户端可以认为使用typeC的接口而不是Usb的接口了

如上,我们将一个对象通过适配器的包装,可以通过这个对象类没有实现的接口进行调用

类适配器模式

对象适配器通常将实例化对象进行包装,但是类适配器并不需要进行示例化,而是在类上直接进行包装。

那么我们思考如果完成这种功能呢? 我们知道类的继承,自类可以继承父类的功能(除非final修饰),如果我们定义了一个子类(适配器类),他继承了适配器需要的接口(需求接口),并且继承了父类,那么他就是父类的一个适配器类了。因为我们在重写接口方法的时候,可以直接调用父类的方法,相当于将父类的方法进行了进一步封装。

我们来看一个示例

首先定义一个适配器

interface Adapter{
    void printDate();
}

接下来定义需要被适配器修饰的类,也就是上述的父类

class DateInfo{
    public static String getDate(){
      return new Date().toString();
   }
}

接下来完成适配器类

class DateAdapter extends DateInfo implements Adapter{
​
    @Override
     public void printDate() {
        System.out.println(
                "当前时间为:"
                +getDate()
        );
    }
}

客户端调用

public class ClassAdapterPattern {
    public static void main(String[] args) {
        new DateAdapter().printDate();
    }
}

类适配器与对象适配器的比较

我们看起来,对象适配器仿佛是将一个个示例的对象进行包装。而类适配器是静态地在类中使用方法进行包装。

在平时仍然是对象适配器的会更为灵活与广泛。

默认适配器

默认适配器最为简单,封装性稍差,但是最好理解。

默认适配器实际上无法认为是一个真正的适配器,因为他只是对需要实现的接口进行了简易化包装。

他的适配器更像是对于用户来说,复杂的接口实现对用户来说更加友善了,但是对于机器来说效果不大。

这里引用一篇优秀文章的例子进行说明


我们希望使用Appache commons-io包中的 FileAlterationListener对文件进行监控

但是此接口定义了十分多的抽象方法,如果我们直接使用此接口,必须实现他的所有方法,无法避免地会造成代码大量地臃肿。

public interface FileAlterationListener {
    void onStart(final FileAlterationObserver observer);
    void onDirectoryCreate(final File directory);
    void onDirectoryChange(final File directory);
    void onDirectoryDelete(final File directory);
    void onFileCreate(final File file);
    void onFileChange(final File file);
    void onFileDelete(final File file);
    void onStop(final FileAlterationObserver observer);
}

但是我们往常只希望使用他的创建和删除文件地两个功能,于是我们可以定义如下一个适配器,在这个适配器中,实现了上述地复杂接口,但是所有的方法均置为空方法。当我们需要再使用这个接口的时候,只需要继承这个抽象的适配器对象就可以了,这样就不需要实现所有的方法了。

public class FileAlterationListenerAdaptor implements FileAlterationListener {
​
    public void onStart(final FileAlterationObserver observer) {
    }
​
    public void onDirectoryCreate(final File directory) {
    }
​
    public void onDirectoryChange(final File directory) {
    }
​
    public void onDirectoryDelete(final File directory) {
    }
​
    public void onFileCreate(final File file) {
    }
​
    public void onFileChange(final File file) {
    }
​
    public void onFileDelete(final File file) {
    }
​
    public void onStop(final FileAlterationObserver observer) {
    }
}

当我们使用的时候,直接继承这个适配器类,选择进行方法地重写。

public class FileMonitor extends FileAlterationListenerAdaptor {
    public void onFileCreate(final File file) {
        // 文件创建
        doSomething();
    }
​
    public void onFileDelete(final File file) {
        // 文件删除
        doSomething();
    }
}