参考文章:《Head Firts设计模式》对应章节
在看看百度语音demo的时候,发现有一段代码写得好像很厉害的样子,其中一个类叫做适配器,于是搜索得到适配器设计模式。
-
为什么要有适配器设计模式以及适配器设计模式是什么?
设计模式是为了将一种接口A适配到另外一种接口B,A接口对于用户可能更友好、更通用等等,B接口是某种库要求的接口。
以百度语音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就绪后的事情
//...
}
...
}
再根据插座的例子来实现下:
首先是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);
}
//...
}