2.Android 设计模式二 建造者模式 (Builder) 在项目中的实战

171 阅读10分钟

1.概念

建造者模式(Builder Pattern)是一种对象创建型设计模式,它通过分离复杂对象的构建过程和表示来解决以下问题:

  • 当创建对象需要多步操作时(尤其当这些步骤需要灵活组合时)
  • 当对象有多个可选参数时(避免构造函数参数爆炸)
  • 当需要创建不同表示的相同构建过程时

核心思想:将对象的构造过程封装在一个专门的 Builder 类中,通过链式调用逐步设置参数,最后通过一个build()方法生成最终对象。

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

2.1 Aronter中的应用
  • 应用场景:跳转参数配置
  • 实现
    通过链式调用(.withXxx())构建路由参数,增强可读性和灵活性。
  • 代码体现
ARouter.getInstance()
    .build("/user/profile")
    .withString("name", "Alice")
    .withInt("age", 25)
    .navigation();

实现原理

  • build() 方法返回一个 Postcard 对象(扮演 Builder 角色)
  • Postcard 类提供多种 withXxx() 方法用于设置不同类型参数
  • 所有 withXxx() 方法都返回 Postcard 实例自身,支持链式调用
  • navigation() 方法最终执行路由跳转(相当于 build() 方法)
2.2 AlertDialog 建造者模式实现
AlertDialog dialog = new AlertDialog.Builder(context)
    .setTitle("Warning")
    .setMessage("Are you sure to delete?")
    .setIcon(R.drawable.ic_warning)
    .setPositiveButton("OK", (d, i) -> deleteItem())
    .setNegativeButton("Cancel", null)
    .create();
    
dialog.show();

适用场景:当对象包含多个组成部分(特别是可选部分),且需要多种组合方式时,建造者模式是最佳选择

3.UML图

deepseek_mermaid_20250628_e83524.png

4.语音项目的例子和没用建造者模式的demo

语音Tts播放引擎的功能封装:

支持播报的声音类型,播报的速度,音量的大小,音频格式情感参数,语音风格

4.1 没有使用设计模式的方式
/**
 * TTS 引擎 SDK - 非建造者模式实现
 */
public class BasicTTSEngine {
    // 声音类型枚举
    public enum VoiceType {
        MALE_DEEP,       // 低沉男声
        FEMALE_SOFT,     // 温柔女声
        CHILD,           // 儿童声音
        ROBOTIC,         // 机器人声音
        CUSTOM           // 自定义声音
    }

    // 语言类型枚举
    public enum Language {
        ENGLISH_US,
        ENGLISH_UK,
        MANDARIN,
        CANTONESE,
        JAPANESE,
        KOREAN,
        SPANISH
    }

    // 配置参数
    private VoiceType voiceType;
    private float speed;
    private float pitch;
    private int volume;
    private Language language;
    private boolean async;
    private String customVoice;

    private boolean isPlaying = false;

    public BasicTTSEngine() {
        // 默认配置
        this.voiceType = VoiceType.FEMALE_SOFT;
        this.speed = 1.0f;
        this.pitch = 1.0f;
        this.volume = 80;
        this.language = Language.MANDARIN;
        this.async = false;
        this.customVoice = null;

        initializeEngine();
    }

    // 全参数构造函数 - 难以使用和维护
    public BasicTTSEngine(VoiceType voiceType, float speed, float pitch, int volume,
                          Language language, boolean async, String customVoice) {
        validateParams(speed, pitch, volume);

        this.voiceType = voiceType;
        this.speed = speed;
        this.pitch = pitch;
        this.volume = volume;
        this.language = language;
        this.async = async;
        this.customVoice = customVoice;

        initializeEngine();
    }

    private void validateParams(float speed, float pitch, int volume) {
        if (speed < 0.5f || speed > 2.0f) {
            throw new IllegalArgumentException("语速必须在0.5到2.0之间");
        }
        if (pitch < 0.5f || pitch > 1.5f) {
            throw new IllegalArgumentException("音调必须在0.5到1.5之间");
        }
        if (volume < 0 || volume > 100) {
            throw new IllegalArgumentException("音量必须在0到100之间");
        }
    }

    private void initializeEngine() {
        // 模拟初始化TTS引擎
        System.out.println("初始化TTS引擎...");
        System.out.println("配置: " + this);
    }

    // 多个setter方法
    public void setVoiceType(VoiceType voiceType) {
        this.voiceType = voiceType;
    }

    public void setSpeed(float speed) {
        validateParams(speed, this.pitch, this.volume);
        this.speed = speed;
    }

    public void setPitch(float pitch) {
        validateParams(this.speed, pitch, this.volume);
        this.pitch = pitch;
    }

    public void setVolume(int volume) {
        validateParams(this.speed, this.pitch, volume);
        this.volume = volume;
    }

    public void setLanguage(Language language) {
        this.language = language;
    }

    public void setAsync(boolean async) {
        this.async = async;
    }

    public void setCustomVoice(String customVoice) {
        this.customVoice = customVoice;
    }

    public void speak(String text) {
        if (isPlaying) {
            System.out.println("警告: TTS引擎正在播放中,忽略新请求");
            return;
        }

        isPlaying = true;

        if (async) {
            new Thread(() -> doSpeak(text)).start();
        } else {
            doSpeak(text);
        }
    }

    private void doSpeak(String text) {
        System.out.println("开始播报: " + text);
        System.out.println("使用配置: " + toString());

        // 模拟语音合成和播放
        try {
            // 根据语速计算播放时间 (每字符0.1秒 * 语速因子)
            long duration = (long) (text.length() * 100 / speed);
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("播报完成");
        isPlaying = false;
    }

    @Override
    public String toString() {
        return String.format("声音类型: %s, 语速: %.1f, 音调: %.1f, 音量: %d%%, 语言: %s, 异步: %b, 自定义: %s",
                voiceType, speed, pitch, volume, language, async,
                customVoice != null ? customVoice : "无");
    }
}
4.2 使用设计模式的方式
/**
 * TTS 引擎 SDK - 建造者模式实现
 */
public class TTSEngineSdk {

        // 声音类型枚举
        public enum VoiceType {
            MALE_DEEP,       // 低沉男声
            FEMALE_SOFT,     // 温柔女声
            CHILD,           // 儿童声音
            ROBOTIC,         // 机器人声音
            CUSTOM           // 自定义声音
        }

        // 语言类型枚举
        public enum Language {
            ENGLISH_US("en-US", "美式英语"),
            ENGLISH_UK("en-UK", "英式英语"),
            MANDARIN("zh-CN", "普通话"),
            CANTONESE("zh-HK", "粤语"),
            JAPANESE("ja-JP", "日语"),
            KOREAN("ko-KR", "韩语"),
            SPANISH("es-ES", "西班牙语");

            private final String code;
            private final String displayName;

            Language(String code, String displayName) {
                this.code = code;
                this.displayName = displayName;
            }

            public String getCode() {
                return code;
            }

            public String getDisplayName() {
                return displayName;
            }
        }

        // 情感参数枚举
        public enum Emotion {
            NEUTRAL,    // 中性
            HAPPY,      // 高兴
            SAD,        // 悲伤
            ANGRY,      // 愤怒
            SURPRISED,  // 惊讶
            CALM,       // 平静
            EXCITED     // 兴奋
        }

        // 音频格式枚举
        public enum AudioFormat {
            PCM_16BIT("audio/pcm;bit=16", "16-bit PCM"),
            MP3("audio/mpeg", "MP3"),
            AAC("audio/aac", "AAC"),
            OGG_VORBIS("audio/ogg;codec=vorbis", "OGG Vorbis"),
            WAV("audio/wav", "WAV");

            private final String mimeType;
            private final String displayName;

            AudioFormat(String mimeType, String displayName) {
                this.mimeType = mimeType;
                this.displayName = displayName;
            }

            public String getMimeType() {
                return mimeType;
            }

            public String getDisplayName() {
                return displayName;
            }
        }

        // 语音风格枚举
        public enum Style {
            NEWS_READING,      // 新闻播报
            STORY_TELLING,     // 故事讲述
            CONVERSATIONAL,    // 对话风格
            COMMANDING,        // 命令风格
            POETIC,            // 诗歌朗诵
            BUSINESS,          // 商务风格
            INFORMAL           // 非正式风格
        }

        // 特殊效果类
        public static class Effects {
            private final float echoLevel;         // 回声效果级别 (0.0-1.0)
            private final float reverbLevel;       // 混响效果级别 (0.0-1.0)
            private final Map<String, Float> eqSettings; // 均衡器设置

            private Effects(Builder builder) {
                this.echoLevel = builder.echoLevel;
                this.reverbLevel = builder.reverbLevel;
                this.eqSettings = builder.eqSettings;
            }

            public static class Builder {
                private float echoLevel = 0.0f;
                private float reverbLevel = 0.0f;
                private Map<String, Float> eqSettings = new HashMap<>();

                public Builder setEchoLevel(float level) {
                    if (level < 0 || level > 1.0f) {
                        throw new IllegalArgumentException("回声级别必须在0.0-1.0之间");
                    }
                    this.echoLevel = level;
                    return this;
                }

                public Builder setReverbLevel(float level) {
                    if (level < 0 || level > 1.0f) {
                        throw new IllegalArgumentException("混响级别必须在0.0-1.0之间");
                    }
                    this.reverbLevel = level;
                    return this;
                }

                public Builder setEQSetting(String band, float level) {
                    if (level < -12.0f || level > 12.0f) {
                        throw new IllegalArgumentException("EQ级别必须在-12.0到12.0之间");
                    }
                    this.eqSettings.put(band, level);
                    return this;
                }

                public Effects build() {
                    return new Effects(this);
                }
            }

            @Override
            public String toString() {
                return String.format("回声: %.1f, 混响: %.1f, EQ: %s",
                        echoLevel, reverbLevel, eqSettings.toString());
            }
        }

        // 配置参数类
        public static class Config {
            final VoiceType voiceType;
            final float speed;            // 语速 (0.5-2.0)
            final float pitch;            // 音调 (0.5-1.5)
            final int volume;             // 音量 (0-100)
            final Language language;
            final boolean async;          // 是否异步播放
            final String customVoice;     // 自定义声音路径
            final Emotion emotion;        // 情感参数
            final AudioFormat audioFormat; // 音频格式
            final Style style;            // 语音风格
            final Effects effects;        // 特殊效果

            private Config(Builder builder) {
                this.voiceType = builder.voiceType;
                this.speed = builder.speed;
                this.pitch = builder.pitch;
                this.volume = builder.volume;
                this.language = builder.language;
                this.async = builder.async;
                this.customVoice = builder.customVoice;
                this.emotion = builder.emotion;
                this.audioFormat = builder.audioFormat;
                this.style = builder.style;
                this.effects = builder.effects;
            }
        }

        // 建造者类
        public static class Builder {
            // 默认配置
            private VoiceType voiceType = VoiceType.FEMALE_SOFT;
            private float speed = 1.0f;               // 正常语速
            private float pitch = 1.0f;               // 正常音调
            private int volume = 80;                  // 80% 音量
            private Language language = Language.MANDARIN;
            private boolean async = false;            // 默认同步
            private String customVoice = null;        // 无自定义
            private Emotion emotion = Emotion.NEUTRAL; // 中性情感
            private AudioFormat audioFormat = AudioFormat.MP3; // 默认MP3
            private Style style = Style.CONVERSATIONAL; // 对话风格
            private Effects effects = new Effects.Builder().build(); // 默认无效果

            public Builder setVoiceType(VoiceType voiceType) {
                this.voiceType = voiceType;
                return this;
            }

            public Builder setSpeed(float speed) {
                if (speed < 0.5f || speed > 2.0f) {
                    throw new IllegalArgumentException("语速必须在0.5到2.0之间");
                }
                this.speed = speed;
                return this;
            }

            public Builder setPitch(float pitch) {
                if (pitch < 0.5f || pitch > 1.5f) {
                    throw new IllegalArgumentException("音调必须在0.5到1.5之间");
                }
                this.pitch = pitch;
                return this;
            }

            public Builder setVolume(int volume) {
                if (volume < 0 || volume > 100) {
                    throw new IllegalArgumentException("音量必须在0到100之间");
                }
                this.volume = volume;
                return this;
            }

            public Builder setLanguage(Language language) {
                this.language = language;
                return this;
            }

            public Builder setAsync(boolean async) {
                this.async = async;
                return this;
            }

            public Builder setCustomVoice(String voicePath) {
                this.customVoice = voicePath;
                return this;
            }

            public Builder setEmotion(Emotion emotion) {
                this.emotion = emotion;
                return this;
            }

            public Builder setAudioFormat(AudioFormat format) {
                this.audioFormat = format;
                return this;
            }

            public Builder setStyle(Style style) {
                this.style = style;
                return this;
            }

            public Builder setEffects(Effects effects) {
                this.effects = effects;
                return this;
            }

            public Builder setEffects(Effects.Builder effectsBuilder) {
                this.effects = effectsBuilder.build();
                return this;
            }

            public TTSEngineSdk build() {
                // 验证参数依赖关系
                if (customVoice != null && voiceType != VoiceType.CUSTOM) {
                    throw new IllegalStateException("自定义声音需要设置voiceType为CUSTOM");
                }

                return new TTSEngineSdk(new Config(this));
            }
        }

        private final Config config;
        private boolean isPlaying = false;

        private TTSEngineSdk(Config config) {
            this.config = config;
            initializeEngine();
        }

        private void initializeEngine() {
            System.out.println("初始化TTS引擎...");
            System.out.println("配置: " + configToString());
        }

        public void speak(String text) {
            if (isPlaying) {
                System.out.println("警告: TTS引擎正在播放中,忽略新请求");
                return;
            }

            isPlaying = true;

            if (config.async) {
                new Thread(() -> doSpeak(text)).start();
            } else {
                doSpeak(text);
            }
        }

        public byte[] synthesize(String text) {
            System.out.println("合成音频: " + text);
            System.out.println("使用配置: " + configToString());

            // 模拟音频合成
            int audioLength = (int) (text.length() * 100 / config.speed);
            return new byte[audioLength];
        }

        private void doSpeak(String text) {
            System.out.println("开始播报: " + text);
            System.out.println("使用配置: " + configToString());

            // 模拟语音合成和播放
            try {
                // 根据语速计算播放时间 (每字符0.1秒 * 语速因子)
                long duration = (long) (text.length() * 100 / config.speed);
                Thread.sleep(duration);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }

            System.out.println("播报完成");
            isPlaying = false;
        }

        public String configToString() {
            return String.format(
                    "声音类型: %s, 语速: %.1f, 音调: %.1f, 音量: %d%%, 语言: %s, 异步: %b\n" +
                            "自定义: %s, 情感: %s, 格式: %s, 风格: %s\n" +
                            "效果: %s",
                    config.voiceType,
                    config.speed,
                    config.pitch,
                    config.volume,
                    config.language.getDisplayName(),
                    config.async,
                    config.customVoice != null ? config.customVoice : "无",
                    config.emotion,
                    config.audioFormat.getDisplayName(),
                    config.style,
                    config.effects
            );
        }

        public static Builder builder() {
            return new Builder();
        }
    }

调用的过程:

public class TTSDemo {

        public static void main(String[] args) {
            // 场景1: 儿童故事讲述
            TTSEngineSdk storyTeller = TTSEngineSdk.builder()
                    .setVoiceType(TTSEngineSdk.VoiceType.CHILD)
                    .setSpeed(0.9f)
                    .setPitch(1.2f)
                    .setEmotion(TTSEngineSdk.Emotion.EXCITED)
                    .setStyle(TTSEngineSdk.Style.STORY_TELLING)
                    .setEffects(new TTSEngineSdk.Effects.Builder()
                            .setReverbLevel(0.3f)
                            .build())
                    .build();

            storyTeller.speak("很久很久以前,在一个遥远的王国里...");

            // 场景2: 新闻播报
            TTSEngineSdk newsReader = TTSEngineSdk.builder()
                    .setVoiceType(TTSEngineSdk.VoiceType.MALE_DEEP)
                    .setSpeed(1.1f)
                    .setVolume(90)
                    .setStyle(TTSEngineSdk.Style.NEWS_READING)
                    .setAudioFormat(TTSEngineSdk.AudioFormat.PCM_16BIT)
                    .build();

            newsReader.speak("今日头条:人工智能技术取得重大突破...");

            // 场景3: 机器人语音提示
            TTSEngineSdk robotVoice = TTSEngineSdk.builder()
                    .setVoiceType(TTSEngineSdk.VoiceType.ROBOTIC)
                    .setPitch(0.8f)
                    .setEmotion(TTSEngineSdk.Emotion.CALM)
                    .setEffects(new TTSEngineSdk.Effects.Builder()
                            .setEchoLevel(0.5f)
                            .setEQSetting("bass", 3.0f)
                            .setEQSetting("treble", 2.0f)
                            .build())
                    .build();

            robotVoice.speak("系统启动完成。当前时间:" + new java.util.Date());

            // 场景4: 自定义情感语音合成
            TTSEngineSdk emotionEngine = TTSEngineSdk.builder()
                    .setVoiceType(TTSEngineSdk.VoiceType.FEMALE_SOFT)
                    .setEmotion(TTSEngineSdk.Emotion.SAD)
                    .setStyle(TTSEngineSdk.Style.POETIC)
                    .setLanguage(TTSEngineSdk.Language.JAPANESE)
                    .setAudioFormat(TTSEngineSdk.AudioFormat.WAV)
                    .build();

            byte[] audioData = emotionEngine.synthesize("春はあけぼの。やうやう白くなりゆく山際、少し明かりて、紫だちたる雲の細くたなびきたる。");
            System.out.println("合成音频长度: " + audioData.length + " 字节");

            // 场景5: 商务场景使用
            TTSEngineSdk businessVoice = TTSEngineSdk.builder()
                    .setVoiceType(TTSEngineSdk.VoiceType.MALE_DEEP)
                    .setSpeed(1.0f)
                    .setVolume(85)
                    .setStyle(TTSEngineSdk.Style.BUSINESS)
                    .setLanguage(TTSEngineSdk.Language.ENGLISH_US)
                    .build();

            businessVoice.speak("Good morning, ladies and gentlemen. Let's begin today's quarterly financial review.");

        }


    public static void mainBase(String[] args) {
        // 创建TTS实例
        BasicTTSEngine tts = new BasicTTSEngine();

        // 配置参数 - 需要多个setter调用
        tts.setVoiceType(BasicTTSEngine.VoiceType.CHILD);
        tts.setSpeed(1.2f);
        tts.setPitch(1.1f);
        tts.setVolume(90);
        tts.setLanguage(BasicTTSEngine.Language.ENGLISH_US);
        tts.setAsync(true);
        tts.setCustomVoice("/voices/custom_child.wav");

        // 使用TTS引擎
        tts.speak("Hello, welcome to our basic TTS engine demo!");

        // 创建另一个配置的TTS实例
        // 必须使用全参数构造函数 - 非常难用
        BasicTTSEngine fastTts;
        try {
            fastTts = new BasicTTSEngine(
                    BasicTTSEngine.VoiceType.FEMALE_SOFT,
                    1.8f,
                    1.0f,
                    100,
                    BasicTTSEngine.Language.MANDARIN,
                    false,
                    null
            );
        } catch (Exception e) {
            System.err.println("创建TTS失败: " + e.getMessage());
            return;
        }

        fastTts.speak("这是一个快速公告!");
    }

    }
架构图:

deepseek_mermaid_20250628_a87210.png

deepseek_mermaid_20250628_b64959.png

4.3 代码可读性与使用便捷性

特性建造者模式实现非建造者模式实现
配置过程链式调用,清晰流畅多个setter调用,代码冗长
参数含义每个参数名明确可见参数名在setter中可见
默认值处理自动应用默认值,无需指定所有参数需要手动设置所有参数或使用默认构造函数
可选参数只设置需要的参数必须处理所有参数
参数验证在设置时立即验证在setter或构造函数中验证
配置组合轻松创建不同配置创建不同配置需要多个对象
不可变性对象创建后不可变对象创建后仍可修改

4.4 扩展性与维护性

特性建造者模式实现非建造者模式实现
新增参数只需在Builder中添加新方法需修改构造函数和添加setter
参数约束可在build()方法中添加跨参数验证难以添加跨参数验证
线程安全对象不可变,线程安全对象可变,需要额外同步
代码复用可复用Builder创建不同配置配置过程无法复用
对象状态一致性保证构建完成后对象状态完整一致可能处于不完整状态

通过建造者模式实现的 TTS 引擎 SDK 具有以下优势:

  1. 高度可配置性:支持声音类型、语速、音调、音量、语言、情感、音频格式、风格和特效等多种参数
  2. 参数验证:在构建时进行参数验证,确保配置合法
  3. 配置复用:支持配置模板和配置派生

5.优点

  1. 参数设置灵活:可以按需设置参数,忽略可选参数
  2. 代码可读性强:链式调用清晰表达参数含义
  3. 对象状态一致:保证构建完成的对象处于完整状态
  4. 参数验证集中:在build()方法中统一验证参数有效性
  5. 构建过程可控:可以控制构建步骤和顺序
  6. 扩展性好:新增参数不影响现有代码
  7. 线程安全:构建完成的对象是不可变的

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

建造者模式的核心价值在于解决多参数对象创建问题,这是其他模式无法替代的:

  1. 与工厂模式的区别

    • 工厂关注生产什么,建造者关注如何构建
    • 建造者适合参数多变的场景,工厂适合固定创建
  2. 与原型模式的区别

    • 原型是克隆,建造者是新建
    • 建造者提供参数化控制,原型保持对象一致性

结论

建造者模式是 Android 开发中极其重要的设计模式,尤其在 ARouter 等框架中被广泛应用。通过分离对象的构建过程和表示,它解决了多参数对象创建时的复杂性问题,提供了清晰、灵活且可维护的解决方案

demo地址: github.com/pengcaihua1…