设计模式之适配器模式


1.定义

适配器模式属于结构型模式的一种,目的是将两个原本互不兼容的接口关联起来,将一个接口转换为另外一个客户端期望的接口,使得两个接口能够在一起工作,这类接口往往都具有相似性,且往往都是对旧版本接口进行兼容,这个时候就需要为两者之间创建一个适配器,达到兼容的目的。
类比生活中的适配器就比较容易理解了,手机接口适配器,电源适配器,笔记本扩展坞,视频信号输出接口适配,本质上都是将两个标准不同规范不同的接口通过适配器使得两者能够兼容,程序中自然也是如此,适配器依然充当着相同的角色。

适配器角色

  • 目标角色(Target):客户端期望的接口,抽象类或接口
  • 适配者角色(Adaptee):已存在接口,但是和客户端期望的接口不一致
  • 适配器角色(Adaper):适配器,将适配者角色现有的接口进行适配,满足目标角色

UML:
在这里插入图片描述
图片来源:百度图库

根据以上的内容,按照平时的代码经验,可以简单推断出适配器实现应该至少存在以下关键点:

  1. 适配器中能够访问需要适配(适配者)的接口
  2. 适配器应该实现客户端期望的接口
  3. 实现接口方法,完成适配逻辑

以VGA-HDMI为例,王大娘攒了一台主机,装了一个4090显卡用来玩扫雷,想着有10年前的显示器(VGA接口),为了节约预算就没有买新显示器,但是很遗憾,这么low的显卡竟然没有VGA接口,王大娘很委屈,这个时候张大爷站出来了,要为王大娘解决问题,解决方案就是设计一个HDMI-VGA的信号适配器
这个时候就要求:

  1. 张大爷的适配器必须遵循HDMI的协议规范细节,否则适配器无从谈起
  2. 张大爷的适配器必须面对VGA适配,否则王大娘不高兴
  3. 基于以上两点,张大爷设计了一个适配器,在充分了解HDMI的协议细节之后,在适配器内部完成了信号格式的转换过程,然后送给王大娘,王大娘很开心,开心的去扫雷去了。
2.示例

回归到正题,根据以上的叙述,就可以对适配器模式进行简单实现了,适配器大概划分为两种,个人更倾向于第二种。第一种就是通过继承适配者来获取适配者原有接口标准,第二种就是通过在适配器内部持有适配者对象来实现,第一种称之为基于类的适配器,第二种是基于对象的适配器

首先准备需要适配的接口信息,如下,目的是将以下HDMI接口通过适配器,完成到VGA的转换

/**
 * @description: Target 期望目标接口
 * @version: 1.0
 */
public interface VGA {

    String VGA_signal();
}


/**
 * @description: Adaptee 适配者
 * @version: 1.0
 */
public interface HDMI {

    String HDMI_signal();
}

/**
 * @description: 需要适配接口实现
 * @version: 1.0
 */
public class GraphicsCard implements HDMI{


    @Override
    public String HDMI_signal() {
        return "我是显卡,只有hdmi接口,不兼容VGA";
    }
}

基于类的适配器:

/**
 * @description: 适配器,将HDMI经过适配转换为VGA
 * @version: 1.0
 */
public class Adapter extends GraphicsCard implements VGA {
    @Override
    public String VGA_signal() {
        String hdmi = super.HDMI_signal();
        System.out.println(hdmi);
        System.out.println("开始适配....");
        System.out.println("适配结束....");
        return "经过适配器,终于将HDMI信号转换为VGA类型";
    }
}

test

public class Test {
    public static void main(String[] args) {
        Adapter adapter = new Adapter();
        String vga_signal = adapter.VGA_signal();
        System.out.println(vga_signal);
    }
}
我是显卡,只有hdmi接口,不兼容VGA
开始适配....
适配结束....
经过适配器,终于将HDMI信号转换为VGA类型

基于对象的适配器:

/**
 * @description: 适配器,将HDMI经过适配转换为VGA
 * @version: 1.0
 */
public class Adapter implements VGA {

    private HDMI hdmi;

    public Adapter(HDMI hdmi){
        this.hdmi = hdmi;
    }

    @Override
    public String VGA_signal() {
        String hdmi = this.hdmi.HDMI_signal();
        System.out.println(hdmi);
        System.out.println("开始适配....");
        System.out.println("适配结束....");
        return "经过适配器,终于将HDMI信号转换为VGA类型";
    }
}
/**
 * @description: test
 * @version: 1.0
 */
public class Test {
    public static void main(String[] args) {
        HDMI hdmi = new GraphicsCard();
        Adapter adapter = new Adapter(hdmi);
        String vga_signal = adapter.VGA_signal();
        System.out.println(vga_signal);
    }
}
我是显卡,只有hdmi接口,不兼容VGA
开始适配....
适配结束....
经过适配器,终于将HDMI信号转换为VGA类型

除了以上基本的例子,如果适配器种类也有很多,可以将适配器进一步向上抽象,然后实现不同具体的适配器实现

/**
 * @description: 抽象适配器
 * @version: 1.0
 */
public abstract class Adapter implements VGA {

    private HDMI hdmi;

    public Adapter(HDMI hdmi){
        this.hdmi = hdmi;
    }

    public HDMI getHdmi() {
        return hdmi;
    }
}

实现具体的适配器

/**
 * @description: HP适配器,将HDMI经过适配转换为VGA
 * @version: 1.0
 */
public class HPAdapter extends Adapter{

    public HPAdapter(HDMI hdmi) {
        super(hdmi);
    }

    @Override
    public String VGA_signal() {
        String hdmi = this.getHdmi().HDMI_signal();
        System.out.println(hdmi);
        System.out.println("开始适配....");
        System.out.println("适配结束....");
        return "经过HP适配器,终于将HDMI信号转换为VGA类型";
    }
}
我是显卡,只有hdmi接口,不兼容VGA
开始适配....
适配结束....
经过HP适配器,终于将HDMI信号转换为VGA类型

到这里突然发现有人提到一个缺省适配器,相当于是一个简化的适配器,之前没发现这个,这里顺便也记录一下,这种方式的目的是为了只对原有接口的部分功能进行适配,而不是整个接口,主要实现方式就是通过一个抽象的中间类,做默认的空实现,而真正适配的时候只对抽象类中的部分方法进行重新适配操作,如下

/**
 * @description: 加入只期望对func2进行适配,1和3没用
 * @version: 1.0
 */
public interface Target {

    void func1();

    void func2();

    void func3();
}
/**
 * @description: 通过抽象类,其子类不必适配所有
 * @version: 1.0
 */
public abstract class AbstractAdapter implements Target{
    @Override
    public void func1() {}
    @Override
    public void func2() {}
    @Override
    public void func3() {}
}
public class Test {
    public static void main(String[] args) {
        Target adapter = new AbstractAdapter(){
            @Override
            public void func2() {
                System.out.println("适配逻辑开始....");
                System.out.println("适配逻辑结束....");
            }
        };
        adapter.func2();
    }
}
3.总结

适配器和装饰器模式实现上有一点相似,但是实际应用场景和要解决的问题并不一样,一个是在原有对象的基础上增强功能,另外一个是将两个接口兼容,或者说将一个接口转为另外一个接口。适配器模式的好处就是提高了接口类的复用,增加了不同接口之间的灵活性,但是如果一个系统适配器过多,则会导致系统变得混乱,最直接表现就是看山不是山,看水不是水(表面调用的是A,但实际内部已经被适配成B的实现),所以使用适配器的场景一般都是对老旧版本接口进行兼容适配,既是优雅解决接口之间兼容问题的次优解,也是重构压力下的妥协之举。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值