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图
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("这是一个快速公告!");
}
}
架构图:
4.3 代码可读性与使用便捷性
| 特性 | 建造者模式实现 | 非建造者模式实现 |
|---|---|---|
| 配置过程 | 链式调用,清晰流畅 | 多个setter调用,代码冗长 |
| 参数含义 | 每个参数名明确可见 | 参数名在setter中可见 |
| 默认值处理 | 自动应用默认值,无需指定所有参数 | 需要手动设置所有参数或使用默认构造函数 |
| 可选参数 | 只设置需要的参数 | 必须处理所有参数 |
| 参数验证 | 在设置时立即验证 | 在setter或构造函数中验证 |
| 配置组合 | 轻松创建不同配置 | 创建不同配置需要多个对象 |
| 不可变性 | 对象创建后不可变 | 对象创建后仍可修改 |
4.4 扩展性与维护性
| 特性 | 建造者模式实现 | 非建造者模式实现 |
|---|---|---|
| 新增参数 | 只需在Builder中添加新方法 | 需修改构造函数和添加setter |
| 参数约束 | 可在build()方法中添加跨参数验证 | 难以添加跨参数验证 |
| 线程安全 | 对象不可变,线程安全 | 对象可变,需要额外同步 |
| 代码复用 | 可复用Builder创建不同配置 | 配置过程无法复用 |
| 对象状态一致性 | 保证构建完成后对象状态完整一致 | 可能处于不完整状态 |
通过建造者模式实现的 TTS 引擎 SDK 具有以下优势:
- 高度可配置性:支持声音类型、语速、音调、音量、语言、情感、音频格式、风格和特效等多种参数
- 参数验证:在构建时进行参数验证,确保配置合法
- 配置复用:支持配置模板和配置派生
5.优点
- 参数设置灵活:可以按需设置参数,忽略可选参数
- 代码可读性强:链式调用清晰表达参数含义
- 对象状态一致:保证构建完成的对象处于完整状态
- 参数验证集中:在build()方法中统一验证参数有效性
- 构建过程可控:可以控制构建步骤和顺序
- 扩展性好:新增参数不影响现有代码
- 线程安全:构建完成的对象是不可变的
6.和别的设计模式的区别
建造者模式的核心价值在于解决多参数对象创建问题,这是其他模式无法替代的:
-
与工厂模式的区别:
- 工厂关注生产什么,建造者关注如何构建
- 建造者适合参数多变的场景,工厂适合固定创建
-
与原型模式的区别:
- 原型是克隆,建造者是新建
- 建造者提供参数化控制,原型保持对象一致性
结论
建造者模式是 Android 开发中极其重要的设计模式,尤其在 ARouter 等框架中被广泛应用。通过分离对象的构建过程和表示,它解决了多参数对象创建时的复杂性问题,提供了清晰、灵活且可维护的解决方案
demo地址: github.com/pengcaihua1…