Dart-Mixin是什么

Mixin 是一种在多个类层次结构中重用代码的方法。mixin是面向对象程序设计语言中的类,其他类可以访问mixin类的方法、变量而不必成为其子类。Mixin的作用就是在多个类层次结构中重用类的代码,在类中混入其他功能,来增强代码的复用能力。可以使用with关键字将多个mixins放入同一个类中,而且dart对这个数量没有作任何限制。

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mixin TestMixin {
void test() {
print('test');
}
int testInt = 1;
void test2();
}

class Test with TestMixin {
@override
test2() {
print('test2');
}
}

void main() {
Test().test(); // test
print(Test().testInt); // 1
Test().test2(); // test2
}

使用注意事项

  • 作为 mixin 的类只能继承自Object,不能继承其他类
  • 作为 mixin 的类不能有构造函数

深入了解

使用关键字 on 来指定哪些类可以使用该 Mixin 类

当声明一个 mixin 时,on后面的类是使用这个 mixin 的父类约束。也就是说一个类若是要 with 这个 mixin,则这个类必须继承或实现这个 mixin 的父类约束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class BaseObject {
void method() {
print('call method');
}
}
mixin TestMixin on BaseObject{
void test() {
print('test');
}
int testInt = 1;
void test2() {
method();
}
}

class Test extends BaseObject with TestMixin {
}

void main() {
Test().test(); // test
print(Test().testInt); // 1
Test().test2(); // call method
}

如上,只有扩展或实现Musician的类才能使用mixin MusicalPerformer

mixin 的顺序决定了同名方法的调用关系

声明mixins的顺序决定了继承链,即决定了最上层到底层的超类(superclass)的排序。mixin 可以理解为对类的一种“增强”,但它与单继承兼容,因为它的继承关系是线性的。

简单来说with 后面的类会覆盖前面的类的同名方法。看下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A { String getMessage() => 'A'; }

class B { String getMessage() => 'B'; }

class P { String getMessage() => 'P'; }

class AB extends P with A, B {}

class BA extends P with B, A {}

void main() {
String result = '';

AB ab = AB();
result += ab.getMessage();

BA ba = BA();
result += ba.getMessage();

print(result);//BA
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class A { printMessage() => print('A'); }

mixin B on A {
printMessage() {
super.printMessage();
print('B');
}
}

mixin C on B {
printMessage() {
super.printMessage();
print('C');
}
}

class D with A, B, C { printMessage() => super.printMessage(); }

void main() {
D().printMessage();
//A
//B
//C
}

第一步 with A 就是 Object with A,此时 super 就是 Object 类。

第二步 with B,由于 mixin B 是 on A 的,所以对于 B 来说,其 super 就是 A。则 B 的 printMessage() 中会调用 A 的 printMessage()

第三步 with C,由于 mixin C 是 on B 的,所以对于 C 来说,其 super 就是 B。则 C 的 printMessage() 中会调用 B 的 printMessage()

第四步,D 继承的就是 ABC 的混合类,由于 A、B、C 三个类都有同名方法,则 B 会覆盖 A 的同名方法,C 会覆盖 B 的同名方法,最终ABC 的混合类中的方法就是 C 的 printMessage()。D 中的 super 就是 ABC 的混合类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A { printMessage() => print('A'); }

class B { printMessage() => print('B'); }

mixin C on A {
printMessage() {
super.printMessage();
print('C');
}
}

class D with A, B, C { printMessage() => super.printMessage(); }

void main() {
D().printMessage();
//B
//C
}

为什么是B C不是A C 呢?根据前面说的线性关系,D().printMessage()调用的是mixin C中的方法,C中调用super.printMessage()时,super应该是class D with A, B,也就是说,在class D with A, B, C的时候, B 的 printMessage() 就覆盖了 A 的 printMessage(),所以with A,B里面的 printMessage() 是 B 的 printMessage()

使用场景

我们应该在什么时候使用 mixin 呢?很简单,在我们编写 Java 的时候,感觉需要实现多个 interface 的时候。

那么,这个和多重继承相比,在某些场景有什么好处吗?答案是有。

在 Flutter 中,framework 的执行依赖多个 Binding,我们查看最外层 WidgetsFlutterBinding 的定义:

1
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {}

WidgetsBindingRendererBinding 中,都有一个叫做 drawFrame 的函数。在 WidgetsBindingdrawFrame中,也有 super.drawFrame() 的调用。

这里 mixin 的优点就体现了出来,我们可以看到这个逻辑有如下2点

  1. 保证了 widget 等的 drawFrame 先于 render 层的调用,保证了 Flutter 在布局和渲染处理中 widgets -> render 的处理顺序
  2. 保证顺序的同时,Widgets 和 Render 仍然属于 2 个不同的对象定义,职责分割的非常的清晰。

PS.在哪里可以找到WidgetsFlutterBinding

最直接的,可以在void runApp(Widget app)源码中找到:

1
2
3
4
5
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
/// Returns an instance of the binding that implements
/// [WidgetsBinding]. If no binding has yet been initialized, the
/// [WidgetsFlutterBinding] class is used to create and initialize
/// one.
///
/// You only need to call this method if you need the binding to be
/// initialized before calling [runApp].
///
/// In the `flutter_test` framework, [testWidgets] initializes the
/// binding instance to a [TestWidgetsFlutterBinding], not a
/// [WidgetsFlutterBinding]. See
/// [TestWidgetsFlutterBinding.ensureInitialized].
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding._instance == null) {
WidgetsFlutterBinding();
}
return WidgetsBinding.instance;
}
}

参考

https://juejin.cn/post/7088962808385110053

https://juejin.cn/post/6844903807848022024