Menu
CoddyTech

混入与接口

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

你已经了解了 mixin 与继承的区别。现在我们来比较 mixin 和接口,因为它们都能让你使用多种类型。它们的关键区别在于提供的内容:接口定义契约,而 mixin 提供实现。

实现接口时,你承诺提供某些方法,但所有代码都要自己编写。使用混入时,你可以免费获得实际代码。

// 接口方式:你必须实现所有内容
abstract class Printable {
  void printInfo();
}

class Report implements Printable {
  @override
  void printInfo() {
    print('Printing report...');  // 这部分由你来写
  }
}

// Mixin 方式:已包含实现
mixin Printable {
  void printInfo() {
    print('Printing...');  // 已经帮你写好了
  }
}

class Report with Printable {}  // 自动获得 printInfo()

那么,什么时候该使用哪一种呢?当不同的类需要相同的方法签名,但实现各不相同时,使用接口。当类需要共享完全相同的行为时,使用混入。

特性接口混入
提供内容仅契约契约 + 代码
实现方式由你编写已包含
关键字implementswith
适用场景不同行为,相同 API共享行为

你甚至可以结合这两种方式:使用接口定义类可以做什么,并在合适的地方使用混入提供通用实现。在 Dart 中,同时使用两者时,始终将 with 放在 implements 前面:

class MyClass with SomeMixin implements SomeInterface {
  // with 放在 implements 前面
}
challenge icon

挑战

简单

我们来构建一个通知系统,演示何时使用接口,何时使用 mixin。你将创建一个通知接口,供需要不同实现方式的通知使用;还将创建一个 mixin,用于提供所有通知类型都应保持一致的日志记录行为。

你将把代码组织到 3 个文件中:

  • notifiable.dart:定义一个充当接口的抽象类 Notifiable。它应声明两个抽象方法:send(String message) 和 getChannel(),后者返回一个 String。不同的通知类型会根据各自的通道,以不同方式实现这些方法。
  • logging.dart:创建一个 Logging mixin,为所有通知提供共享行为。添加一个方法 logSent(String channel, String message),用于打印 [LOG] Sent via [channel]: [message];再添加一个方法 logTimestamp(),用于打印 [LOG] Timestamp recorded。所有通知类型的日志行为都相同,因此非常适合使用 mixin。
  • main.dart:导入这两个文件,并创建两个将接口与 mixin 结合使用的通知类:
    • 创建一个使用 Logging mixin 并实现 Notifiable 的 EmailNotification 类。它的 getChannel() 应返回 'Email',它的 send() 方法应打印 Sending email: [message],然后调用 logSent() 并传入通道和消息,最后调用 logTimestamp()。
    • 创建一个使用 Logging mixin 并实现 Notifiable 的 SMSNotification 类。它的 getChannel() 应返回 'SMS',它的 send() 方法应打印 Sending SMS: [message],然后调用 logSent() 并传入通道和消息,最后调用 logTimestamp()。
    然后创建一个 EmailNotification,并调用 send('Hello World');打印一个空行;再创建一个 SMSNotification,并调用 send('Quick update')。

注意,接口会强制每个类自行编写 send() 和 getChannel() 的实现(不同行为);而 mixin 提供的日志记录方法对这两种通知都以相同方式生效(共享行为)。

预期输出:

Sending email: Hello World
[LOG] Sent via Email: Hello World
[LOG] Timestamp recorded

Sending SMS: Quick update
[LOG] Sent via SMS: Quick update
[LOG] Timestamp recorded

亲手试试

import 'notifiable.dart';
import 'logging.dart';

// TODO: 创建 EmailNotification 类,使其:
//       - 使用 Logging 混入类(with)
//       - 实现 Notifiable(implements)
//       - getChannel() 返回 'Email'
//       - send() 打印 'Sending email: [message]',然后依次调用 logSent() 和 logTimestamp()

// TODO: 创建 SMSNotification 类,使其:
//       - 使用 Logging 混入类(with)
//       - 实现 Notifiable(implements)
//       - getChannel() 返回 'SMS'
//       - send() 打印 'Sending SMS: [message]',然后依次调用 logSent() 和 logTimestamp()

void main() {
  // TODO: 创建一个 EmailNotification 对象,并调用 send('Hello World')
  
  // TODO: 打印一个空行
  
  // TODO: 创建一个 SMSNotification 对象,并调用 send('Quick update')
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: Dart 在线编译器