Menu
CoddyTech

混入与继承

本课属于 Coddy Dart 学习旅程的“面向对象编程”单元。第 54 课,共 110 课。

现在你已经学会了混入的工作原理,接下来我们来说明什么时候应该使用混入,而不是继承。关键区别在于它们所表达的关系。

继承表示一种“is-a”关系。Dog是一种Animal。子类是父类的专门版本,而且你只能扩展一个类。

另一方面,混入表示一种“能做”的关系。Dog 可以游泳,但游泳并不是狗的定义特征。

// 继承:Dog 是一种 Animal
class Animal {
  String name;
  Animal(this.name);
}

class Dog extends Animal {
  Dog(String name) : super(name);
}

// 混入:Dog 可以游泳(但游泳并不能定义 Dog)
mixin Swimming {
  void swim() => print('Swimming');
}

class Dog extends Animal with Swimming {
  Dog(String name) : super(name);
}

当类具有共同的基本身份,并且你需要在父类中定义构造函数时,使用继承。当你想为没有共同祖先的类添加能力时,使用混入。

特性继承混入
关系是一种可以做
限制只能有一个父类可以有多个混入
构造函数允许不允许
使用场景特化共享行为

最棒的是,你可以把两者结合起来:扩展一个类,并混入多种能力,让你的类恰好具备所需的功能。

challenge icon

挑战

简单

我们来构建一个工作者系统,演示何时使用继承,何时使用 mixin。你将创建一个表示“是什么”关系的基类,以及表示“能做什么”能力的 mixin,然后将它们组合起来,展示两种方式如何协同工作。

你将把代码分成三个文件:

  • worker.dart:定义基类,用来确立所有工作者的核心身份:
    • 创建一个 Worker 类,包含 String name 和 String department 属性。添加一个接收这两个值的构造函数。添加一个方法 introduce(),用于打印 [name] works in [department]
  • skills.dart:定义表示工作者可能具备的能力(“能做什么”关系)的 mixin:
    • 创建一个 CanCode mixin,其中的方法 writeCode() 用于打印 Writing code...
    • 创建一个 CanDesign mixin,其中的方法 createDesign() 用于打印 Creating design...
    • 创建一个 CanManage mixin,其中的方法 leadTeam() 用于打印 Leading the team...
  • main.dart:导入这两个文件,并创建结合继承和 mixin 的专门工作者类型:
    • 创建一个 Developer 类,它继承 Worker 并使用 CanCode mixin
    • 创建一个 TechLead 类,它继承 Worker 并同时使用 CanCode 和 CanManage mixin
    • 创建一个名为 'Alice'、所属部门为 'Engineering' 的 Developer
    • 创建一个名为 'Bob'、所属部门为 'Engineering' 的 TechLead
    • 对 Alice:先调用 introduce(),再调用 writeCode()
    • 打印一个空行
    • 对 Bob:先调用 introduce(),再调用 writeCode(),然后调用 leadTeam()

注意,Developer 和 TechLead 都是工作者(继承),但它们能做不同的事情(mixin)。TechLead 通过组合多种能力,无需使用多重继承。

预期输出:

Alice works in Engineering
Writing code...

Bob works in Engineering
Writing code...
Leading the team...

亲手试试

import 'worker.dart';
import 'skills.dart';

// TODO: 创建一个继承 Worker 并使用 CanCode 混入的 Developer 类

// TODO: 创建一个继承 Worker 并同时使用 CanCode 和 CanManage 混入的 TechLead 类

void main() {
  // TODO: 在 'Engineering' 部门创建一个名为 'Alice' 的 Developer
  
  // TODO: 为 Alice 依次调用 introduce() 和 writeCode()
  
  // TODO: 输出一个空行
  
  // TODO: 在 'Engineering' 部门创建一个名为 'Bob' 的 TechLead
  
  // TODO: 为 Bob 依次调用 introduce()、writeCode() 和 leadTeam()
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Dart 在线编译器