资讯动态

03-A-抽象工厂模式详解

发布时间:2026/10/17 13:40:41 来源:尧图企业网站定制
03-A-抽象工厂模式详解关键词抽象工厂、产品族、配套对象、数据库方言、扩展性抽象工厂模式和工厂方法很像但关注点是一整套配套产品。比如你要换数据库不只是换一个连接池而是连接、分页、类型处理都要一起换。抽象工厂就是把这些配套产品的创建统一起来。 目录03-A-抽象工厂模式详解 术语速查表一、不用抽象工厂的问题二、模式结构三、业务场景多数据库方言支持四、与工厂方法的对比五、框架中的同款实现六、常见误用误用把工厂方法硬说成抽象工厂误用产品种类频繁变化七、总结 术语速查表术语人话解释产品族同一系列、配套使用的产品集合抽象工厂定义创建一系列产品的方法具体工厂创建某个产品族的工厂抽象产品某类产品的接口具体产品某个产品族里的具体实现一、不用抽象工厂的问题假设电商系统需要支持多种数据库MySQL 和 PostgreSQL。每种数据库都需要连接配置ConnectionConfig分页方言PaginationDialect类型转换器TypeConverter。不用抽象工厂代码可能这样写publicclassDbConfigFactory{publicObjectcreate(StringdbType,Stringname){if(mysql.equals(dbType)){if(connection.equals(name))returnnewMySQLConnectionConfig();if(pagination.equals(name))returnnewMySQLPaginationDialect();if(typeConverter.equals(name))returnnewMySQLTypeConverter();}elseif(postgres.equals(dbType)){// ...}thrownewIllegalArgumentException(unsupported);}}痛点不同数据库的配套对象散落在各处新增数据库类型要改很多地方类型转换是Object返回失去编译期检查。二、模式结构«abstract»DbFactorycreateConnectionConfig()createPaginationDialect()createTypeConverter()MySQLDbFactoryPostgresDbFactoryConnectionConfigPaginationDialectTypeConverter三、业务场景多数据库方言支持下面是整个业务场景的调用流程图Main 入口QueryExecutor 使用方DbFactory 抽象工厂MySQLDbFactory 具体工厂PostgresDbFactory 具体工厂MySQLConnectionConfigMySQLPaginationDialectMySQLTypeConverterPostgresConnectionConfigPostgresPaginationDialectPostgresTypeConverterdriver: com.mysql.cj.jdbc.DriverlimitSql: SELECT * FROM t_order LIMIT 0, 10type: BIGINTdriver: org.postgresql.DriverlimitSql: SELECT * FROM t_order LIMIT 10 OFFSET 0type: BIGINT下面把抽象产品、MySQL 产品族、PostgreSQL 产品族、抽象工厂与具体工厂、使用方整合到一个完整的 Java 文件中可直接编译运行。// 抽象产品interfaceConnectionConfig{StringgetDriverClass();StringgetUrlTemplate();}interfacePaginationDialect{StringbuildLimitSql(Stringsql,intoffset,intlimit);}interfaceTypeConverter{StringtoDbType(StringjavaType);}// MySQL 产品族classMySQLConnectionConfigimplementsConnectionConfig{OverridepublicStringgetDriverClass(){returncom.mysql.cj.jdbc.Driver;}OverridepublicStringgetUrlTemplate(){returnjdbc:mysql://${host}:${port}/${db};}}classMySQLPaginationDialectimplementsPaginationDialect{OverridepublicStringbuildLimitSql(Stringsql,intoffset,intlimit){returnsql LIMIT offset, limit;}}classMySQLTypeConverterimplementsTypeConverter{OverridepublicStringtoDbType(StringjavaType){returnBIGINT.equals(javaType)?BIGINT:VARCHAR;}}// PostgreSQL 产品族classPostgresConnectionConfigimplementsConnectionConfig{OverridepublicStringgetDriverClass(){returnorg.postgresql.Driver;}OverridepublicStringgetUrlTemplate(){returnjdbc:postgresql://${host}:${port}/${db};}}classPostgresPaginationDialectimplementsPaginationDialect{OverridepublicStringbuildLimitSql(Stringsql,intoffset,intlimit){returnsql LIMIT limit OFFSET offset;}}classPostgresTypeConverterimplementsTypeConverter{OverridepublicStringtoDbType(StringjavaType){returnBIGINT.equals(javaType)?BIGINT:TEXT;}}// 抽象工厂与具体工厂interfaceDbFactory{ConnectionConfigcreateConnectionConfig();PaginationDialectcreatePaginationDialect();TypeConvertercreateTypeConverter();}classMySQLDbFactoryimplementsDbFactory{OverridepublicConnectionConfigcreateConnectionConfig(){returnnewMySQLConnectionConfig();}OverridepublicPaginationDialectcreatePaginationDialect(){returnnewMySQLPaginationDialect();}OverridepublicTypeConvertercreateTypeConverter(){returnnewMySQLTypeConverter();}}classPostgresDbFactoryimplementsDbFactory{OverridepublicConnectionConfigcreateConnectionConfig(){returnnewPostgresConnectionConfig();}OverridepublicPaginationDialectcreatePaginationDialect(){returnnewPostgresPaginationDialect();}OverridepublicTypeConvertercreateTypeConverter(){returnnewPostgresTypeConverter();}}// 使用方classQueryExecutor{privatefinalDbFactoryfactory;publicQueryExecutor(DbFactoryfactory){this.factoryfactory;}publicvoidexecute(Stringsql,intoffset,intlimit){ConnectionConfigconnectionConfigfactory.createConnectionConfig();PaginationDialectdialectfactory.createPaginationDialect();TypeConverterconverterfactory.createTypeConverter();System.out.println(driver: connectionConfig.getDriverClass());System.out.println(limitSql: dialect.buildLimitSql(sql,offset,limit));System.out.println(type: converter.toDbType(Long));}}// 运行入口publicclassMain{publicstaticvoidmain(String[]args){System.out.println( MySQL );QueryExecutormysqlExecutornewQueryExecutor(newMySQLDbFactory());mysqlExecutor.execute(SELECT * FROM t_order,0,10);System.out.println();System.out.println( PostgreSQL );QueryExecutorpostgresExecutornewQueryExecutor(newPostgresDbFactory());postgresExecutor.execute(SELECT * FROM t_order,0,10);}}运行结果 MySQL driver: com.mysql.cj.jdbc.Driver limitSql: SELECT * FROM t_order LIMIT 0, 10 type: BIGINT PostgreSQL driver: org.postgresql.Driver limitSql: SELECT * FROM t_order LIMIT 10 OFFSET 0 type: BIGINT可以看到使用方QueryExecutor完全不感知具体数据库类型只依赖抽象的DbFactory。切换数据库时只需替换工厂实现配套产品连接、分页、类型转换会自动保持一致。从图中可以清晰看到Main只负责选择具体工厂QueryExecutor只依赖抽象的DbFactory而每个具体工厂负责创建自己产品族里的配套对象连接、分页、类型转换最终输出对应的方言结果。。四、与工厂方法的对比维度工厂方法抽象工厂粒度创建一个产品创建一族配套产品扩展新产品族容易容易新增一个具体工厂扩展新产品种类容易困难所有工厂接口都要改典型场景支付渠道数据库方言、UI 主题五、框架中的同款实现框架应用说明SpringApplicationContext创建 Bean不同环境dev/test/prod创建不同的 BeanJDBCConnection、Statement、ResultSet不同数据库驱动提供不同的实现MyBatisSqlSessionFactory 多数据库方言XML 解析出的对象由不同方言适配六、常见误用误用把工厂方法硬说成抽象工厂错误判断标准如果你只是根据类型创建不同对象那是工厂方法如果你创建的是一整套配套对象那才是抽象工厂。误用产品种类频繁变化如果每次需求都要在产品族里新增一种产品比如新增一个IndexDialect所有具体工厂都要改。这种情况下抽象工厂会变得很痛苦。七、总结抽象工厂的核心创建一族配套产品保证这些产品互相兼容。适用场景数据库方言、UI 主题、多环境配置。优点产品族切换方便使用方只依赖抽象。代价新增产品种类要改抽象工厂和所有具体工厂。与工厂方法的关系抽象工厂可以看成多个工厂方法的组合关注从一个产品升级到一族产品。上一篇02-A-工厂方法模式详解下一篇04-A-建造者模式详解

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑