当前位置:首页 > 文章列表 > 文章 > 软件教程 > Dart - 认识Pattern Matching(模式匹配)

Dart - 认识Pattern Matching(模式匹配)

2026-08-20 23:47:36 0浏览 收藏

第一章:告别“剥洋葱”式的防御性编程

在 Dart 3 之前,处理未知数据(比如 dynamic、父类对象、或者 JSON Map)就像是在剥洋葱。我们需要一层一层地检查类型、检查长度、检查 Key 是否存在,最后小心翼翼地把数据取出来。

Dart - 认识Pattern Matching(模式匹配)

这种代码通常被称为**“防御性代码”**。它的特点是:真正干活的代码只有一行,但为了这一行,前面写了十行检查代码。

Pattern Matching 的出现,就是为了消灭这些冗余的“废话”。

我们来看三个最典型的痛点场景。

痛点一:多态类型检查 —— isas 的无尽循环

这是面向对象编程中最常见的场景。你有一个父类 Shape,和两个子类 CircleRect。你需要根据不同类型计算面积。

⛔️ 痛苦的旧时代 (The Old Way)

你需要遵循“三步走”仪式:

  1. Check (检查):使用 is 判断类型。
  2. Cast (强转):虽然 Dart 有类型提升,但在某些复杂场景或旧版本中,你心里总惦记着 as
  3. Access (访问):最后访问属性。
abstract class Shape {}
class Circle extends Shape { final double radius; Circle(this.radius); }
class Rect extends Shape { final double width; final double height; Rect(this.width, this.height); }

double calculateArea(Shape shape) {
  // 繁琐的 if-else 链条
  if (shape is Circle) {
    // 即使 Dart 帮你做了类型提升,这种 indent(缩进)也很难受
    return 3.14 * shape.radius * shape.radius;
  } else if (shape is Rect) {
    return shape.width * shape.height;
  } else {
    return 0;
  }
}

✅ 优雅的新时代 (The Pattern Matching Way)

使用 对象模式 (Object Pattern)。我们将“类型检查”和“属性提取”合二为一。

double calculateArea(Shape shape) {
  // 结合 Switch 表达式
  return switch (shape) {
    // 语法解读:如果是 Circle 类型,直接把 radius 拆出来赋值给 r
    Circle(radius: var r) => 3.14 * r * r,
    
    // 语法解读:如果是 Rect 类型,直接拆出 w 和 h
    Rect(width: var w, height: var h) => w * h,
    
    // 处理其他情况
    _ => 0,
  };
}

改变了什么?

  • **消灭了 if**:逻辑变成了扁平的列表,而不是嵌套的块。
  • **消灭了 .**:直接使用变量 rwh,代码更短更易读。

痛点二:JSON 解析 —— 充满恐惧的 []?

处理后端返回的 API 数据(JSON)是每个客户端开发者的噩梦。因为数据是 Map,你永远不知道里面是不是空的,或者类型对不对。

⛔️ 痛苦的旧时代

假设 API 返回的数据结构是:{'user': ['Han Meimei', 18]}。我们要提取名字和年龄。

void handleJson(MapString, dynamic> json) {
  // 1. 检查 key 是否存在且不为 null
  var user = json['user'];
  if (user != null) {
    // 2. 检查类型是不是 List
    if (user is List) {
      // 3. 检查长度够不够(防止越界 Crash)
      if (user.length == 2) {
        // 4. 检查元素类型
        if (user[0] is String && user[1] is int) {
          // 5. 终于敢取值了!
          String name = user[0];
          int age = user[1];
          print('$name is $age');
        }
      }
    }
  }
}

这一坨代码,读起来让人窒息。 核心逻辑完全被淹没在安全检查里了。

✅ 优雅的新时代

使用 集合模式 (Collection Pattern)。你可以像写 JSON 一样去匹配 JSON。

void handleJson(MapString, dynamic> json) {
  // 一行代码完成:Key检查 + 类型检查 + 长度检查 + 元素类型检查 + 赋值
  // 语法解读:如果 json 符合 {'user': [String name, int age]} 这种结构
  if (json case {'user': [String name, int age]}) {
    // 直接用!无需任何担心
    print('$name is $age');
  } else {
    print('无效的 JSON 格式');
  }
}

改变了什么?

  • 结构可视化:代码的样子就是数据的样子。
  • 原子性:要么完全匹配成功拿到数据,要么完全失败,不存在“取了一半发现错了”的尴尬。

痛点三:逻辑组合 —— 啰嗦的 &&||

有时候我们需要匹配某些特定的值范围或组合。

⛔️ 痛苦的旧时代

假设我们要写一个逻辑:判断一个坐标点 (x, y) 是否在坐标轴上。

void checkPoint(int x, int y) {
  if (x == 0 && y == 0) {
    print('原点');
  } else if (x == 0 || y == 0) {
    print('在坐标轴上');
  } else {
    print('在象限内');
  }
}

这代码没问题,但是对于复杂的逻辑判断,||&& 的混合使用很容易让人看花眼。

✅ 优雅的新时代

使用 逻辑操作符模式

void checkPoint(int x, int y) {
  // 这里的 switch 参数是一个 Record
  switch ((x, y)) {
    // 完美匹配原点
    case (0, 0): 
      print('原点');
      
    // 使用 || (逻辑或) 和 _ (通配符)
    // 翻译:x是0且y随意,或者,x随意且y是0
    case (0, _) || (_, 0): 
      print('在坐标轴上');
      
    // 其他
    case _: 
      print('在象限内');
  }
}

改变了什么?

  • 可读性提升:用模式来描述逻辑,比用布尔运算符描述逻辑更直观。

第一章小结

Pattern Matching 解决的核心场景是:

  1. 解包 (Destructuring):当你拿到一个“包裹”(对象、List、Record)时,不再需要手动拆封,而是一把撕开,直接拿里面的东西。
  2. 结构校验 (Validation):不再写命令式的 if (length == 2),而是声明式的 case [a, b]

它带来的思维转变:

  • Before: "先检查它是不是个圆,然后把它的半径取出来,再赋值给 r。"
  • After: "如果是圆(r),就把 r 给我。"

如今的你,已然知晓了怎样运用模式匹配来实现代码的简化。接下来的章节,我们将投身实战,去探究当Records + Sealed Classes + Pattern Matching这三者融合在一起时,究竟会产生何等强大的化学反应(例如:在Flutter UI中如何巧妙地运用它们)。


第二章:三剑合璧 —— 打造 Dart 开发的“终极武器”

Sealed Classes 提供了完美的类型封闭,Records 提供了轻量的数据载体,而 Pattern Matching 提供了手术刀般的解构能力时,你的代码将发生质的变化。

我们将通过三个最真实的开发场景,来看看这套组合拳的威力。

场景一:Flutter UI 的“声明式”构建

在 Flutter 中,我们经常根据不同的状态(State)显示不同的 UI。 以前我们习惯用 if-else 或者三目运算符 ? :,如果状态多了,代码就会变成“嵌套地狱”。

现在,配合 switch 表达式和模式匹配,UI 代码变得异常平滑。

假设:一个用户详情页的状态流 我们定义一个 Sealed Class 来管理状态:

sealed class UserState {}
class Loading extends UserState {}
class Error extends UserState { final String msg; Error(this.msg); }
class Success extends UserState { 
  final String name; 
  final int age; 
  final bool isVip;
  Success(this.name, this.age, this.isVip); 
}

Flutter 里的写法:

Widget build(BuildContext context) {
  final UserState state = context.watch().state;

  // 这里的 switch 表达式直接返回一个 Widget
  return switch (state) {
    // 1. 匹配 Loading
    Loading() => const CircularProgressIndicator(),

    // 2. 匹配 Error,顺便把 msg 拆出来
    Error(msg: var m) => Text('出错了: $m', style: TextStyle(color: Colors.red)),

    // 3. 匹配 Success,这就是“高光时刻”!
    // 我们不仅匹配了类型,还把 name, age, isVip 全部拆解成了局部变量
    // 甚至还可以结合 'when' 关键字做更细的逻辑判断
    Success(isVip: true, name: var n) => VipCardWidget(userName: n), // 只匹配 VIP 用户
    Success(name: var n, age: var a)  => NormalUserCard(name: n, age: a), // 普通用户
  };
}

解析:

  • 解构 (Destructuring)Success(name: var n) 直接把对象里的属性变成了 Widget 可用的变量 n
  • 守卫 (Guard):注意 Success(isVip: true) 这一行。这叫常量模式。意思是:“只有当状态是 Success isVip 属性为 true 时,才匹配这一行。” 这一行代码替代了以前嵌套的 if (user.isVip)

场景二:HTTP 响应的优雅处理 (Records + Pattern)

处理网络请求时,我们经常需要同时返回“状态码”和“数据”。以前可能会封装一个 HttpResponse 对象,现在用 Record 最快。

模拟一个网络请求函数: 返回一个 Record:(int statusCode, Map json)

(int, MapString, dynamic>) fetchUserInfo() {
  // 模拟返回 200 成功,或者 404 失败
  return (200, {'name': 'Li Lei', 'role': 'admin'});
}

使用模式匹配处理结果:

void handleResponse() {
  var response = fetchUserInfo();

  // 直接针对 Record 进行 switch 匹配
  switch (response) {
    // 场景 A: 状态码 200,且用户角色是 admin
    // 这里的 'admin' 是常量匹配,role 是变量提取
    case (200, {'role': 'admin', 'name': var name}):
      print('管理员 $name 登录成功!');

    // 场景 B: 状态码 200,其他用户
    case (200, {'name': var name}):
      print('普通用户 $name 登录');

    // 场景 C: 404 错误
    case (404, _):
      print('用户未找到');

    // 场景 D: 其他错误 (用 _ 通配符)
    case (var code, _):
      print('未知错误: $code');
  }
}

解析: 这段代码展示了模式匹配的强大之处——深层匹配。 它没有先判断 statusCode == 200,再去取 json['role']。而是一眼看穿了数据的内部结构:“只要是 200 且 role 是 admin 的,都走这里。”


场景三:循环中的解构 (For-Loop Pattern)

这是很多开发者容易忽略的“隐藏技能”。模式匹配甚至可以用在 for 循环里,用来处理 Map 或者 List。

旧写法:

var users = [
  {'name': 'A', 'age': 10},
  {'name': 'B', 'age': 20}
];

for (var user in users) {
  var name = user['name']; // 又是不安全的 key 访问
  print(name);
}

新写法 (For-in 里的解构):

var users = [
  {'name': 'A', 'age': 10},
  {'name': 'B', 'age': 20}
];

// 直接在 for 循环头里把 map 解体!
for (var {'name': name, 'age': age} in users) {
  // 循环体内直接使用 name 和 age 变量
  print('$name is $age years old');
}


总结:从“怎么做”到“是什么”

通过这两章的学习,你应该能感受到 Dart 3 带来的范式转变。

  • Records 让我们随手打包数据。
  • Sealed Classes 让我们严丝合缝地定义数据。
  • Pattern Matching 让我们极其优雅地消费数据。

以前,我们写代码像是在写说明书(先检查A,再转换B,再取出C...)。 现在,我们写代码像是在画图纸(如果数据长成 Success(data) 这个样子,就给我展示这个 Widget)。

Dart ——??运算符Dart ——??运算符
上一篇
Dart ——??运算符
Dart - 内存管理与垃圾回收(GC)深度解析
下一篇
Dart - 内存管理与垃圾回收(GC)深度解析
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ljg-skills -
    ljg-skills
    ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
    5043次使用
  • MELO音乐 - AI 音乐生成平台,支持多模态创作能力
    MELO音乐
    MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
    4575次使用
  • UniScribe - AI 免费在线音视频转文字平台
    UniScribe
    UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
    4530次使用
  • 剧云 - 免费 AI 智能中文剧本创作平台
    剧云
    剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
    4783次使用
  • 万象有声 - AI 一站式有声内容创作平台
    万象有声
    万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
    4735次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码