适配器模式:众所周知,我是个变压器

适配器模式:众所周知,我是个变压器

哈啰,各位小伙伴们,这里是每天进步一点点的花栗鼠小K

上期小K讲到了结构型模式里的双胞胎(装饰者模式代理模式 ),今天就索性接着结构型模式聊呗,正好也是面试中的常客。今天这位嘉宾,可非比寻常,它可以轻松地将两个不同的接口兼容起来,将两个独立接口的功能结合起来,就有点像生活中的变压器,此君就是适配器模式



01


适配器模式



适配器模式能将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作

PS:适配器模式有两类:类适配器对象适配器

类适配器:通过继承现有接口类并实现目标接口,会使得现有接口类完全对适配器暴露,使得适配器具有现有接口类的全部功能,破坏了封装性。目前已不太使用

对象适配器:持有现有接口类一个实例,并扩展其功能,实现目标接口。优先采用组合而不是继承,会使得代码更利于维护。比较推荐

类适配器的UML如图

适配器模式:众所周知,我是个变压器
类适配器UML

对象适配器的UML如图

适配器模式:众所周知,我是个变压器
对象适配器UML

二者最大的区别是后者使用聚合代替了继承

适配器模式主要包含三个角色

Target :目标,当前系统业务所期待的接口,它可以是抽象类或接口。

Adaptee :适配者,它是被访问和适配的现存组件库中的组件接口。

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



02


代码实现



举个🌰,小K暑假去漂亮国旅游。小K是忠实的国产品牌支持者,带着小米手机跑到了漂亮国。漂亮国的正常供电电压为110V ,小K带了一款中国制造电器,这个电器必须要在 220V 电压下才能充电使用。这种情况下,小K的期望接口是有一个220V 的电压为电器充电,但实际的接口是仅有一个 110V 的电压供电器充电,这种情况下就需要采用一根电压转换器使得 110V 的电压能够转换为 220V 的电压,供客户使用

类适配器

小K期望 220V 的接口(Target)

public interface Target {
    void chargeBy220V();
}

现有的110V充电接口(Adaptee)

public interface Adaptee {
    void chargeBy110V();
}

漂亮国现有充电接口(Adaptee实现类)

public class AmericanCharger implements Adaptee{
    @Override
    public void chargeBy110V() {
        System.out.println("美国供电器,正在通过110V电压为您服务");
    }
}

继承现有接口来完成对现有接口的扩展(Adapter)

public class Adapter extends AmericanCharger implements Target{
    @Override
    public void chargeBy220V() {
        super.chargeBy110V();//现有功能
        System.out.println("再加110V,达到220V,冲鸭!");//对现有功能扩展
    }
}

客户端调用(Client)

public static void main(String[] args) {
    new Adapter().chargeBy220V();
}

类适配器使得代码逻辑混乱 这种情况下仿佛 Adapter110V 的漂亮国供电器可以直接使用而不需要其他信息的

优点:

  • 使用方便,代码简化仅仅引入一个对象,并不需要额外的字段来引用Adaptee实例

缺点:

  • 高耦合,灵活性低使用对象继承的方式,是静态的定义方式

对象适配器

这类适配器需要和类适配器对比着来看

主要代码上的差异在Adapter实现和客户端调用上

通过聚合完成对现有接口功能的扩展(Adapter)

class Adapter implements Target {
    Adaptee adaptee;//持有现有接口具体实现对象的引用

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void chargeBy220V() {
        adaptee.chargeBy110V();//该对象的现有功能
        System.out.println("再加110V,达到220V,冲鸭!");//对现有功能扩展
    }
}

客户端调用(Client)

public static void main(String[] args) {
    //现在小K有一个美国110V供电站,但我无法使用
    Adaptee adaptee = new americanCharger(); // 本处为向上转型(upcasting)
    //将这个供电器交给适配器,适配器转换为220V供电器
    Adapter adapter = new Adapter(adaptee);
    //接下来通过适配器充电就好了
    adapter.chargeBy220V();        
}

该部分Adaptee adaptee = new americanCharger(); 处使用到了向上转型( upcasting

补充一个知识点

向上转型( upcasting ):把子类对象直接赋给父类引用,不需要强制转换

向下转型( downcasting ):把指向子类对象的父类引用赋给子类引用,要强制转换

对象适配器采用组合的方式实现对现有接口的扩展以达到客户期望的接口

优点:

  • 灵活性高、低耦合采用 “对象组合”的方式,是动态组合方式

缺点:

  • 使用复杂需要引入对象实例



03


适配器模式的优缺点



GOF有23种设计模式,都不是尽善尽美的。就比如适配器模式,总有它的一些优劣性

优点:

  • 可以让任何两个没有关联的类一起运行
  • 提高了类的复用,可以一致化多个不同接口
  • 将现有接口实现类隐藏,增加了类的透明度
  • 灵活性高,可自由适配。

缺点:

  • 过多地使用适配器,会让系统非常凌乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构
  • 某些适配工作可能非常困难,例如让房子飞起来



04


总结



本文主要介绍了适配器模式的两种模式:类适配器对象适配器,当然面试倒不会问的那么细致啦,但是学知识嘛,内卷不丢人。小K建议尽量使用对象的适配器模式,多用合成/聚合、少用继承

本期就到这了,这里是花栗鼠小K,下次有🌰,我再来,拜拜~~~

关注六只栗子,面试不迷路!


作者    花栗鼠小K

编辑   一口栗子  

原文始发于微信公众号(六只栗子):适配器模式:众所周知,我是个变压器

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/88594.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!