建造者模式 - Flutter中的乐高大师,优雅组装复杂UI组件!

发布于:2025-06-30 ⋅ 阅读:(18) ⋅ 点赞:(0)

痛点场景:复杂的对话框配置

假设你需要创建一个多功能对话框:

CustomDialog(
  title: '警告',
  content: '确定要删除吗?',
  titleStyle: TextStyle(fontSize: 20, color: Colors.red),
  contentStyle: TextStyle(fontSize: 16),
  backgroundColor: Colors.grey[200],
  barrierDismissible: false,
  positiveButton: DialogButton(
    text: '确认',
    color: Colors.red,
    onPressed: () => print('Confirmed'),
  ),
  negativeButton: DialogButton(
    text: '取消',
    color: Colors.grey,
    onPressed: () => print('Cancelled'),
  ),
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(10),
  ),
  // 还有10个参数...
)

问题爆发点:

  • 🚨 构造函数参数爆炸(难以维护和使用)
  • 💥 必须记住参数顺序
  • 🔥 可选参数处理困难(需要传入大量null)
  • 🌋 相似组件创建需要重复代码

建造者模式解决方案

核心思想: 将复杂对象的构建与其表示分离,使同样的构建过程可以创建不同的表示。

四个关键角色:

  1. 产品(Product): 最终要构建的复杂对象(如对话框)
  2. 抽象建造者(Builder): 定义构建步骤的接口
  3. 具体建造者(ConcreteBuilder): 实现构建步骤
  4. 指挥者(Director): 控制构建过程(Flutter中常可省略)

Flutter对话框建造者实现

1. 定义产品类
class DialogConfig {
  String? title;
  String? content;
  TextStyle? titleStyle;
  TextStyle? contentStyle;
  Color? backgroundColor;
  bool barrierDismissible = true;
  DialogButton? positiveButton;
  DialogButton? negativeButton;
  ShapeBorder? shape;
  // 其他配置项...
}

class DialogButton {
  final String text;
  final Color color;
  final VoidCallback onPressed;

  DialogButton({
    required this.text,
    required this.color,
    required this.onPressed,
  });
}
2. 创建建造者类
class DialogBuilder {
  final DialogConfig _config = DialogConfig();

  // 设置标题
  DialogBuilder title(String title, {TextStyle? style}) {
    _config.title = title;
    _config.titleStyle = style;
    return this;
  }

  // 设置内容
  DialogBuilder content(String content, {TextStyle? style}) {
    _config.content = content;
    _config.contentStyle = style;
    return this;
  }

  // 设置背景
  DialogBuilder backgroundColor(Color color) {
    _config.backgroundColor = color;
    return this;
  }

  // 添加确认按钮
  DialogBuilder withPositiveButton(String text, {
    required VoidCallback onPressed,
    Color color = Colors.blue,
  }) {
    _config.positiveButton = DialogButton(
      text: text,
      color: color,
      onPressed: onPressed,
    );
    return this;
  }

  // 构建最终产品
  Widget build() {
    return AlertDialog(
      title: _config.title != null 
          ? Text(_config.title!, style: _config.titleStyle)
          : null,
      content: _config.content != null
          ? Text(_config.content!, style: _config.contentStyle)
          : null,
      backgroundColor: _config.backgroundColor,
      actions: [
        if (_config.negativeButton != null)
          TextButton(
            style: TextButton.styleFrom(
              foregroundColor: _config.negativeButton!.color,
            ),
            onPressed: _config.negativeButton!.onPressed,
            child: Text(_config.negativeButton!.text),
          ),
        if (_config.positiveButton != null)
          TextButton(
            style: TextButton.styleFrom(
              foregroundColor: _config.positiveButton!.color,
            ),
            onPressed: _config.positiveButton!.onPressed,
            child: Text(_config.positiveButton!.text),
          ),
      ],
      shape: _config.shape,
    );
  }
}
3. 在Flutter中使用
// 基础用法
showDialog(
  context: context,
  builder: (_) => DialogBuilder()
    .title('警告', style: TextStyle(color: Colors.red))
    .content('确定要删除这个文件吗?')
    .withPositiveButton('删除', onPressed: () => Navigator.pop(context))
    .build()
);

// 复杂用法
final deleteDialog = DialogBuilder()
  .title('永久删除', style: TextStyle(fontWeight: FontWeight.bold))
  .content('此操作将永久删除数据,不可恢复!')
  .backgroundColor(Colors.grey[100]!)
  .withPositiveButton('确认删除', 
    onPressed: () => _deleteItem(),
    color: Colors.red,
  )
  .withNegativeButton('取消')
  .build();

showDialog(context: context, builder: (_) => deleteDialog);

Flutter框架中的建造者模式

实际上,Flutter自身大量使用了建造者模式的变体:

1. TextStyle 的copyWith方法
TextStyle style = TextStyle(
  fontSize: 16,
  color: Colors.black,
).copyWith(
  fontWeight: FontWeight.bold,
  decoration: TextDecoration.underline,
);
2. ThemeData 的构造方式
final theme = ThemeData.light().copyWith(
  primaryColor: Colors.blue,
  buttonTheme: ButtonThemeData(
    buttonColor: Colors.blue[700],
  ),
);
3. BoxDecoration 的链式配置
BoxDecoration(
  color: Colors.blue,
  borderRadius: BorderRadius.circular(10),
  boxShadow: [
    BoxShadow(color: Colors.black12, blurRadius: 6),
  ],
)

高级应用:响应式布局建造者

创建一个能自动适应不同屏幕尺寸的布局建造者:

class ResponsiveBuilder {
  final List<Widget> _children = [];
  double _spacing = 16.0;
  Axis _direction = Axis.vertical;
  int _crossAxisCount = 2;

  ResponsiveBuilder addChild(Widget child) {
    _children.add(child);
    return this;
  }

  ResponsiveBuilder spacing(double value) {
    _spacing = value;
    return this;
  }

  ResponsiveBuilder horizontal() {
    _direction = Axis.horizontal;
    return this;
  }

  ResponsiveBuilder columns(int count) {
    _crossAxisCount = count;
    return this;
  }

  Widget build() {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth > 600) {
          return GridView.count(
            crossAxisCount: _crossAxisCount,
            childAspectRatio: 1.0,
            mainAxisSpacing: _spacing,
            crossAxisSpacing: _spacing,
            children: _children,
          );
        } else {
          return ListView.separated(
            scrollDirection: _direction,
            itemCount: _children.length,
            separatorBuilder: (_, __) => SizedBox(
              width: _direction == Axis.horizontal ? _spacing : 0,
              height: _direction == Axis.vertical ? _spacing : 0,
            ),
            itemBuilder: (context, index) => _children[index],
          );
        }
      },
    );
  }
}

// 使用示例
ResponsiveBuilder()
  .addChild(ProductCard(product1))
  .addChild(ProductCard(product2))
  .spacing(20)
  .columns(3)
  .build()

建造者模式最佳实践

  1. 何时使用建造者模式:

    • 对象需要多个步骤或复杂配置才能创建
    • 构造函数参数超过4个
    • 需要创建不可变对象但又要灵活配置
    • 相同构建过程需要创建不同表示
  2. Flutter特化技巧:

    // 使用可选命名参数简化初始创建
    DialogBuilder({String? title, String? content}) {
      if (title != null) _config.title = title;
      if (content != null) _config.content = content;
    }
    
    // 添加预置配置
    DialogBuilder.error() {
      return DialogBuilder()
        .titleColor(Colors.red)
        .backgroundColor(Colors.red[50]!);
    }
    
    // 支持运算符重载
    DialogBuilder operator <<(Widget child) {
      addChild(child);
      return this;
    }
    
  3. 性能优化:

    // 使用memoization缓存常用配置
    class DialogBuilder {
      static final Map<String, DialogConfig> _presets = {};
      
      static DialogBuilder preset(String name) {
        if (!_presets.containsKey(name)) {
          _presets[name] = _createPreset(name);
        }
        return DialogBuilder.fromConfig(_presets[name]!);
      }
    }
    

建造者模式 vs 工厂模式

特性 建造者模式 工厂模式
主要目的 分步构建复杂对象 创建对象家族
构建过程 显式控制每个步骤 隐藏创建细节
适用场景 包含多个组件的对象 不同系列的对象
Flutter应用 复杂组件配置、响应式布局 主题系统、平台适配

建造者模式的强大变体

1. 阶段式建造者
abstract class DialogBuilderPhase1 {
  DialogBuilderPhase2 setTitle(String title);
}

abstract class DialogBuilderPhase2 {
  DialogBuilderPhase3 setContent(String content);
}

class DialogBuilder implements 
  DialogBuilderPhase1, 
  DialogBuilderPhase2 {
  
  // 分阶段实现...
}

// 强制按顺序调用:
DialogBuilder()
  .setTitle('标题') // 必须第一步
  .setContent('内容') // 必须第二步
  .build();
2. 装饰器增强建造者
class DialogDecorator implements DialogBuilder {
  final DialogBuilder _builder;
  
  DialogDecorator(this._builder);
  
  
  DialogBuilder title(String title) {
    _builder.title(title);
    return this;
  }
  
  // 添加额外功能
  DialogDecorator withAnimation(Duration duration) {
    // 添加动画逻辑
    return this;
  }
}

// 使用增强功能
DialogDecorator(CustomDialogBuilder())
  .title('动画对话框')
  .withAnimation(Duration(seconds: 1))
  .build();

总结:建造者模式是你的UI组装线

  • 核心价值: 将复杂对象的构建过程分解为可管理的步骤
  • Flutter优势:
    • 解决"参数爆炸"问题
    • 创建不可变对象的灵活方式
    • 提高大型组件配置的可读性
    • 支持流畅的链式调用
  • 适用场景: 复杂对话框、表单构建、响应式布局、主题配置

🔧 设计启示: 当组件参数超过5个或创建逻辑复杂时,建造者模式能显著改善代码质量!


网站公告

今日签到

点亮在社区的每一天
去签到