如何将工厂方法模式应用于复杂生成?

摘要:在软件开发中,对象创建是最基础也是最关键的操作之一。随着项目规模的增长,直接使用 new 关键字创建对象会导致代码高度耦合,难以维护和扩展。工厂方法模式正是为了解决这一问题而生的经典设计模式。 什么是工厂方法模式? 工厂方法模式是一种创建型
在软件开发中,对象创建是最基础也是最关键的操作之一。随着项目规模的增长,直接使用 new 关键字创建对象会导致代码高度耦合,难以维护和扩展。工厂方法模式正是为了解决这一问题而生的经典设计模式。 什么是工厂方法模式? 工厂方法模式是一种创建型设计模式,它定义了一个创建对象的接口,但让子类决定实例化哪个类。这种模式将对象的实例化推迟到子类,从而实现了创建逻辑与使用逻辑的分离。 核心概念解析 工厂方法模式包含以下几个关键角色: 产品接口(Product Interface):定义了产品对象的通用接口 具体产品(Concrete Products):实现产品接口的具体类 创建者(Creator):声明工厂方法,返回产品对象 具体创建者(Concrete Creators):重写工厂方法,返回具体产品实例 为什么使用工厂方法模式? 解决紧耦合问题 考虑以下紧耦合的代码: class Car { drive() { console.log('Driving a car'); } } class Truck { drive() { console.log('Driving a truck'); } } // 客户端代码直接依赖具体类 const vehicle1 = new Car(); const vehicle1 = new Truck(); 这种写法的问题在于,客户端代码直接依赖于具体类,一旦需要添加新的车辆类型或修改创建逻辑,就需要在所有使用的地方进行修改。 实现开闭原则 工厂方法模式帮助你的代码遵循开闭原则:对扩展开放,对修改关闭。你可以添加新的产品类型而无需修改现有代码。 工厂方法模式实现 让我们通过一个完整的示例来理解工厂方法模式。 第一步:定义产品接口 interface Vehicle { drive(): void; getInfo(): string; } 第二步:实现具体产品类 class Car implements Vehicle { drive() { console.log('Driving a car'); } getInfo(): string { return 'This is a car with 4 seats'; } } class Truck implements Vehicle { drive() { console.log('Driving a truck'); } getInfo(): string { return 'This is a truck for heavy loads'; } } class Motorcycle implements Vehicle { drive() { console.log('Riding a motorcycle'); } getInfo(): string { return 'This is a motorcycle with 2 wheels'; } } 第三步:创建抽象创建者类 abstract class VehicleFactory { // 工厂方法 public abstract createVehicle(): Vehicle; // 业务逻辑方法 public deliverVehicle(): string { const vehicle = this.createVehicle(); vehicle.drive(); return vehicle.getInfo(); } } 第四步:实现具体创建者类 class CarFactory extends VehicleFactory { public createVehicle(): Vehicle { return new Car(); } } class TruckFactory extends VehicleFactory { public createVehicle(): Vehicle { return new Truck(); } } class MotorcycleFactory extends VehicleFactory { public createVehicle(): Vehicle { return new Motorcycle(); } } 第五步:客户端使用 function clientCode(factory: VehicleFactory) { console.log('Client: Delivery process started...'); const result = factory.deliverVehicle(); console.log(result); } // 使用不同的工厂创建不同的产品 console.log('App: Launched with CarFactory.'); clientCode(new CarFactory()); console.log('\nApp: Launched with TruckFactory.'); clientCode(new TruckFactory()); console.log('\nApp: Launched with MotorcycleFactory.'); clientCode(new MotorcycleFactory()); 高级应用:参数化工厂方法 在某些场景下,你可能希望通过参数来决定创建哪种产品: class UniversalVehicleFactory extends VehicleFactory { constructor(private vehicleType: 'car' | 'truck' | 'motorcycle') { super(); } public createVehicle(): Vehicle { switch (this.vehicleType) { case 'car': return new Car(); case 'truck': return new Truck(); case 'motorcycle': return new Motorcycle(); default: throw new Error('Unknown vehicle type'); } } } // 使用参数化工厂 const carFactory = new UniversalVehicleFactory('car'); clientCode(carFactory); 结合 TypeScript 的高级特性 使用泛型增强类型安全 abstract class GenericVehicleFactory<T extends Vehicle> { public abstract createVehicle(): T; public deliverVehicle(): string { const vehicle = this.createVehicle(); vehicle.drive(); return vehicle.getInfo(); } } class GenericCarFactory extends GenericVehicleFactory<Car> { public createVehicle(): Car { return new Car(); } } 利用枚举提高代码可读性 enum VehicleType { CAR = 'car', TRUCK = 'truck', MOTORCYCLE = 'motorcycle' } class EnumVehicleFactory extends VehicleFactory { constructor(private type: VehicleType) { super(); } public createVehicle(): Vehicle { switch (this.type) { case VehicleType.CAR: return new Car(); case VehicleType.TRUCK: return new Truck(); case VehicleType.MOTORCYCLE: return new Motorcycle(); } } } 实际应用场景 场景一:UI 组件库 在不同平台(Web、Mobile、Desktop)上创建相同功能的 UI 组件: interface Button { render(): void; onClick(callback: () => void): void; } class WebButton implements Button { render() { console.log('Rendering web button'); } onClick(callback: () => void) { /* web 实现 */ } } class MobileButton implements Button { render() { console.log('Rendering mobile button'); } onClick(callback: () => void) { /* mobile 实现 */ } } abstract class UIFactory { abstract createButton(): Button; abstract createModal(): Modal; // 假设有 Modal 接口 } class WebUIFactory extends UIFactory { createButton(): Button { return new WebButton(); } createModal(): Modal { return new WebModal(); } } 场景二:数据库连接工厂 interface DatabaseConnection { connect(): void; query(sql: string): any[]; } class MySQLConnection implements DatabaseConnection { connect() { console.log('Connecting to MySQL'); } query(sql: string) { return []; } } class PostgreSQLConnection implements DatabaseConnection { connect() { console.log('Connecting to PostgreSQL'); } query(sql: string) { return []; } } abstract class DatabaseFactory { abstract createConnection(): DatabaseConnection; } class MySQLFactory extends DatabaseFactory { createConnection(): DatabaseConnection { return new MySQLConnection(); } } 真实案例 TypeORM 的 Driver 工厂 使用工厂方法来根据数据库类型创建不同驱动实例。 export class DriverFactory { create(connection: Connection): Driver { switch (connection.options.type) { case "mysql": return new MysqlDriver(connection) case "postgres": return new PostgresDriver(connection) case "sqlite": return new SqliteDriver(connection) // ... } } } 基于数据库类型返回不同的 Driver 子类 统一入口 create() 使用者不关心具体 driver,只依赖 Driver 接口 工厂方法模式的优势与局限 优势 避免紧耦合:客户端代码只依赖于抽象接口,不依赖于具体类 单一职责原则:将创建逻辑集中在一个地方,便于维护 开闭原则:添加新产品类型时无需修改现有代码 代码可测试性:可以轻松创建模拟对象进行单元测试 局限 代码复杂度增加:需要引入多个额外的类和接口 可能过度设计:对于简单场景,直接实例化可能更合适 实践建议 适时使用:当预计会有多种类似产品,或创建逻辑比较复杂时使用 结合依赖注入:在大型应用中,结合依赖注入容器使用效果更佳 文档化工厂意图:明确每个工厂的职责和适用场景 考虑简单工厂:如果产品类型不多,可以考虑使用简单工厂模式 总结 工厂方法模式是开发中极其重要的设计模式,它通过将对象的创建与使用分离,显著提高了代码的灵活性和可维护性。虽然会引入一定的复杂度,但在面对变化和扩展需求时,这种前期的投入会带来长期的收益。 掌握工厂方法模式,意味着你不仅学会了如何创建对象,更学会了如何以更加优雅和可持续的方式构建软件系统。在实际项目中,工厂方法模式能够帮助你构建出更加健壮和可扩展的应用程序。