一天一种JAVA设计模式之六:适配器模式

314 阅读8分钟

写在前面的话

复习、总结23种设计模式

获取详细源码请点击我

上一篇

# 一天一种JAVA设计模式之五:原型模式

适配器模式

记重点

总结一下三种适配器模式的应用场景:

类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。

接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

定义

适配器模式 (Adapter Pattern) 是一种结构型设计模式,它通过将一个类的接口转换成客户端所期望的另一个接口,使得原本由于接口不兼容而无法一起工作的类能够协同工作。

适配器模式的分类

类适配器模式对象适配器模式

前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,故应用相对较少。

适配器模式中主要的角色

  • Target目标角色

该角色定义把其他类转换为何种接口,也就是我们的期望接口。(当前系统业务所期待的接口,它可以是抽象类或接口。)

  • Adaptee源角色

你想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象,经过适配器角色的包装,它会成为一个崭新、靓丽的角色。(它是被访问和适配的现存组件库中的组件接口。)

  • Adapter适配器角色

适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:把源角色转换为目标角色,怎么转换?

通过继承或是类关联的方式。(它是一个转换器,通过继承或引用适配者的对象,
把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。)

类适配器

适配器模式通用类图

image.png

类适配器模式DEMO-1

目标角色

package com.design.pattern.adapter.test04;  
  
public interface Target {  
  
    void Request();  
}

目标角色的实现类

package com.design.pattern.adapter.test04;  
  
public class ConcreteTarget implements Target{  
  
    @Override  
    public void Request() {  
        System.out.println("call this...");  
    }  
}

源角色

package com.design.pattern.adapter.test04;  
  
public class Adaptee {  
  
    public void SpecificRequest() {  
        System.out.println("111111111");  
    }  
}

适配器角色

package com.design.pattern.adapter.test04;  
  
public class Adapter extends Adaptee implements Target {  

    @Override  
    public void Request() {  
        super.SpecificRequest();  
    }  
}

场景类

package com.design.pattern.adapter.test04;  
  
public class Client {  
  
    public static void main(String[] args) {  
        // 原有的业务逻辑  
        Target target1 = new ConcreteTarget();  
        target1.Request();  
        // 现在增加了适配器角色后的业务逻辑  
        Target target2 = new Adapter();  
        target2.Request();
    }  
  
}

类适配器模式DEMO-2

【读卡器】:现有一台电脑只能读取 SD 卡,而要读取 TF 卡中的内容的话就需要使用到适配器模式。创建一个读卡器,将 TF 卡中的内容读取出来。类图如下:

image.png

目标接口

package com.design.pattern.adapter.test01;  
  
//目标接口  
public interface SDCard {  
    //从 SD 卡中读取数据  
    String readSD();  

    //往 SD 卡中写数据  
    void writeSD(String msg);  
}

class SDCardImpl implements SDCard {  
    @Override  
    public String readSD() {  
        String msg = "SDCard read msg : hello word SD";  
        return msg;  
    }  

    @Override  
    public void writeSD(String msg) {  
        System.out.println("SDCard write msg :" + msg);  
    }  
}

被适配类

package com.design.pattern.adapter.test01;  
  
//被适配类的接口  
public interface TFCard {  
    //从TF卡中读数据  
    String readTF();  

    //从THF卡中写数据  
    void writeTF(String msg);  
}

//适配者类  
class TFCardImpl implements TFCard {  
    @Override  
    public String readTF() {  
        String msg = "TFCard read msg :hello word TFCard";  
        return msg;  
    }  

    @Override  
    public void writeTF(String msg) {  
        System.out.println("TFCard write msg :" + msg);  
    }  
}

适配器类

package com.design.pattern.adapter.test01;  
  
//适配器类  
public class ADAdapterTF extends TFCardImpl implements SDCard{  
  
    @Override  
    public String readSD() {  
        System.out.println("adapter read tf card");  
        return readTF();  
    }  

    @Override  
    public void writeSD(String msg) {  
        System.out.println("adapter write tf card");  
        writeTF(msg);  
    }  
}

我们的电脑

package com.design.pattern.adapter.test01;  
  
//计算机类  
public class Computer {  
    //从 SD 卡中读取数据  
    public String readSD(SDCard sdCard) {  
        if (sdCard == null) {  
            throw new NullPointerException("sd card cannot be null");  
        }  
        return sdCard.readSD();  
    }  
}

Client

package com.design.pattern.adapter.test01;  
  
public class Client {  
  
    public static void main(String[] args) {  
        //创建计算机对象  
        Computer computer = new Computer();  
        String msg = computer.readSD(new SDCardImpl());  
        System.out.println(msg);  

        System.out.println("=============");  

        //使用该电脑读取 TF 卡中的数据  
        String msg1 = computer.readSD(new ADAdapterTF());  
        System.out.println(msg1);  
    }  
}

类适配器模式违背了合成复用原则。类适配器是客户类有一个接口规范的情况下可用,反之不可用

对象适配器

对象适配器模式类图

image.png

对象适配器模式DEMO-1

package com.design.pattern.adapter.test05;  
  
public class Adaptee1 {  
  
    public void SpecificRequest() {  
        System.out.println("111111111");  
    }  
}
package com.design.pattern.adapter.test05;  
  
public class Adaptee2 {  
  
    public void SpecificRequest() {  
        System.out.println("111111111");  
    }  
}
package com.design.pattern.adapter.test05;  
  
import com.design.pattern.adapter.test04.Target;  
  
public class Adapter implements Target {  
    private Adaptee1 adaptee1;  
    private Adaptee2 adaptee2;  

    public Adapter(Adaptee1 adaptee1, Adaptee2 adaptee2) {  
        this.adaptee1 = adaptee1;  
        this.adaptee2 = adaptee2;  
    }  

    @Override  
    public void Request() {  
        adaptee1.SpecificRequest();  
        adaptee2.SpecificRequest();  
    }  
  
}

对象适配器模式DEMO-2

实现方式:对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。还是以上面的读卡器为例进行改写,类图如下:

image.png

对象适配器类

package com.design.pattern.adapter.test02;  
  
import com.design.pattern.adapter.test01.SDCard;  
import com.design.pattern.adapter.test01.TFCard;  
  
//适配器类  
public class SDAdapterTF implements SDCard {  
  
    //声明适配者类  
    private TFCard tfCard;  

    public SDAdapterTF(TFCard tfCard) {  
        this.tfCard = tfCard;  
    }  

    @Override  
    public String readSD() {  
        System.out.println("adapter read tf card");  
        return tfCard.readTF();  
    }  

    @Override  
    public void writeSD(String msg) {  
        System.out.println("adapter write tf card");  
        tfCard.writeTF(msg);  
    }  
}

Client

package com.design.pattern.adapter.test02;  
  
import com.design.pattern.adapter.test01.Computer;  
import com.design.pattern.adapter.test01.SDCardImpl;  
import com.design.pattern.adapter.test01.TFCardImpl;  
  
public class Client {  
    public static void main(String[] args) {  
        //创建计算机对象  
        Computer computer = new Computer();  
        String msg = computer.readSD(new SDCardImpl());  
        System.out.println(msg);  

        System.out.println("===============");  
        //使用该电脑读取 TF 卡中的数据  
        //创建适配器类对象  
        SDAdapterTF sdAdapterTF = new SDAdapterTF(new TFCardImpl());  
        String msg1 = computer.readSD(sdAdapterTF);  
        System.out.println(msg1);  
    }  
}

接口适配器

当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter,实现所有方法。而此时我们只需要继承该抽象类即可

接口适配器DEMO-1

package com.design.pattern.adapter.test06;  
  
public interface Listener {  
  
    void onSuccess(Object response);  

    void onTimeout();  

    void onError(Throwable throwable);  
}

定义一个抽象子类实现父接口中所有的方法

package com.design.pattern.adapter.test06;  
  
public abstract class ListenerAdapter implements Listener{  
    @Override  
    public void onSuccess(Object response) {  
    }  

    @Override  
    public void onTimeout() {  
    }  

    @Override  
    public void onError(Throwable throwable) {  
    }  
}

适配器模式的优缺点

优点:

  • 适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定他们就成

  • 增加了类的透明性

    想想看,我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块是透明的,也是它不需要关心的。

  • 提高了类的复用度

    当然了,源角色在原有的系统中还是可以正常使用,而在目标角色中也可以充当新的演员。

  • 灵活性非常好

    某一天,突然不想要适配器,没问题,删除掉这个适配器就可以了,其他的代码都不用修改,基本上就类似一个灵活的构件,想用就用,不想就卸载。

缺点:

  • 类型数量增加:引入适配器类可能会增加代码中的类的数量,使得代码的结构复杂化。
  • 过多的适配器:如果系统中存在大量的不兼容接口,就会导致需要大量的适配器类,增加了系统的复杂性。
  • 性能损失:适配器模式可能会导致性能损失,因为在进行接口转换时需要额外的逻辑处理。

应用场景

(1)适配器模式适用于以下场景:

  1. 系统需要使用已有的类,但其接口与系统要求的接口不匹配。适配器模式可以将已有类的接口适配成系统所需的接口。
  2. 需要复用现有类,在现有类的基础上进行功能扩展,但原有类的接口无法直接满足需求。适配器模式可以通过适配器来扩展已有类的功能,使其适配新的接口。
  3. 在系统中引入新的类,但该类的接口与系统的其他组件不兼容。适配器模式可以将新类的接口转换成系统要求的接口,使其与其他组件协同工作。
  4. 不同团队开发的两个系统需要进行集成,但其接口不兼容。适配器模式可以将两个系统的接口进行适配,使其能够协同工作。
  5. 需要设计一个可复用的类,它需要与其他不兼容的接口一起工作。适配器模式可以将该类的接口适配成其他不兼容接口,使其能够与其他组件协同工作。

总之,适配器模式适用于在不兼容接口之间进行转换的场景。它可以使原本不兼容的类能够协同工作,提高代码的复用性和灵活性。在软件开发过程中,适配器模式经常被用于系统集成、API 设计、旧系统的功能扩展等方面。

总结

适配器的通用代码也比较简单,把原有的继承关系变更为关联关系就可以了,不再赘述。

对象适配器和类适配器的区别是:类适配器是类间继承,对象适配器是对象的合成关系,也可以说是类的关联关系,这是两者的根本区别。二者在实际项目中都会经常用到,由于对象适配器是通过类间的关联关系进行耦合的,因此在设计时就可以做到比较灵活,比如修补源角色的隐形缺陷,关联其他对象等,而类适配器就只能通过覆写源角色的方法进行扩展,在实际项目中,对象适配器使用到场景相对较多。

下一篇

# 一天一种JAVA设计模式之五:原型模式