1. Android 设计模式 Facade 外观模式 在项目中的实战

359 阅读7分钟

1.概念

外观模式(Facade Pattern)是一种结构型设计模式,核心思想是:

  1. 统一入口:为复杂子系统提供简化接口
  2. 隐藏复杂性:封装内部多个模块/类的交互细节
  3. 降低耦合:客户端只需与外观类交互,不直接依赖子系统

2.在Android源码中的应用场景

2.1 Android ARouter(阿里巴巴开源的路由框架)在设计中运用了多种设计模式,以下是核心模式及其应用场景的详细分析:
  • 应用场景ARouter 入口类
  • 实现
    提供统一的静态接口(如 init()build()navigation()),隐藏内部复杂的路由初始化、拦截器调度、跨模块通信等逻辑。
  • 代码体现
// 用户只需调用简单接口
ARouter.getInstance().build("/user/profile").navigation();
2.2 Context 体系(核心外观模式实现
  • 角色Context 是抽象外观类,ContextImpl 是具体外观类。
  • 功能:封装了应用环境的核心操作(如启动组件、访问资源、系统服务等),隐藏了 ActivityManagerServicePackageManagerService 等子系统的复杂性。
  • 源码示例
// ContextImpl.java
@Override
public void startActivity(Intent intent) {
    // 委托给ActivityManagerService处理
    mMainThread.getInstrumentation().execStartActivity(...);
}

@Override
public Object getSystemService(String name) {
    // 返回子系统服务实例(如LayoutInflater、ConnectivityManager)
    return SystemServiceRegistry.getSystemService(this, name);
}

开发者通过 Activity(继承自 Context)直接调用 startActivity() 等接口,无需关注 Binder 跨进程通信等底层细节

3.UML图

faced.png

4.项目的例子和没用设计模式的对比

4.1 典型语音系统包含:
  1. 音频输入层:麦克风采集、降噪、VAD(语音活动检测)
  2. 核心引擎层:ASR(语音识别)、NLU(自然语言理解)、TTS(语音合成)
  3. 业务层:指令执行、对话管理、多轮交互
  4. 输出层:音频播放、设备控制

face.png

流程图:
4.2 不用外观l模式

// ===== 新增:意图表示类 =====
public class Intent {
    private final String action;
    private final String target;
    private final String value;
    
    public Intent(String action, String target, String value) {
        this.action = action;
        this.target = target;
        this.value = value;
    }
    
    public String getAction() { return action; }
    public String getTarget() { return target; }
    public String getValue() { return value; }
    
    @Override
    public String toString() {
        return action + ":" + target + "=" + value;
    }
}

// ===== 新增:语音异常类 =====
public class VoiceException extends RuntimeException {
    private final ErrorType type;
    
    public VoiceException(ErrorType type, String message) {
        super(message);
        this.type = type;
    }
    
    public ErrorType getType() { return type; }
    
    public enum ErrorType {
        AUDIO_CAPTURE,    // 音频采集错误
        PROCESSING,       // 处理错误
        RECOGNITION,      // 识别错误
        UNDERSTANDING,    // 理解错误
        EXECUTION         // 执行错误
    }
}


// ===== 子系统组件 =====
public class AudioCapture {
    public byte[] record(int durationMs) {
        System.out.println("[AudioCapture] 录制音频中...时长: " + durationMs + "ms");
        // 模拟10%概率出现采集失败
        if (Math.random() > 0.9) {
            throw new VoiceException(
                VoiceException.ErrorType.AUDIO_CAPTURE, 
                "麦克风未就绪"
            );
        }
        return new byte[1024]; // 模拟音频数据
    }
}

public class NoiseReducer {
    public byte[] process(byte[] rawAudio) {
        System.out.println("[NoiseReducer] 降噪处理中...");
        return rawAudio; // 返回处理后的音频
    }
}

public class VADetector {
    public byte[] removeSilence(byte[] audio) {
        System.out.println("[VADetector] 静音段检测与切除...");
        return audio;
    }
}

public class SpeechRecognizer {
    public String recognize(byte[] audio) {
        System.out.println("[SpeechRecognizer] 语音识别中...");
        // 模拟10%概率识别失败
        if (Math.random() > 0.9) {
            throw new VoiceException(
                VoiceException.ErrorType.RECOGNITION, 
                "语音不清晰"
            );
        }
        return "打开客厅的灯"; // 模拟识别结果
    }
}

public class NLUProcessor {
    public Intent parse(String text) {
        System.out.println("[NLUProcessor] 自然语言理解中...");
        
        // 简单的意图解析逻辑
        if (text.contains("打开") && text.contains("灯")) {
            String location = text.contains("客厅") ? "客厅" : "卧室";
            return new Intent("light_control", location, "on");
        } else if (text.contains("关闭")) {
            String location = text.contains("客厅") ? "客厅" : "卧室";
            return new Intent("light_control", location, "off");
        } else if (text.contains("温度")) {
            return new Intent("query", "temperature", "");
        }
        
        throw new VoiceException(
            VoiceException.ErrorType.UNDERSTANDING, 
            "无法理解的指令: " + text
        );
    }
}

public class CommandExecutor {
    public void execute(Intent intent) {
        System.out.println("[CommandExecutor] 执行命令: " + intent);
        
        // 模拟10%概率执行失败
        if (Math.random() > 0.9) {
            throw new VoiceException(
                VoiceException.ErrorType.EXECUTION, 
                "设备未响应"
            );
        }
        
        // 实际设备控制逻辑...
    }
}

public class TTSEngine {
    public byte[] synthesize(String text) {
        System.out.println("[TTSEngine] 语音合成中: "" + text + """);
        return text.getBytes();
    }
}

public class AudioPlayer {
    public void play(byte[] audio) {
        System.out.println("[AudioPlayer] 播放音频...");
    }
}
4.3 使用外观模式

// ===== 外观类 =====
public class VoiceSystemFacade {
    private final AudioCapture capture;
    private final NoiseReducer reducer;
    private final VADetector vad;
    private final SpeechRecognizer recognizer;
    private final NLUProcessor nlu;
    private final CommandExecutor executor;
    private final TTSEngine tts;
    private final AudioPlayer player;

    public VoiceSystemFacade() {
        // 初始化所有子系统组件
        this.capture = new AudioCapture();
        this.reducer = new NoiseReducer();
        this.vad = new VADetector();
        this.recognizer = new SpeechRecognizer();
        this.nlu = new NLUProcessor();
        this.executor = new CommandExecutor();
        this.tts = new TTSEngine();
        this.player = new AudioPlayer();

        System.out.println("语音系统初始化完成");
    }

    // 核心简化接口
    public void processVoiceCommand(int durationMs) {
        try {
            // 1. 音频采集与处理
            byte[] processedAudio = processAudio(durationMs);

            // 2. 语音识别与理解
            Intent intent = recognizeAndParse(processedAudio);

            // 3. 执行命令
            executeCommand(intent);

            // 4. 语音反馈
            provideFeedback("已执行命令: " + intent.getAction());

        } catch (VoiceException e) {
            handleError(e);
        }
    }

    // 语音识别专用接口
    public String recognizeSpeech(int durationMs) {
        byte[] audio = processAudio(durationMs);
        return recognizer.recognize(audio);
    }

    // 语音播报接口
    public void speak(String text) {
        byte[] audio = tts.synthesize(text);
        player.play(audio);
    }

    // ===== 私有方法封装实现细节 =====
    private byte[] processAudio(int durationMs) {
        byte[] raw = capture.record(durationMs);
        byte[] clean = reducer.process(raw);
        return vad.removeSilence(clean);
    }

    private Intent recognizeAndParse(byte[] audio) {
        String text = recognizer.recognize(audio);
        return nlu.parse(text);
    }

    private void executeCommand(Intent intent) {
        executor.execute(intent);
    }

    private void provideFeedback(String message) {
        byte[] audio = tts.synthesize(message);
        player.play(audio);
    }

    private void handleError(VoiceException e) {
        String errorMsg = "语音指令处理失败: " + e.getMessage();
        System.err.println(errorMsg);
        speak(errorMsg);
    }
}

调用

// ===== 客户端调用 =====
public class SmartHomeApp {
    public void processVoiceCommand() {
        // 1. 音频采集
        AudioCapture capture = new AudioCapture();
        byte[] rawAudio = capture.record(3000);

        // 2. 音频预处理
        NoiseReducer reducer = new NoiseReducer();
        byte[] cleanAudio = reducer.process(rawAudio);

        VADetector vad = new VADetector();
        byte[] finalAudio = vad.removeSilence(cleanAudio);

        // 3. 语音识别
        SpeechRecognizer recognizer = new SpeechRecognizer();
        String text = recognizer.recognize(finalAudio);

        // 4. 语义理解
        NLUProcessor nlu = new NLUProcessor();
        Intent intent = nlu.parse(text);

        // 5. 执行命令
        CommandExecutor executor = new CommandExecutor();
        executor.execute(intent);

        // 6. 语音反馈
        TTSEngine tts = new TTSEngine();
        byte[] responseAudio = tts.synthesize("已为您打开客厅的灯");

        AudioPlayer player = new AudioPlayer();
        player.play(responseAudio);
    }

    public static void processVoiceCommandByFaced() {
        // 单行代码完成整个语音流程
        voiceSystem.processVoiceCommand(3000);
    }

    private static VoiceSystemFacade voiceSystem;

    public static void announceTime() {
        voiceSystem.speak("当前时间是 ");
    }

    public static void main(String[] args) {

        System.out.println("------------------main-------------------");

        SmartHomeApp app = new SmartHomeApp();
        app.processVoiceCommand();

        voiceSystem = new VoiceSystemFacade();
        announceTime();
        processVoiceCommandByFaced();
    }
}

gradle的配置

plugins {
    id 'java-library'
}


// ==== 添加以下配置 ====
task runSmartHomeApp(type: JavaExec) {
    classpath = sourceSets.main.runtimeClasspath
    mainClass = "com.example.design.face.SmartHomeApp" // 替换为你的完整类名
    dependsOn 'classes'
}

afterEvaluate {
    // 确保在构建后运行
    tasks.named('runSmartHomeApp') {
        it.mustRunAfter 'build'
    }
}

详细的打印日志:

[VoiceSystem] 语音系统初始化完成

测试场景1: 正常指令

===== 开始语音指令处理 =====
[AudioCapture] 录制音频中...时长: 2000ms
[NoiseReducer] 降噪处理中...
[VADetector] 静音段检测与切除...
[SpeechRecognizer] 语音识别中...
[NLUProcessor] 自然语言理解中...
[CommandExecutor] 执行命令: light_control:客厅=on
[TTSEngine] 语音合成中: "已执行命令: light_control:客厅=on"
[AudioPlayer] 播放音频...
===== 语音指令处理完成 =====


测试场景2: 仅语音识别
[AudioCapture] 录制音频中...时长: 1500ms
[NoiseReducer] 降噪处理中...
[VADetector] 静音段检测与切除...
[SpeechRecognizer] 语音识别中...
识别结果: 打开客厅的灯

测试场景3: 异常处理演示

===== 开始语音指令处理 =====
[AudioCapture] 录制音频中...时长: 1000ms
[错误] 语音指令处理失败: 麦克风未就绪
[TTSEngine] 语音合成中: "麦克风故障,请检查设备"
[AudioPlayer] 播放音频...

===== 开始语音指令处理 =====
[AudioCapture] 录制音频中...时长: 1000ms
[NoiseReducer] 降噪处理中...
[VADetector] 静音段检测与切除...
[SpeechRecognizer] 语音识别中...
[错误] 语音指令处理失败: 语音不清晰
[TTSEngine] 语音合成中: "抱歉,没有听清楚,请再说一次"
[AudioPlayer] 播放音频...

===== 开始语音指令处理 =====
[AudioCapture] 录制音频中...时长: 1000ms
[NoiseReducer] 降噪处理中...
[VADetector] 静音段检测与切除...
[SpeechRecognizer] 语音识别中...
[NLUProcessor] 自然语言理解中...
[CommandExecutor] 执行命令: light_control:客厅=on
[TTSEngine] 语音合成中: "已执行命令: light_control:客厅=on"
[AudioPlayer] 播放音频...
===== 语音指令处理完成 =====

测试场景4: 直接播报
[TTSEngine] 语音合成中: "现在时间是下午三点整"
[AudioPlayer] 播放音频...

四、外观模式在语音系统中的核心优势

  1. 简化复杂系统入口

    // 使用前
    capture.record() → reducer.process() → vad.removeSilence() → ...
    
    // 使用后
    voiceSystem.processVoiceCommand(3000);
    
  2. 统一错误处理机制

    private void handleError(VoiceException e) {
        // 统一记录日志
        logger.error("Voice processing failed", e);
        
        // 根据错误类型播放不同提示
        if(e.getType() == ErrorType.NO_AUDIO) {
            speak("未检测到语音输入");
        } else if(e.getType() == ErrorType.RECOGNITION_FAILED) {
            speak("未能识别您的指令");
        }
    }
    

    3. 灵活替换底层实现

    public VoiceSystemFacade(RecognitionEngine engine) {
        // 可注入不同的识别引擎
        this.recognizer = new SpeechRecognizer(engine);
    }
    

5.优点

  • ✅ 简化客户端调用复杂度
  • ✅ 提高代码可维护性(修改内部不影响客户端)
  • ✅ 作为系统边界,明确职责划分
  • ✅ 特别适合重构遗留复杂系统

6.和别的设计模式的区别

门面模式的核心价值在于解决复杂系统易用性问题,这是其他模式无法替代的:

  1. 与适配器的区别

    • 适配器解决接口不匹配,门面解决接口太复杂
  2. 与中介者的区别

    • 中介者关注对象间通信解耦,门面关注简化客户端调用
  3. 与代理的区别

    • 代理控制单个对象的访问,门面封装多个对象的协作

demo的地址: github.com/pengcaihua1…