资讯专栏INFORMATION COLUMN

人人都会设计模式--适配器模式--Adapter

phpmatt / 1789人阅读

摘要:转载请注明出处作者地址本文出自简书人人都会设计模式教程简介阅读对象本篇教程适合新手阅读,老手直接略过教程难度初级,本人水平有限,文章内容难免会出现问题,如果有问题欢迎指出,谢谢正文一什么是适配器模式生活中的适配器比如电脑转接器这里主要是指连

PS:转载请注明出处
作者: TigerChain
地址: http://www.jianshu.com/p/1edf5d944abb
本文出自 TigerChain 简书 人人都会设计模式

教程简介

1、阅读对象
本篇教程适合新手阅读,老手直接略过

2、教程难度
初级,本人水平有限,文章内容难免会出现问题,如果有问题欢迎指出,谢谢

正文

一、什么是适配器模式 1、生活中的适配器

比如电脑转接器「这里主要是指连接电脑和投影仪的」,以我的 MAC 电脑为例子,我们公司的投影支持 VGA 和 HDMI ,但是我的 MAC 电脑只有一个 MINI DP 接口,如何把 MINI DP 转成 VGA 或 HDMI ,那么我就买了这个玩意「全称 MINI DP 转 VGA & HDMI 适配器」,这东西就是一个适配器

这个适配器就可以把 MAC 和有 VGA 或 HDMI 的设备连接起来了,如下:

类似的还有电脑电源适配器,变压器「也是一种适配器」,其实净水器也可以看作是一种适配器「把杂水变成纯净水」,等等

2、程序中的适配器

比如我们对接第三方的接口到我们的系统「对方给我们的接口,我们现在的接口对接不起来」

我们就需要写一个中间层「适配器」,做为一个桥梁,把两个接口连接起来

适配器模式的定义

通俗的说适配器模式就是把两个不兼容的接口连接起来,类似一个桥梁的作用

注:适配器模式类比一个桥梁作用「它的作用不仅仅连接这么简单,还有转化等操作,桥梁就是为了方便理解」

适配器模式的结构

角色 类别 说明
Target 目标角色 是一个接口,也就是我们期待要转化成的接口
Adaptee 源角色 原始的类或接口对象
Adapter 适配器角色 把源角色转化成目标角色的类

适配器模式的分类

1、类适配器模式

类适配器简单的 UML

总结一下就是:适配器「Adapter」继承源类「Src」并且实现目标「Dst」接口,来实现 Src-->Dst 的转换

2、对象适配器模式

对象适配器简单的 UML

总结一下就是:适配器「Adapter」持有源类「Src」的引用,并实现目标「Dst」接口,来实现 Src--> Dst 的转化

3、接口适配器模式

对于这种模式「资料上也没有说有这种模式,我是在写代码的过程中发现可以这样写」,我持保留意见,如果有什么问题,大家完全可以说适配器模式的分类就有以上两种模式,可我认为这是适配器模式的一个变种

接口适配器简单的 UML

*总结一下就是: 适配器实现源和目标,把源转化成目标这么一个过程

二、适配器模式举例

1、Mac 电脑连接投影仪适配器

以开头的例子为例子, MAC 电脑要连接投影仪器,需要一个 MINI DP 转 VGA & HDMI 适配器,然后才能连接上投影仪

所以这里目标是 VGAORHDMI ,源是 MINI DP 适配器就是上面的那根线

类适配投影仪和 MAC 电脑简单的 UML

根据 UML 撸码

使用类适配器模式

1、定义目标接口 VgaOrHdmi

/**
 * 目标角色,对投影仪来说就要 VAG 或 HDMI
 * @auther TigerChain
 */
public interface VgaOrHdmi {
    /**输出 VGA 或是 Hdmi 接口*/
    String getVgaOrHdmi() ;
}

2、定义源类 MiniDp

/**
 * 源角色,MAC 电脑上的 MINIDP 接口
 * @auther TigerChain
 */
public class MiniDp {

    public String outPutMinkDp(){
        return "我是 mac 上的 MiniDp 输入接口" ;
    }
}

3、定义适配器类 MidiDp2VgaOrHdmiAdapter

/**
 * 适配器,既是 MINIDP 接口也是 VAGORHDMI 接口,这样就可以把 MINIDP 转成
 * VAG OR HDMI 接口
 * @auther TigerChain
 */
public class MidiDp2VgaOrHdmiAdapter extends MiniDp implements VgaOrHdmi{

    @Override
    public String getVgaOrHdmi() {
       return  convertMiniDp2VgaOrHdmi() ;
    }
    /**
     * 把 MINIDP 转化成 VAG 或 HDMI 方法
     * @return
     */
    private String convertMiniDp2VgaOrHdmi(){
        //拿到源
        String str = outPutMinkDp();
        System.out.println(str+" 
 经过适配器转化 ");
        // 为这简单起见,这里直接修改源
        str = "输出变成  VGA 和 HDMI 接口" ;
        return str ;
    }
}

4、定义打印机类 Projector

/**
 * 这是投影仪,我就是 VGA 和 HDMI 接口的
 * @auther TigerChain
 */
public class Projector {
        // 我要的就是 VGA 或者 HDMI 接口
        public String getVgaOrHdmi(VgaOrHdmi vgaOrHdmi){
            return vgaOrHdmi.getVgaOrHdmi() ;
        }
}

5、定义测试类 Test

/**
 * 测试类
 * @auther TigerChain
 */
public class Test {
    public static void main(String args[]){
        //投影仪
        Projector projector = new Projector() ;
        //适配器
        VgaOrHdmi adapter = new MidiDp2VgaOrHdmiAdapter() ;
        // 最后得到投影仪想要的 VAG or HDMI 即可
        String str = projector.getVgaOrHdmi(adapter) ;
        System.out.println(str);
    }
}

6、运行查看结果

完美转化了有木有

对象适配器实现上述例子

对象适配投影仪和 MAC 电脑简单的 UML

是不是和上面的图一样?错,肯定不一样,一样我还贴出来「我又没病」,只有一点改变,就是适配器不是继承源,而是持有源的引用,代码修改起来非常简单,只是修改适配器即可「别的代码都是一样的」

1、修改 MidiDp2VgaOrHdmiAdapter

/**
 * 适配器,既是 MINIDP 接口也是 VAGORHDMI 接口,这样就可以把 MINIDP 转成
 * VAG OR HDMI 接口
 */
public class MidiDp2VgaOrHdmiAdapter  implements VgaOrHdmi{
    
    // 修改之处 1 
    private MiniDp miniDp ;
    // 修改之处 2
    public MidiDp2VgaOrHdmiAdapter(MiniDp miniDp){
        this.miniDp = miniDp ;
    }

    @Override
    public String getVgaOrHdmi() {
       return  convertMiniDp2VgaOrHdmi() ;
    }
    /**
     * 把 MINIDP 转化成 VAG 或 HDMI 方法
     * @return
     */
    private String convertMiniDp2VgaOrHdmi(){
        // 修改之处 3  拿到源
        String str = miniDp.outPutMinkDp();
        System.out.println(str+" 
 经过适配器转化 ");
        // 为这简单起见,这里直接修改源
        str = "输出变成  VGA 和 HDMI 接口" ;
        return str ;
    }
}

2、修改测试类,并运行查看结果

修改测试类

结果和上面是一样的

适配器模式一般情况下不是软件设计的时候就要考虑的一种模式,它是一种随着软件的维护可能由于不同的开发人员,不同的产品,不同的厂家造成的功能类似而接口不相同的情况下一种解决方案「只有碰到无法改变原有设计和代码的情况下,才考虑适配」

2、成龙初探好莱坞

我们的功夫明星成龙初闯好莱坞的时候有一个最大的障碍就是语言问题「英文不太熟悉」,那么最早的时候都是有翻译者的,那么这个翻译员就充当了适配器的角色「把英文翻译成中文,或者把中文翻译成英文」

翻译员简单的 UML

根据 UML 擼代码

1、新建 ISpeakEn 接口

/**
 * Created by tigerchain on 11/12/17.
 */
public interface ISpeakEn {
    // 说英文
    String speakEnglish(String str) ;
}

2、新建 ISpeakCn 接口

/**
 * Created by tigerchain on 11/12/17.
 */
public interface ISpeakCn {
    // 说中文
    String speakCn(String str) ;
}

3、翻译的接口「适配器」 Interpreter

/**
 * Created by tigerchain on 11/12/17.
 * 翻译的接口
 */
public interface Interpreter {

    // 中文翻译成英文
    void chinese2English(String str) ;
    // 英文翻译成中文
    void english2Chinese(String str) ;
}

4、具体的翻译员小张 ZhangTranslation

/**
 * Created by tigerchain on 11/12/17.
 * 举个例子,成龙有一个张翻译,能把英文翻译成中文,也能把中文翻译成英文
 */
public class ZhangTranslation implements Iinterpreter,ISpeakCn,ISpeakEn{

    @Override
    public void chinese2English(String str) {
        translationC2E(speakCn(str));
    }

    @Override
    public void english2Chinese(String str) {
        translationE2C(str) ;
    }
    // 翻译英文--> 中文
    private void translationE2C(String str) {
        System.out.println("小张把 "+str+" 翻译成中文");
    }

    // 翻译中文--> 英文
    private void translationC2E(String str){
        System.out.println("小张把 "+str+" 翻译成英文");
    }
    @Override
    public String speakCn(String str) {
        return str ;
    }

    @Override
    public String speakEnglish(String str) {
        return str;
    }
}

5、来一个老外「要对话肯定要有关建人物呀」 Foreigner

/**
 * Created by tigerchain on 11/12/17.
 * 一个老外用英文给成龙打招呼
 */
public class Foreigner implements ISpeakEn {

    @Override
    public String speakEnglish(String str) {
        String say = "Wills say:"+str ;
        System.out.println(say);
        return say ;
    }
}

6、成龙上场「另一个关建人物」 JackieChan

/**
 * Created by tigerchain on 11/12/17.
 */
public class JackieChan implements ISpeakCn {

    @Override
    public String speakCn(String str) {
        String say = "成龙说:"+str ;
        System.out.println(say);
        return say ;
    }
}

7、测试对话 Test

/**
 * Created by tigerchain on 11/12/17.
 * 这是一个成龙对话老外的测试类
 */
public class Test {

    public static void main(String args[]){
        // 成龙说了一句话
        JackieChan jackieChan = new JackieChan() ;
        String str = jackieChan.speakCn("你好 wills");

        // 老外说了一句
        Foreigner foreigner = new Foreigner() ;
        String str2 = foreigner.speakEnglish("Hello Jackie Chain");

        // 张翻译翻译
        ZhangTranslation zhangTranslation = new ZhangTranslation() ;
        zhangTranslation.chinese2English(str);
        zhangTranslation.english2Chinese(str2);
    }
}

8、运行查看结果

怎么样这个张翻译「适配器」还不错吧,当然适配器模式也会进化,会变种,但是万变不离其宗「上面 Demo 就可以看作是一个变种的适配器模式」

三、Android 源码中的适配器模式

ListAdapter

没有搞错吧,上一节不是说了 ListAdapter 是一种策略模式吗?没错它也是一种适配器模式「从名字就可以看出来」

ListAdapter 适配器简单的 UML

从上图可以看出,BaseAdapter 是一个基础适配器,下面子类是具体各自的适配器,这些适配器的作用就是把数据 List,Cusor 等转化成 ListAdapter 接口,最终让客户端 ListView 来调用「可以通俗的说就是把数据适配到 View 上面」

以 ArrayAdapter 源码分析一下

1、先看看 Adapter

public interface Adapter {
int getCount();

    Object getItem(int var1);

    long getItemId(int var1);

    boolean hasStableIds();

    View getView(int var1, View var2, ViewGroup var3);

    int getItemViewType(int var1);

    int getViewTypeCount();

    boolean isEmpty();
 }

2、ListAdapter 继承 Adapter 接口,所以拥有 Adapter 所有功能

3、BaseAdapter 实现 ListAdapter 所以不仅拥有 ListAdapteer 的所有能力

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter{

    //省略苦干代码
    public boolean areAllItemsEnabled() {
        return true;
    }

    public boolean isEnabled(int position) {
        return true;
    }

    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return getView(position, convertView, parent);
    }

    public int getItemViewType(int position) {
        return 0;
    }

    public int getViewTypeCount() {
        return 1;
    }
    
    public boolean isEmpty() {
        return getCount() == 0;
    }    
}

3、再来看看 ArrayAdapter

public class ArrayAdapter extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {

 private List mObjects;  
    //列出其中一个构造方法 
     public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
            @IdRes int textViewResourceId) {
        this(context, resource, textViewResourceId, new ArrayList<>());
    }
    
    @Override
    public int getCount() {
        return mObjects.size();
    }

    @Override
    public @Nullable T getItem(int position) {
        return mObjects.get(position);
    }

    public int getPosition(@Nullable T item) {
        return mObjects.indexOf(item);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public @NonNull View getView(int position, @Nullable View convertView,
            @NonNull ViewGroup parent) {
        return createViewFromResource(mInflater, position, convertView, parent, mResource);
    }
    //其它代码流省略
}

我们通过源码可以看到 ArrayAdapter 就是把 List 的数据源采用一系列方法转化成 ListAdapter 需要的几种方法 getView getPosition 等等「这就是一个适配的过程」

ListAdapter 既是策略模式又是适配器模式

根据选择模式使用那种适配器 ListAdapter 就是策略模式,但是根据每个策略所实现功能「它就是适配器模式」

四、适配器模式的优缺点

优点

1、客户端只关心适配器,对客户端来说更简单

2、现有类的复用而不需要改变,解决了现有类和目标类环境不一致的问题

3、解耦「目标类和适配器解耦」,不用改变原有的代码,再一个就是某天目标大变了,那么我们再编写一个适配器就可以了「原来的适配器可以扔掉了,就像某天你的 MAC 笔记本坏了,电源适配器就可以扔了--这是一个玩笑,除非是适配器不适用新买的 MAC」

缺点

1、适配器编写过程需要多方考虑「可能会很复杂」

2、适配器把一个接口转化成另一个接口,在客户端会给人误导,明明传入的是 A 接口,最后成 B 了,让人很晕

到此为止,我们就介绍完了适配器模式,点赞是一种美德

公众号:TigerChain 更多精彩的文章等着你

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70666.html

相关文章

  • Java 23种设计模式----配器模式

    摘要:适配器模式的结构通过继承实现通过委让实现代码实现目标类使用数据线适配类使用转适配线主函数与在适配器模式中的应用当前,不少公司使用整合进行系统开发。 Java 23种设计模式----适配器模式 1、面向对象OO = 面向对象分析OOA + 面向对象设计OOD + 面向对象编程OOP 2、编程是一门技术、同时也是一门艺术 3、应该面向接口编程,而不是面向实现编程 什么是设计模式 设计模式是...

    VincentFF 评论0 收藏0
  • 配器模式Adapter

    摘要:适配器模式一适配器模式定义将一个接口转换成客户希望的另一个接口角色目标接口对象客户希望的另一个接口或具体类需要适配的类现有的,不符合客户需求的接口或具体类适配器对象包装适配的对象,转换接口实现方式类适配器继承对象适配器聚合二具体实现创建目标 适配器模式 一.适配器模式 1.1 定义 将一个接口转换成客户希望的另一个接口. 1.2 角色 目标接口对象(Target):客户希望的另一个接...

    calx 评论0 收藏0
  • 一起学设计模式 - 配器模式

    摘要:建议尽量使用对象的适配器模式,少用继承。适配器模式也是一种包装模式,它与装饰模式同样具有包装的功能,此外,对象适配器模式还具有委托的意思。 适配器模式(Adapter Pattern)属于结构型模式的一种,把一个类的接口变成客户端所期待的另一种接口,从而使原本接口不匹配而无法一起工作的两个类能够在一起工作... 概述 当你想使用一个已经存在的类,而它的接口不符合你的需求,或者你想创建...

    codeKK 评论0 收藏0
  • 深入理解配器模式

    摘要:组件目标角色客户端所期待得到的接口,适配器角色适配器类是本模式的核心。最后附上一张适配器的概览图参考适配器模式与模式之适配器模式基础浅谈常见设计模式适配器模式原理及实例介绍适配器模式装饰模式代理模式的异同 适配器模式 说到适配器,小秋第一想到的是我们电脑的电源适配器,它能够将220v,110v等不同电压转换为我们电脑需要的电压值供电脑使用,那么你知道适配器模式是怎样实现的吗,下面就和小...

    Rindia 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<