适配器设计模式

参考文章:《Head Firts设计模式》对应章节

在看看百度语音demo的时候,发现有一段代码写得好像很厉害的样子,其中一个类叫做适配器,于是搜索得到适配器设计模式。

  • 为什么要有适配器设计模式以及适配器设计模式是什么?
    设计模式是为了将一种接口A适配到另外一种接口B,A接口对于用户可能更友好、更通用等等,B接口是某种库要求的接口。


    适配器设计模式_第1张图片

    以百度语音demo中为例,API需要用户实现一个EventListener的接口B,这个接口提供多个事件,比如识别成功、识别失败、返回识别结果等等.....

//EventListener.java
package com.baidu.speech;

public interface EventListener {
    void onEvent(String var1, String var2, byte[] var3, int var4, int var5);
}

如果你直接实现B接口,然后交给API,这样是OK的。
但想象你调用的时候自己的接口A只有onAsrBegin,onAsrEnd等方法来实现对应响应,可能你自己没有这样的A接口,但创建这样的A接口可以让程序更加友好,从B接口的onEvent到A接口的onAsrBegin,onAsrEnd。

具体实现:
A接口:

//IRecogListener.java
public interface IRecogListener {
    /**
     * onAsrReady后检查到用户开始说话
     */
    void onAsrBegin();

    /**
     * 检查到用户开始说话停止,或者ASR_STOP 输入事件调用后,
     */
    void onAsrEnd();

    ...
}

适配器:
适配器(实际上API需要的B接口EventListener)首先输入的是A接口(IRecogListener),然后适配器就将A接口的内容跟B接口的内容适配起来,比如A接口接受到API发来的CALLBACK_EVENT_ASR_READY事件的时候,就应该调用listener.onAsrReady(),因为A在就绪状态的时候要执行的动作就放在onAsrReady()方法中。

//RecogEventAdapter.java
package com.baidu.android.voicedemo.recognization;

import android.util.Log;

import com.baidu.android.voicedemo.control.ErrorTranslation;
import com.baidu.speech.EventListener;
import com.baidu.speech.asr.SpeechConstant;
import com.baidu.android.voicedemo.util.Logger;

import org.json.JSONException;
import org.json.JSONObject;

/**
 * Created by fujiayi on 2017/6/14.
 */

public class RecogEventAdapter implements EventListener {

    private static final String TAG = "RecogEventAdapter";


    private IRecogListener listener;

    public RecogEventAdapter(IRecogListener listener) {
        this.listener = listener;
    }

    protected String currentJson;

    @Override
    public void onEvent(String name, String params, byte[] data, int offset, int length) {
        currentJson = params;
        String logMessage = "name:" + name + "; params:" + params;

        // logcat 中 搜索RecogEventAdapter,即可以看见下面一行的日志
        Log.i(TAG, logMessage);
        if (false) { // 可以调试,不需要后续逻辑
            return;
        }
       if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_READY)) {
            // 引擎准备就绪,可以开始说话
            listener.onAsrReady();
        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_UNLOADED)) {
            listener.onOfflineUnLoaded();

...
}

用户真正在实现A类的时候就可以很友好了。

public class StatusRecogListener implements IRecogListener, IStatus {

    private static final String TAG = "StatusRecogListener";

    /**
     * 识别的引擎当前的状态
     */
    protected int status = STATUS_NONE;

    @Override
    public void onAsrReady() {
        status = STATUS_READY;
        //这里可以做更多ASR就绪后的事情
        //...
    }

...
}

再根据插座的例子来实现下:


适配器设计模式_第2张图片

首先是USB接口,需要输出5V的电压:

//USB.java
public interface USB {
    void supply();
}

插座接口,提供任意电压

//Chazuo.java
public interface Chazuo {
    void supply();
}

实现一个插座类,提供220V电压

//Chazuo220V.java
class Chazuo220V implements Chazuo{
    public void supply(){
        System.out.println(String.format("插座提供220V电压"));
    }
}

功能实现,首先我们有一个220V的插座,然后要有一个USB接口的转化器,输出5V电压。

public class Main {

    public static void main(String[] args) {
    // write your code here
        System.out.println("我:有一个220V的插座");
        Chazuo chazuo = new Chazuo220V();

        System.out.println("我:用适配器得到USB接口");
        USB USBAdapter = new Adapter(chazuo);

        System.out.println("我:用适配得到的USB接口给手机充电");
        charge(USBAdapter);
    }

    static void charge(USB usb){
        usb.supply();
        System.out.println("手机:充电ing...");
    }
}

class Chazuo220V implements Chazuo{
    public void supply(){
        System.out.println(String.format("插座:提供220V电压"));
    }
}

适配器:

public class Adapter implements USB{
    Chazuo chazuo;

    public Adapter(Chazuo chazuo) {
        this.chazuo = chazuo;
    }

    public void supply() {
        chazuo.supply();

        System.out.println("适配器:转化电压ing...");

        System.out.println("适配器:输出5V电压");
    }
}

最后结果输出:

我:有一个220V的插座
我:用适配器得到USB接口
我:用适配得到的USB接口给手机充电
插座:提供220V电压
适配器:转化电压ing...
适配器:输出5V电压
手机:充电ing...

可以看到适配器在插座和USB之间适配,让手机要求USB接口得到满足,插座也可以是任意的电压。

问题:在第一个例子中有些人希望就用百度的B接口EventListener,但是有的人就希望用友好的接口A,要怎么做。
在希望得到接口的地方做重载,比如百度的demo中MyRecognizer需要传入EventListener接口,因为最终百度还是要EventListener接口,所以传入的时候A接口的时候需要用适配器转化一下,然后就跟传入EventListener接口一样了。

public class MyRecognizer {

    /**
     * 初始化
     *
     * @param context
     * @param recogListener 将EventListener结果做解析的DEMO回调。使用RecogEventAdapter 适配EventListener
     */
    public MyRecognizer(Context context, IRecogListener recogListener) {
       //这里先适配成百度需要的A接口
        this(context, new RecogEventAdapter(recogListener));
    }

    /**
     * 初始化 提供 EventManagerFactory需要的Context和EventListener
     *
     * @param context
     * @param eventListener
     */
    public MyRecognizer(Context context, EventListener eventListener) {
       // ...
        asr = EventManagerFactory.create(context, "asr");
        asr.registerListener(eventListener);
    }

    //...
}

你可能感兴趣的:(适配器设计模式)