设计模式与配置化:工厂模式创建配置对象、策略模式选择配置、建造者构建配置
说实话,做后端开发这么多年,我见过太多因为配置管理混乱导致的线上事故。有的项目把配置散落在代码的各个角落,改一个参数要翻遍十几个文件;有的项目配置对象初始化逻辑复杂到没人敢动。今天咱们就聊聊怎么用三个经典设计模式,把配置这件事做得既优雅又灵活。
为什么需要配置化?
先说说我的一个真实经历。几年前我接手过一个支付网关项目,里面光支付渠道的配置就有三十多个字段:超时时间、重试次数、回调地址、签名算法……每个渠道的配置还不一样。最要命的是,这些配置对象是通过一个巨大的 if-else 工厂方法创建的,每次新增渠道都要改那个方法,改完还要担心影响其他渠道。
你想想看,这种场景下,如果能把配置的创建、选择和构建过程拆分开,每个环节用最合适的模式去处理,是不是就清爽多了?
整体思路:三个模式各司其职
咱们先画一张图,看看这三个模式是怎么配合的:
这张图其实已经说得很清楚了。工厂模式负责「造出来」,策略模式负责「选哪个」,建造者模式负责「怎么拼」。咱们一个一个来看。
工厂模式:创建配置对象
工厂模式在这里的作用,说白了就是帮你把 new 对象的逻辑藏起来。我习惯用简单工厂或者工厂方法,具体看配置类型的数量。
举个例子,假设我们有数据库配置、缓存配置、消息队列配置三种:
// 配置接口
public interface Config {
void validate();
}
// 数据库配置
public class DatabaseConfig implements Config {
private String url;
private String username;
private String password;
private int maxConnections;
@Override
public void validate() {
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("数据库URL不能为空");
}
}
// getter/setter 省略
}
// 缓存配置
public class CacheConfig implements Config {
private String host;
private int port;
private int expireSeconds;
@Override
public void validate() {
if (host == null || host.isEmpty()) {
throw new IllegalArgumentException("缓存主机地址不能为空");
}
}
// getter/setter 省略
}
// 配置工厂
public class ConfigFactory {
public static Config createConfig(String type) {
switch (type) {
case "database":
return new DatabaseConfig();
case "cache":
return new CacheConfig();
case "mq":
return new MqConfig();
default:
throw new IllegalArgumentException("未知配置类型: " + type);
}
}
}
策略模式:选择配置
策略模式解决的是「运行时动态选择」的问题。我记得之前做一个多租户系统,每个租户的配置策略都不一样——有的用默认配置,有的用自定义配置,还有的需要从远程配置中心拉取。
用策略模式来处理,简直不要太合适:
// 配置选择策略接口
public interface ConfigSelectionStrategy {
Config select(String configType);
}
// 本地配置策略
public class LocalConfigStrategy implements ConfigSelectionStrategy {
@Override
public Config select(String configType) {
// 从本地文件或数据库读取配置
return ConfigFactory.createConfig(configType);
}
}
// 远程配置策略
public class RemoteConfigStrategy implements ConfigSelectionStrategy {
@Override
public Config select(String configType) {
// 从配置中心拉取(如 Apollo、Nacos)
String remoteConfig = fetchFromConfigCenter(configType);
return deserializeConfig(remoteConfig, configType);
}
}
// 策略上下文
public class ConfigSelector {
private ConfigSelectionStrategy strategy;
public void setStrategy(ConfigSelectionStrategy strategy) {
this.strategy = strategy;
}
public Config getConfig(String configType) {
return strategy.select(configType);
}
}
建造者模式:构建配置
建造者模式是处理复杂配置的利器。你想想看,一个配置对象可能有十几个字段,有些是必填的,有些是选填的,有些还有默认值。如果用构造方法或者 setter 去设置,代码会变得又臭又长。
建造者模式可以让你像搭积木一样构建配置:
// 数据库配置的建造者
public class DatabaseConfigBuilder {
private String url;
private String username;
private String password;
private int maxConnections = 10; // 默认值
private int connectionTimeout = 5000; // 默认值
private boolean useSSL = false; // 默认值
public DatabaseConfigBuilder setUrl(String url) {
this.url = url;
return this;
}
public DatabaseConfigBuilder setUsername(String username) {
this.username = username;
return this;
}
public DatabaseConfigBuilder setPassword(String password) {
this.password = password;
return this;
}
public DatabaseConfigBuilder setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
return this;
}
public DatabaseConfigBuilder setConnectionTimeout(int timeout) {
this.connectionTimeout = timeout;
return this;
}
public DatabaseConfigBuilder setUseSSL(boolean useSSL) {
this.useSSL = useSSL;
return this;
}
public DatabaseConfig build() {
DatabaseConfig config = new DatabaseConfig();
config.setUrl(this.url);
config.setUsername(this.username);
config.setPassword(this.password);
config.setMaxConnections(this.maxConnections);
config.setConnectionTimeout(this.connectionTimeout);
config.setUseSSL(this.useSSL);
config.validate(); // 构建时自动校验
return config;
}
}
// 使用示例
DatabaseConfig config = new DatabaseConfigBuilder()
.setUrl("jdbc:mysql://localhost:3306/mydb")
.setUsername("admin")
.setPassword("123456")
.setMaxConnections(20)
.setUseSSL(true)
.build();
三者组合:完整的配置管理方案
把三个模式组合起来,就形成了一个完整的配置管理闭环。我通常的做法是这样的:
// 配置管理器
public class ConfigManager {
private ConfigSelector selector;
public ConfigManager() {
this.selector = new ConfigSelector();
// 根据环境选择策略
String env = System.getProperty("app.env", "local");
if ("local".equals(env)) {
selector.setStrategy(new LocalConfigStrategy());
} else {
selector.setStrategy(new RemoteConfigStrategy());
}
}
public <T extends Config> T getConfig(String configType, ConfigBuilder<T> builder) {
// 1. 策略模式选择配置源
Config rawConfig = selector.getConfig(configType);
// 2. 建造者模式构建完整配置
return builder.fromRawConfig(rawConfig).build();
}
}
// 使用
ConfigManager manager = new ConfigManager();
DatabaseConfig dbConfig = manager.getConfig("database", new DatabaseConfigBuilder());
你看,客户端代码只需要两行就能拿到一个完整、校验过的配置对象。至于配置是从本地读还是从远程拉,配置对象是怎么一步步构建出来的,这些细节都被封装在了内部。
总结
这三个模式组合起来,解决的是配置管理中的三个核心问题:
- 工厂模式:解决「怎么创建」的问题,把 new 的逻辑集中管理
- 策略模式:解决「选哪个」的问题,支持运行时动态切换
- 建造者模式:解决「怎么拼」的问题,让复杂对象的构建变得清晰
我个人觉得,这种组合方式最大的价值在于:它让配置的创建、选择和构建三个环节解耦了。每个环节都可以独立变化,互不影响。比如你想新增一个配置类型,只需要在工厂里加一个 case;想切换配置来源,只需要换一个策略;想调整配置的默认值,只需要改建造者。
嗯,这就是我在多个项目中沉淀下来的配置管理方案。希望能给你一些启发。