一、为什么需要工厂模式?

想象一下你正在开发一个外卖APP,里面需要创建各种不同类型的餐厅对象:中餐馆、西餐厅、日料店等等。如果每次都要用new关键字直接创建,代码会变成这样:

// 技术栈:Dart
ChineseRestaurant restaurant1 = new ChineseRestaurant();
WesternRestaurant restaurant2 = new WesternRestaurant();
JapaneseRestaurant restaurant3 = new JapaneseRestaurant();

这样写有三个明显问题:

  1. 创建逻辑散落在代码各处,难以统一管理
  2. 如果要修改创建方式,需要改动所有创建代码
  3. 客户端代码需要知道所有具体类,耦合度太高

工厂模式就像个"对象生产车间",把对象的创建过程封装起来,让调用方不需要关心具体实现细节。这特别适合以下场景:

  • 对象创建过程比较复杂
  • 需要根据不同条件创建不同对象
  • 希望隐藏对象的具体实现

二、简单工厂模式实战

我们先来看最简单的实现方式:

// 技术栈:Dart
// 餐厅抽象类
abstract class Restaurant {
  void serveFood();
}

// 具体餐厅类
class ChineseRestaurant implements Restaurant {
  @override
  void serveFood() {
    print('提供宫保鸡丁和米饭');
  }
}

class WesternRestaurant implements Restaurant {
  @override
  void serveFood() {
    print('提供牛排和红酒');
  }
}

// 简单工厂类
class RestaurantFactory {
  static Restaurant createRestaurant(String type) {
    switch (type) {
      case 'chinese':
        return ChineseRestaurant();
      case 'western':
        return WesternRestaurant();
      default:
        throw Exception('不支持的餐厅类型');
    }
  }
}

// 使用示例
void main() {
  // 客户端只需要知道工厂类和抽象类型
  Restaurant restaurant = RestaurantFactory.createRestaurant('chinese');
  restaurant.serveFood(); // 输出:提供宫保鸡丁和米饭
}

这个例子展示了工厂模式的核心思想:

  1. 定义统一的接口(Restaurant)
  2. 把创建逻辑集中到工厂类中
  3. 客户端通过工厂获取实例,而不直接new具体类

三、工厂方法模式进阶

当餐厅类型越来越多时,简单工厂的switch-case会变得臃肿。这时可以用工厂方法模式:

// 技术栈:Dart
// 抽象工厂
abstract class RestaurantFactory {
  Restaurant createRestaurant();
  
  // 可以添加一些默认实现
  void prepareIngredients() {
    print('准备基础食材...');
  }
}

// 具体工厂
class ChineseRestaurantFactory implements RestaurantFactory {
  @override
  Restaurant createRestaurant() {
    return ChineseRestaurant();
  }
  
  @override
  void prepareIngredients() {
    super.prepareIngredients();
    print('特别准备酱油和生姜');
  }
}

// 使用示例
void main() {
  RestaurantFactory factory = ChineseRestaurantFactory();
  factory.prepareIngredients();
  Restaurant restaurant = factory.createRestaurant();
  restaurant.serveFood();
}

工厂方法模式的特点:

  1. 每个具体类对应一个具体工厂
  2. 可以重写工厂的准备工作
  3. 符合开闭原则,新增类型时不需要修改已有代码

四、抽象工厂模式实战

当需要创建一系列相关对象时,抽象工厂更有优势。比如我们的餐厅需要配套的餐具:

// 技术栈:Dart
// 餐具抽象
abstract class Tableware {
  String getMaterial();
}

// 餐厅抽象工厂
abstract class RestaurantKit {
  Restaurant createRestaurant();
  Tableware createTableware();
}

// 中式实现
class ChineseRestaurantKit implements RestaurantKit {
  @override
  Restaurant createRestaurant() => ChineseRestaurant();
  
  @override
  Tableware createTableware() => ChineseTableware();
}

class ChineseTableware implements Tableware {
  @override
  String getMaterial() => '瓷器';
}

// 使用示例
void main() {
  RestaurantKit kit = ChineseRestaurantKit();
  Restaurant restaurant = kit.createRestaurant();
  Tableware tableware = kit.createTableware();
  
  print('使用${tableware.getMaterial()}餐具'); // 输出:使用瓷器餐具
  restaurant.serveFood();
}

抽象工厂适合:

  1. 需要确保创建的对象是兼容的
  2. 系统需要多个产品族
  3. 产品之间有明确的关联关系

五、工厂模式的应用场景

工厂模式特别适合以下开发场景:

  1. 跨平台UI开发 比如Flutter中,根据不同平台创建不同的按钮样式:
// 技术栈:Dart
Button createButton(Platform platform) {
  if (platform == Platform.android) {
    return MaterialButton();
  } else if (platform == Platform.ios) {
    return CupertinoButton();
  }
}
  1. 数据库访问 创建不同类型的数据库连接:
// 技术栈:Dart
DatabaseConnection createConnection(DatabaseType type) {
  switch (type) {
    case DatabaseType.mysql:
      return MySqlConnection();
    case DatabaseType.postgres:
      return PostgresConnection();
  }
}
  1. 插件系统 动态加载和创建插件实例:
// 技术栈:Dart
Plugin createPlugin(String pluginName) {
  // 通过反射动态创建插件实例
}

六、技术优缺点分析

优点:

  1. 解耦:客户端和具体实现解耦
  2. 可扩展:新增类型很方便
  3. 统一管理:创建逻辑集中处理
  4. 可测试:便于mock对象

缺点:

  1. 增加复杂度:需要多写一些类
  2. 需要设计:前期需要合理规划层次
  3. 过度设计:简单场景可能不需要

注意事项:

  1. 不要滥用,简单对象直接new就行
  2. 考虑使用依赖注入框架替代手动实现
  3. 工厂类命名要清晰,比如XXXFactory
  4. 考虑线程安全问题(Dart中通常不需要)

七、总结

工厂模式是解决对象创建难题的利器,特别是当你的代码中频繁出现new关键字时,就该考虑使用工厂模式了。在Dart中实现工厂模式有几点建议:

  1. 优先使用工厂构造函数 Dart支持特殊的工厂构造函数语法:
class Restaurant {
  factory Restaurant.fromType(String type) {
    switch (type) {
      case 'chinese': return ChineseRestaurant();
      default: throw Exception('不支持的餐厅类型');
    }
  }
}
  1. 考虑使用顶层函数 Dart中函数是一等公民,简单的工厂可以直接用函数:
Restaurant createRestaurant(String type) {
  // 工厂逻辑
}
  1. 结合Dart的其他特性 比如可以使用扩展方法增强已有类的工厂能力:
extension RestaurantExtension on String {
  Restaurant toRestaurant() {
    switch (this) {
      case 'chinese': return ChineseRestaurant();
      default: throw Exception('不支持的餐厅类型');
    }
  }
}

// 使用
var restaurant = 'chinese'.toRestaurant();

记住,设计模式不是银弹,关键是理解其思想。在实际开发中,你可以根据项目规模灵活调整实现方式,找到最适合的方案。

工厂模式就像是一个好厨师,它知道每种食材应该怎么处理,而你只需要告诉它"我要一份中餐",它就会为你准备好一切。学会合理使用工厂模式,能让你的代码更加优雅、更易维护。