AIGC标识 Dart之面向对象概述

Dart之面向对象概述

一、前言

什么是对象?什么又是面向对象?

这两个词很常见,但第一次认真解释时,反而容易卡住。好像知道它是什么,又很难说清楚它到底解决了什么问题。

可以先从做菜理解。

如果我们要做一道回锅肉,面向过程的想法是:第一步准备食材,第二步切肉,第三步热锅,第四步翻炒,最后出锅。它关注的是“步骤”。

面向对象的想法会换一个角度:这件事里有哪些对象?厨师、锅、肉、调料、火候都可以是对象。每个对象有自己的属性和行为,对象之间通过协作完成做菜这件事。

一句话理解:面向对象编程是一种用对象来抽象现实问题、组织代码和完成协作的编程思想。

二、面向过程编程

2.1 面向过程关注步骤

面向过程编程的核心是:把问题拆成一步一步的流程,然后按顺序执行。

比如做菜:

  1. 准备食材。
  2. 清洗食材。
  3. 切片。
  4. 热锅。
  5. 翻炒。
  6. 出锅。

写成代码时,它可能更像这样:

void prepareFood() {
  print('准备食材');
}

void cutFood() {
  print('切菜');
}

void cookFood() {
  print('翻炒');
}

void main() {
  prepareFood();
  cutFood();
  cookFood();
}

这种方式直接、清晰,很适合流程简单的问题。

2.2 面向过程的特点

面向过程通常有几个特点:

特点 含义 做菜类比
线性流程 按顺序执行步骤 先切菜,再下锅
集中管理 主流程负责调度所有函数 厨师一个人安排所有步骤
数据共享 多个函数可能操作同一份数据 肉、锅、调料被多个步骤反复使用

它的优势是容易上手,但当项目变大时,问题也会出现:

  • 步骤越来越多,主流程越来越长。
  • 数据被多个函数修改,状态不容易追踪。
  • 新增功能时,可能要改动很多旧流程。
  • 代码复用和扩展会变得困难。

所以面向过程不是不好,而是它更适合规模较小、流程明确的问题。

三、面向对象编程

3.1 对象是什么

面向对象里常说一句话:万物皆对象。

这句话不是说世界真的都变成代码对象,而是提醒我们:可以把现实世界中的事物、事件、规则和概念抽象成对象。

比如做菜这件事里:

  • 厨师可以是对象。
  • 锅可以是对象。
  • 食材可以是对象。
  • 菜谱可以是对象。
  • 一次烹饪过程也可以是对象。

对象通常包含两类内容:

  • 属性:对象有什么。
  • 行为:对象能做什么。

比如一个 Chef 对象:

class Chef {
  String name;

  Chef(this.name);

  void cut() {
    print('$name 正在切菜');
  }

  void cook() {
    print('$name 正在炒菜');
  }
}

void main() {
  final chef = Chef('小王');

  chef.cut();
  chef.cook();
}

这里的 name 是属性,cut()cook() 是行为。

3.2 面向对象关注建模

面向对象编程不只是把函数塞进类里。

它真正关注的是:如何把问题中的事物抽象成对象,再让这些对象互相协作。

还是做菜:

  • 厨师负责切菜和翻炒。
  • 锅负责承载食材。
  • 食材有生熟状态。
  • 菜谱规定步骤。

这些对象各自负责一部分事情,整体通过协作完成目标。

这比一个主函数管理所有细节更接近现实世界,也更容易扩展。

3.3 对象之间通过消息协作

对象不是孤立存在的。

一个对象想让另一个对象做事,通常会调用它的方法。这个过程可以理解为消息传递。

class Pot {
  void heat() {
    print('锅开始加热');
  }
}

class Chef {
  void cook(Pot pot) {
    pot.heat();
    print('厨师开始炒菜');
  }
}

void main() {
  final chef = Chef();
  final pot = Pot();

  chef.cook(pot);
}

chef.cook(pot) 中,厨师对象使用锅对象完成烹饪流程。对象之间不是把所有数据摊开共享,而是通过方法调用来协作。

四、面向对象的核心特点

4.1 模块化

模块化就是让不同对象负责不同任务。

比如:

  • Chef 负责烹饪行为。
  • Pot 负责锅相关行为。
  • Ingredient 负责食材状态。

这样每个类的职责更清晰,也更容易单独开发和测试。

4.2 封装

封装就是把对象内部的数据和实现细节藏起来,只暴露必要接口。

比如食材内部怎么变化,外部不一定需要知道。外部只需要问:熟了吗?

class Ingredient {
  bool _cooked = false;

  void cook() {
    _cooked = true;
  }

  bool get isCooked => _cooked;
}

_cooked 是内部状态,外部通过 isCooked 获取结果。这样可以减少外部随意修改内部数据导致的问题。

4.3 继承

继承可以让新类型复用已有类型的属性和行为。

class Animal {
  void eat() {
    print('正在吃东西');
  }
}

class Cat extends Animal {
  void meow() {
    print('喵');
  }
}

Cat 继承了 Animaleat(),同时又有自己的 meow()

继承适合表达“是一种”的关系,比如猫是一种动物。

4.4 多态

多态表示同一个行为,在不同对象上可以有不同表现。

abstract class Animal {
  void speak();
}

class Cat extends Animal {
  @override
  void speak() {
    print('喵');
  }
}

class Dog extends Animal {
  @override
  void speak() {
    print('汪');
  }
}

void makeSound(Animal animal) {
  animal.speak();
}

void main() {
  makeSound(Cat());
  makeSound(Dog());
}

makeSound() 面向的是 Animal,但传入猫就喵,传入狗就汪。这就是多态带来的灵活性。

五、面向对象的优势

5.1 更容易维护

当对象职责清晰时,修改某一类逻辑通常只需要看对应类。

比如锅的加热规则变化了,就优先修改 Pot;食材状态变化了,就优先修改 Ingredient

5.2 更容易扩展

新增功能时,可以增加新类或扩展已有类,而不是把所有逻辑堆进一个越来越长的流程。

比如要新增一道菜,不一定要重写整个厨房流程,而是可以新增菜谱对象、食材对象或烹饪策略。

5.3 更容易复用

对象和类可以在不同场景复用。

比如 Ingredient 不只可以用于回锅肉,也可以用于其他菜品。Chef 的切菜、炒菜行为也可以复用到多个流程中。

5.4 更适合复杂系统建模

当业务复杂时,我们需要先理解现实问题,再组织代码。

面向对象提供了一个比较自然的思考方式:

  1. 找出问题中的对象。
  2. 分析对象的属性。
  3. 分析对象的行为。
  4. 明确对象之间怎么协作。

这个过程就是从现实问题到代码结构的桥。

六、面向过程和面向对象怎么选

6.1 简单流程可以面向过程

如果问题很小、流程很明确,面向过程写法可能更简单。

比如一个临时脚本、一个小工具函数、一次简单数据转换,不一定非要设计很多类。

6.2 复杂业务更适合面向对象

如果问题里有多个实体、多个状态、多种行为和长期变化,面向对象会更有优势。

比如:

  • 用户系统。
  • 商品订单。
  • IM 消息。
  • 游戏角色。
  • Flutter 页面状态。

这些场景中,对象之间的关系比单纯步骤更重要。

6.3 不要为了对象而对象

面向对象是一种建模方式,不是把所有代码都包进类里。

好的面向对象设计,应该让代码更清晰;如果类越写越绕,说明建模可能需要重新调整。

七、总结

这一篇我们系统了解了面向对象编程的基础概念。

面向过程关注步骤,适合流程简单的问题;面向对象关注对象、属性、行为和协作,更适合复杂系统建模。

对象可以理解为现实世界中事物、事件、规则和概念的代码抽象。对象拥有属性和行为,对象之间通过方法调用进行消息传递。

最后记住三句话:

  • 面向过程问的是:第一步做什么,第二步做什么。
  • 面向对象问的是:有哪些对象,它们各自负责什么。
  • 面向对象的核心价值,是让复杂问题更容易建模、维护、扩展和复用。
posted @ 2026-07-19 15:00  好的佩奇  阅读(11)  评论(0)    收藏  举报