1 开局思考
mixin
是什么?mixin
被设计出来用于解决什么问题?mixin
所解决的问题之前是如何解决的?mixin
的特点以及使用方法和场景是哪些?
2 举例说明
上图中,一个Animal
的父类,有三个子类: Mamal
, Bird
, Fish
。每个子类对应的也有一些子类,最下层的子类有不同的行为动作。分别是 walk
,swim
,fly
。
继承自不同父类的子类可能会用有相同的行为动作。那么如果需要将这些行为放在父类
中,也就只能放在Animal
这个父类中,很明显这是十分不优雅的做法。
对于支持多继承的语言来说,可以定义 walk
,swim
,fly
三个父类,然后让对应的动作子类再
去继承相应的动作类,那么可以完美解决,但是基于dart中只有单继承。
dart中有两种方式可以解决:
- 不优雅,繁琐的方式 -> 使用
"implement"
- 简洁高效 -> 使用"
mixin
"
2.1 implement解决方案
这里用 walk
和swim
来举例。
创建 walk
和swim
类用来定义接口,然后对应的子类通过implement
实现对应的动作类,并在子类内部实现对应的方法。使用如下:
abstract class Animal {}
/// 定义动作类
abstract class Swim {
void swim() {}
}
abstract class Walk {
void walk() {}
}
/// 通过implement 实现动作类
class Duck extends Animal implements Swim, Walk {
@override
void swim() {
print('swim');
}
@override
void walk() {
print('walk');
}
}
class Shark extends Animal implements Swim {
@override
void swim() {
print('swim');
}
}
main(List<String> args) {
Duck().swim();
Duck().walk();
Shark().swim();
}
implement
的作用在于引用指定类的接口,然后在类自身实现。
上述代码中可见对于相同的行为动作,不同的子类需要自行去实现动作方法,有些繁琐,与我们所希望的多继承的效果有些差距。
2.2 mixin解决方案
直接使用mixin
方案:
abstract class Animal {}
<code>mixin</code> Swim {
void swim() {
print('swim');
}
}
<code>mixin</code> Walk {
void walk() {
print('walk');
}
}
class Duck extends Animal with Swim, Walk {}
class Shark extends Animal with Swim {}
main(List<String> args) {
Duck().swim();
Duck().walk();
Shark().swim();
}
打印结果为:
swim
walk
swim
可见,相对于implement
的方式,简洁优雅。
3 mixin的特性
mixin
是面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问mixin
类的方法、变量而不必成为其子类。mixin
的作用就是在多个类层次结构中重用类的代码。
以上是mixin
的定义,那么使用mixin
混入类之后,是个什么原理机制呢?
3.1 mixin的实现机制
Dart中的
mixin
s通过创建一个新类来实现,该类将mixin
的实现层叠在一个超类之上以创建一个新类,它不是“在超类中”,而是在超类的“顶部”,因此如何解决查找问题不会产生歧义。— Lasse R. H. Nielsen on StackOverflow.
简单的理解为mixin
在原有的类的基础之上新建了一个子类,并且混入了with
后的类的方法以及实现。通过一段代码来了解:
<code>mixin</code> A {
run() => print('A');
}
class Person {
run() => print('Person');
}
class Male extends Person with A {}
main(List<String> args) {
Male().run();
}
以上打印结果会是什么呢??是Person
还是A
?
打印结果:
A
如果在A类的run
方法中调用super
,打印结果会是怎样的呢?
<code>mixin</code> A on Person {
run() {
super.run();
print('A');
}
}
class Male extends Person with A {}
main(List<String> args) {
Male().run();
}
Tips:"on"
为父类约束,约定如果需要混入类A,那么被混入的类首先需要实现Person
类,否则会报错。所以一般使用on
来约束被混入的类需要继承自指定的父类。而且使用了on
父约束后,可以使用super调用父类的方法(注意:父类并不一定就是on后面的类,参考下面的线性化)。上述代码中,如果Male类不是继承自Person类,则不能 with
混入类A。
打印结果如下:
Person
A
其实在使用 with
之后,代码等同于:
Class PersonA = Person with A
Class Male extends PersonA;
所以在male调用run方法时,会先调用A类的run方法。A类的run方法中调用super的run方法,则会先打印"person",然后会打印"A"。
继承者关系如下图所示:
3.2 线性化
mixin
s不是一种在经典意义上获得多重继承的方法。mixin
s是一种抽象和重用一系列操作和状态的方法。- 它类似于扩展类所获得的重用,但它与单继承兼容,因为它是线性的。
— Lasse R. H. Nielsen on StackOverflow.
如下代码,打印结果是什么?
class Person {
run() => print('Person');
}
<code>mixin</code> A {
run() => print('A');
}
<code>mixin</code> B {
run() => print('B');
}
class Male extends Person with A, B {}
class Female extends Person with B, A {}
main(List<String> args) {
Male().run();
Female().run();
}
打印结果:
B
A
上述的Male和Female类语义上等于:
Class PersonA = Person with A
Class PersonAB = PersonA with B
Class Male extends PersonAB{}
Class PersonB = Person with B
Class PersonBA = PersonB with A
Class Female extends PersonBA{}
最终的继承关系如下图所示:
所以对于Male来说,调用run,会直接从B类中寻找方法实现。Female类会直接从A类中寻找方法实现。
为了证明上述的继承关系,分别在A类,B类的run方法中调用super.run(),如下:
class Person {
run() => print('Person');
}
<code>mixin</code> A on Person {
run() {
super.run();
print('A');
}
}
<code>mixin</code> B on Person {
run() {
super.run();
print('B');
}
}
class Male extends Person with A, B {}
main(List<String> args) {
Male().run();
}
打印结果为:
Person
A
B
通过打印结果,证明了 with
混入一个类时,会创建一个新的类。
4 flutter中的应用解析
最后通过效仿flutter中 runApp() 方法中WidgetsFlutterBinding.ensureInitialized()方法,演示一个使用mixin
的示例代码:
class BaseBinding {
BaseBinding() {
instances();
}
void instances() {
print('BaseBinding instance');
}
}
<code>mixin</code> GestureBinding on BaseBinding {
void instances() {
super.instances();
print('gesturebinding instance');
}
}
<code>mixin</code> WidgetBinding on BaseBinding, GestureBinding {
static WidgetBinding _instance;
static WidgetBinding get instance => _instance;
void instances() {
super.instances();
_instance = this;
print('widgetBinding instance');
}
}
class WidgetFlutterBinding extends BaseBinding with GestureBinding, WidgetBinding {
static WidgetBinding ensureInitialized() {
if (WidgetBinding.instance == null) {
WidgetFlutterBinding();
}
return WidgetBinding.instance;
}
}
main(List<String> args) {
WidgetFlutterBinding.ensureInitialized();
}
打印结果为:
BaseBinding instance
gesturebinding instance
widgetBinding instance
继承者关系图示
上述代码的调用顺序为:
- 调用WidgetFlutterBinding.ensureInitialized(),进入到对应的类方法中。
- 判断WidgetBinding.instance为null,则执行WidgetFlutterBinding()构造函数。
- 因为构造函数中必须调用父类的构造函数(自动或手动),按照形成的继承者关系图,因为BaseBinding类定义了无名无参构造函数,则BaseBinding的子类会自动调用BaseBinding自定义的构造函数,所以会调用BaseBinding()方法。
- BaseBinding()方法中调用了instances()方法。当前类为WidgetFlutterBinding,根据
with
的顺序,那么首先会从WidgetBinding类中去查找instances方法。 - WidgetBinding类中查找到instances方法,遇到super.instances(),根据继承者关系图,super.instances方法则会去GestureBinding 类中查找instances方法。
- GestureBinding类中查找到instances方法,遇到super.instances(),根据继承者关系图,会去BaseBinding中查找instances方法。
- BaseBinding中instances方法,打印"BaseBinding instance",函数调用完毕,返回GestureBinding的instances方法。
- GestureBinding中instances方法继续执行,打印"GetureBinding instances",函数调用完毕,返回WidgetBinding的instances方法。
- WidgetBinding的instances方法中,将this值复制给instance类变量,打印"WidgetBinding instances"。
The End