三、适配器模式

一、模式定义

将一个类的接口转换成使用者希望的另一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

二、应用场景

当使用者用到的某些类的接口与其它代码不兼容时,就可以使用适配器模式来改造。当使用者希望重用几个现有的子类,但这些类缺少一些不能添加到超类中的公共功能时,也可以使用适配器模式。

2.1、优点

①、符合单一职责原则(Single responsibility principle)

②、符合开闭原则(open-closed principle)

三、几种不同的适配器实现方式

3.1、对象适配器
  • 对象适配器代码一

    package adaptor;

    public class ObjectAdapterDesignPattern {
    public static void main(String[] args) {
    //实例化对象
    Product product = new Product();
    //将对象注入到适配器中
    Target target = new Adapter(product);
    int i = target.output5V();
    System.out.println("适配后,修改参数为:"+i);
    }
    }
    //适配器接口,想要修改或适配的规则
    interface Target{
    int output5V();
    }
    //原始产品
    class Product{
    public int output220V(){
    return 220;
    }
    }
    //对象适配器
    class Adapter implements Target {
    private Product product;

    复制代码
     public Adapter(Product product) {
        this.product = product;
     }
     //进行适配,返回需要的适配结果
     @Override
     public int output5V() {
        int i_220 = product.output220V();
        System.out.println(String.format("原始电压:%d v  --->  输出电压:%d  v ", i_220, 5));
        return 5;
     }

    }

上面代码的运行结果如下:

  • 对象适配器代码二

    package adaptor.objectAdapter;

    public class AdapterPattern {
    public static void main(String[] args) {
    //实例化对象
    Source source = new Source();
    //将对象注入适配器
    Adapter adapter = new Adapter(source);
    adapter.method1();
    adapter.method2();
    }
    }
    //定义需要执行的标准方法
    interface Targetable {
    /**
    * 与Source类中的方法名相同
    */
    public void method1();

    复制代码
     /**
      * 新的方法
      */
     public void method2();

    }

    class Source {
    public void method1(){
    System.out.println("this is Source method...");
    }
    }
    //对象适配器类型,要注入对象的实体类
    class Adapter implements Targetable {
    public Source source;

    复制代码
     public Adapter(Source source) {
        this.source = source;
     }
    
     //在标准方法中调用注入对象的方法
     @Override
     public void method1() {
        source.method1();
     }
    
     @Override
     public void method2() {
        System.out.println("This is the targetable method...");
     }

    }

上面代码的运行结果如下:

3.2、类适配器
  • 类适配器代码一

    package adaptor;

    public class ClassAdapterDesignPattern {
    public static void main(String[] args) {
    //只用实例化适配器就可以。
    Target_ target_ = new Adapter_();
    int i_5 = target_.output5V();
    System.out.println("适配后,修改参数为:"+i_5);
    //类适配器,会对接口和适配器产生一些污染,可以调用到父类中的方法
    int i_220 = ((Adapter_) target_).output220V();
    System.out.println("产生污染的对象,可以获取父类中的方法:"+i_220);
    }
    }

    //原始产品
    class Product_{
    public int output220V(){
    return 220;
    }
    }

    //适配器接口,想要修改或适配的规则
    interface Target_{
    int output5V();
    }

    //类适配器,直接继承原始产品,而不是注入原始产品的实例
    //可能产生问题:会对接口和适配器产生一些污染,可以调用到父类中的方法
    class Adapter_ extends Product_ implements Target_{
    //进行适配,返回需要的适配结果
    @Override
    public int output5V() {
    int i_220 = super.output220V();
    System.out.println(String.format("原始电压:%d v ---> 输出电压:%d v ", i_220, 5));
    return 5;
    }
    }

上面代码的运行结果如下:

  • 类适配器代码二

    package adaptor.classAdapter;

    public class AdapterPattern {
    public static void main(String[] args) {
    //只用实例化适配器
    Adapter adapter = new Adapter();
    adapter.method1();
    adapter.method2();
    }
    }

    interface Targetable {
    /**
    * 与Source类中的方法名相同
    */
    public void method1();

    复制代码
     /**
      * 新类的方法
      */
     public void method2();

    }

    class Source {
    //原始产品中,有与接口中声明的方法同名的method1()方法
    public void method1(){
    System.out.println("this is Source method...");
    }
    }
    //继承原始产品,等于重写了接口的同名方法
    class Adapter extends Source implements Targetable {
    @Override
    public void method2() {
    System.out.println("This is the targetable method...");
    }
    }

上面代码的运行结果如下:

3.3、接口适配器

下面代码中的abstract Wrapper.class为适配类。其余的类只要继承Wrapper类即可适配Port.interface接口中定义的函数

复制代码
package adaptor.interfaceAdaptor;

public class AdapterPattern {

   private static Port chatPort = new Chat();
   private static Port serverPort = new Server();

   public static void main(String[] args) {
      // 聊天服务
      chatPort.NET();

      // 服务器
      serverPort.SSH();
      serverPort.NET();
      serverPort.Tomcat();
      serverPort.MySQL();
   }
}

interface Port {
   /**
    * 远程SSH端口为22
    */
   void SSH();

   /**
    * 网络端口为80
    */
   void NET();

   /**
    * Tomcat容器端口为8080
    */
   void Tomcat();

   /**
    * MySQL数据库端口为3306
    */
   void MySQL();
}

/**
 * 定义抽象类实现端口接口,但是什么事情都不做
 */
abstract class Wrapper implements Port {
   @Override
   public void SSH() {

   }

   @Override
   public void NET() {

   }

   @Override
   public void Tomcat() {

   }

   @Override
   public void MySQL() {

   }
}

/**
 * 网站服务器
 * 需要Tomcat容器,Mysql数据库,网络服务,远程服务
 */
class Server extends Wrapper {
   @Override
   public void SSH() {
      System.out.println("Connect success...");
   }

   @Override
   public void NET() {
      System.out.println("WWW...");
   }

   @Override
   public void Tomcat() {
      System.out.println("Tomcat is running...");
   }

   @Override
   public void MySQL() {
      System.out.println("MySQL is running...");
   }
}


/**
 * 提供聊天服务
 * 需要网络功能
 */
class Chat extends Wrapper {

   @Override
   public void NET() {
      System.out.println("Hello World...");
   }
}

四、适配器模式的应用

Java IO 库采用了装饰器模式(Decorator Pattern)和适配器模式(Adapter Pattern)的组合设计模式,其中InputStream是一个抽象类:

①、FileInputStream.class覆盖(适配了)了abstract InputStream.class的read()函数,skip()函数等,是一个读取字节的输入Stream;

②、ObjectInputStream.class覆盖(适配了)了abstract InputStream.class的read()函数,skip()函数等,然后又有一些abstract InputStream.class种没有的函数,比如readObject()函数等,是一个从流中读入一个自定义的对象的输入Stream,但是需要与ObjectOutputStream与配合使用,且按同样的顺序(写入的对象的顺序决定了读取对象的顺序)。

这3个类的UML图,如下所示: