假设我们正在开发一个电商系统,需要支持多种数据库,比如 MySQL 和 PostgreSQL。最初,我们的代码可能到处都是 if...else 判断,根据配置选择不同的数据库连接、SQL 语句等等。这种方式不仅代码臃肿,而且可维护性极差。每增加一种数据库,都要修改大量的代码,测试压力巨大。这种硬编码的方式显然无法应对未来的变化。
这时,抽象工厂模式就派上用场了。它可以帮助我们创建一个产品家族,而无需指定具体的类。
抽象工厂模式深度剖析
抽象工厂模式属于创建型设计模式,它提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。它的核心思想是将一组产品的创建封装到一个工厂中,客户端通过工厂来获取产品,而无需关心产品的具体实现。
模式结构
- 抽象工厂 (Abstract Factory):声明一组用于创建抽象产品的接口。
- 具体工厂 (Concrete Factory):实现抽象工厂接口,创建具体的产品对象。
- 抽象产品 (Abstract Product):声明产品对象的接口。
- 具体产品 (Concrete Product):实现抽象产品接口,定义具体的产品。
- 客户端 (Client):使用抽象工厂和抽象产品提供的接口。
工作原理
- 客户端通过具体工厂来创建产品家族。
- 具体工厂根据客户端的需求,创建相应的具体产品。
- 客户端使用抽象产品接口来操作产品,而无需关心产品的具体实现。
代码实战:MySQL 和 PostgreSQL 的支持
现在我们用 Java 代码来实现一个简单的示例,支持 MySQL 和 PostgreSQL 数据库。
// 抽象产品:数据库连接接口
interface Connection {
void connect();
}
// 具体产品:MySQL 连接
class MySQLConnection implements Connection {
@Override
public void connect() {
System.out.println("Connecting to MySQL...");
}
}
// 具体产品:PostgreSQL 连接
class PostgreSQLConnection implements Connection {
@Override
public void connect() {
System.out.println("Connecting to PostgreSQL...");
}
}
// 抽象产品:数据库命令接口
interface Command {
void execute(String sql);
}
// 具体产品:MySQL 命令
class MySQLCommand implements Command {
@Override
public void execute(String sql) {
System.out.println("Executing MySQL command: " + sql);
}
}
// 具体产品:PostgreSQL 命令
class PostgreSQLCommand implements Command {
@Override
public void execute(String sql) {
System.out.println("Executing PostgreSQL command: " + sql);
}
}
// 抽象工厂:数据库工厂接口
interface DatabaseFactory {
Connection createConnection();
Command createCommand();
}
// 具体工厂:MySQL 工厂
class MySQLFactory implements DatabaseFactory {
@Override
public Connection createConnection() {
return new MySQLConnection();
}
@Override
public Command createCommand() {
return new MySQLCommand();
}
}
// 具体工厂:PostgreSQL 工厂
class PostgreSQLFactory implements DatabaseFactory {
@Override
public Connection createConnection() {
return new PostgreSQLConnection();
}
@Override
public Command createCommand() {
return new PostgreSQLCommand();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 使用 MySQL 工厂
DatabaseFactory mysqlFactory = new MySQLFactory();
Connection mysqlConnection = mysqlFactory.createConnection();
Command mysqlCommand = mysqlFactory.createCommand();
mysqlConnection.connect();
mysqlCommand.execute("SELECT * FROM users");
// 使用 PostgreSQL 工厂
DatabaseFactory postgresqlFactory = new PostgreSQLFactory();
Connection postgresqlConnection = postgresqlFactory.createConnection();
Command postgresqlCommand = postgresqlFactory.createCommand();
postgresqlConnection.connect();
postgresqlCommand.execute("SELECT * FROM users");
}
}
配置方案 (示例:Spring Boot + 多数据源)
在 Spring Boot 项目中,可以结合 @ConfigurationProperties 和 @Bean 注解,将数据库配置信息注入到工厂类中。例如:
@Configuration
public class DatabaseConfig {
@ConfigurationProperties(prefix = "mysql")
@Bean
public DataSource mysqlDataSource() { // 使用 HikariCP 连接池
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@ConfigurationProperties(prefix = "postgresql")
@Bean
public DataSource postgresqlDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean
@Primary // 优先使用 MySQL
public JdbcTemplate jdbcTemplate(DataSource mysqlDataSource) {
return new JdbcTemplate(mysqlDataSource);
}
@Bean(name = "postgresqlJdbcTemplate")
public JdbcTemplate postgresqlJdbcTemplate(DataSource postgresqlDataSource) {
return new JdbcTemplate(postgresqlDataSource);
}
}
在 application.yml 或 application.properties 中配置数据库连接信息:
mysql:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
postgresql:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: postgres
password: password
实战避坑经验总结
- 过度设计:不要为了使用抽象工厂模式而使用,只有在确实需要创建产品家族时才考虑它。如果只有单个产品,简单工厂模式可能更合适。
- 接口膨胀:抽象工厂的接口应该足够通用,但也需要避免过于庞大,导致实现类难以维护。可以考虑使用多个小的抽象工厂接口,然后组合它们。
- 依赖注入:在大型项目中,可以使用依赖注入框架(如 Spring)来管理工厂类的创建和依赖关系,避免手动创建工厂对象。
- 扩展性考虑:在使用抽象工厂模式时,要考虑到未来的扩展性。如果需要增加新的产品,应该尽量避免修改现有的代码,而是通过新增具体工厂和具体产品来实现。
- 并发安全:在多线程环境下,需要注意工厂类的并发安全问题,避免多个线程同时创建产品对象导致数据不一致。可以使用线程安全的集合或者同步机制来保证并发安全。
通过使用抽象工厂模式,我们可以将数据库的创建和使用解耦,使得代码更加灵活、可维护和可扩展。无论未来需要支持多少种数据库,我们都可以通过新增具体工厂来实现,而无需修改现有的代码。这大大提高了系统的稳定性和可维护性。
冠军资讯
键盘上的咸鱼