混入与接口
本课属于 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()那么,什么时候该使用哪一种呢?当不同的类需要相同的方法签名,但实现各不相同时,使用接口。当类需要共享完全相同的行为时,使用混入。
| 特性 | 接口 | 混入 |
|---|---|---|
| 提供内容 | 仅契约 | 契约 + 代码 |
| 实现方式 | 由你编写 | 已包含 |
| 关键字 | implements | with |
| 适用场景 | 不同行为,相同 API | 共享行为 |
你甚至可以结合这两种方式:使用接口定义类可以做什么,并在合适的地方使用混入提供通用实现。在 Dart 中,同时使用两者时,始终将 with 放在 implements 前面:
class MyClass with SomeMixin implements SomeInterface {
// with 放在 implements 前面
}挑战
简单我们来构建一个通知系统,演示何时使用接口,何时使用 mixin。你将创建一个通知接口,供需要不同实现方式的通知使用;还将创建一个 mixin,用于提供所有通知类型都应保持一致的日志记录行为。
你将把代码组织到 3 个文件中:
notifiable.dart:定义一个充当接口的抽象类Notifiable。它应声明两个抽象方法:send(String message)和getChannel(),后者返回一个String。不同的通知类型会根据各自的通道,以不同方式实现这些方法。logging.dart:创建一个Loggingmixin,为所有通知提供共享行为。添加一个方法logSent(String channel, String message),用于打印[LOG] Sent via [channel]: [message];再添加一个方法logTimestamp(),用于打印[LOG] Timestamp recorded。所有通知类型的日志行为都相同,因此非常适合使用 mixin。main.dart:导入这两个文件,并创建两个将接口与 mixin 结合使用的通知类:- 创建一个使用
Loggingmixin 并实现Notifiable的EmailNotification类。它的getChannel()应返回'Email',它的send()方法应打印Sending email: [message],然后调用logSent()并传入通道和消息,最后调用logTimestamp()。 - 创建一个使用
Loggingmixin 并实现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')
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Dart 在线编译器