Dart Mixin全解析

1,186 阅读4分钟

概念

Mixin是面向对象程序设计语言中的类,用户与Mixin不是“is-a”的关系,而是"-able"关系,这种设计模式体现了依赖反转原则。 Mixin可以在单继承模式下获得多继承的共用性,从而更好地复用代码。

Mixin - 维基百科,自由的百科全书

特点

  1. 不可以声明构造函数,否则在混入多个mixin的时候会产生语法歧义。
  2. 混用使用with关键字,后面可以跟class,abstract class,mixin
  3. mixin用on关键字来限定条件

单个mixin

首先看一个简单的例子:

mixin C{
  void run(){}
}

class D with C{

}

下面是dart生成的字节码文件:

abstract class C extends core::Object /*isMixinDeclaration*/  {
  method run() → void {}
  ///省略无关信息
}

abstract class _D&Object&C extends core::Object implements main::C /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
  method run() → void {}
  ///省略无关信息
}

class D extends main::_D&Object&C {
}

继承链:C<-_D&Object&C<-D

  • 首先Dart会生成一个与mixin C对应的abstract class C'
  • 生成一个桥接类_D&Object&C继承Object同时实现C'
  • D直接继承_D&Object&C

由此可见mixin创建了一条继承链,并由此来实现代码复用。

多个mixin

多个mixin同时使用会按照出现的前后顺序分别生成桥接类,所以如果多个mixin拥有同一个方法,子类调用方法时以最后一个mixin为准。同样,如果父类和mixin拥有相同方法,mixin会把父类的方法覆盖。

下面是dart生成的字节码文件:

mixin A{
  void run(){}
}

mixin B{
  void run(){}
}

class C with A,B{
}

--------------------------------字节码--------------------------------------
abstract class A extends core::Object /*isMixinDeclaration*/  {
}

abstract class B extends core::Object /*isMixinDeclaration*/  {
}

abstract class **_C&Object&A** extends core::Object implements main::A /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
}

abstract class _C&Object&A&B extends main::_C&Object&A implements main::B /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
}

class C extends main::_C&Object&A&B {
}

限定条件

mixin可以限定条件,关键字是on,后面可以跟class, abstract class或者mixin

  • 如果后面跟class或者abstract class,表示只有该class的子类型才能混入该mixin
  • 如果后面跟mixin,表示只有该mixin的子类才可以混入。需要注意的是混入顺序:
mixin A{}

mixin B on A{}

///正确写法
class C with A,B{}

///错误写法
class C with B,A{}

下面是实例和字节码:

class Human{
  void run(){}
}

mixin ShuffleDance on Human{}

class Girl extends Human with ShuffleDance{}

--------------------------------字节码--------------------------------------
abstract class Human extends core::Object {
}

abstract class ShuffleDance extends main::Human /*isMixinDeclaration*/  {
}

abstract class _Girl&Human&ShuffleDance extends main::Human implements main::ShuffleDance /*isAnonymousMixin,isEliminatedMixin*/  {
}

class Girl extends main::_Girl&Human&ShuffleDance {
}

继承链:Human<-ShuffleDance<-_Girl&Human&ShuffleDance<-Girl

mixin ShuffleDance生成的辅助类是Human的子类,这就是on关键字的作用

多个限定条件

当存在多个限定条件时会生成多个桥接类,并且按照继承关系从父类到子类的顺序生成。

生成的字节码文件:

class A {
 void run() {
   print("a");
 }
}

mixin B {
 void run() {}
}

///混入E前要先implementsA和B
mixin E on A, B {
 void run() {
   print("e");
 }
}

class C extends A with B, E {
 void run() {
   print("c");
 }
}

--------------------------------字节码--------------------------------------
abstract class A extends core::Object {
}

abstract class B extends main::A /*isMixinDeclaration*/  {
}

abstract class _E&A&B extends core::Object implements main::A, main::B /*isAnonymousMixin*/  {
}

abstract class E extends main::_E&A&B /*isMixinDeclaration*/  {
}

abstract class _C&Object&A extends core::Object implements main::A /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
}

abstract class _C&Object&A&B extends main::_C&Object&A implements main::B /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
}

abstract class _C&Object&A&B&E extends main::_C&Object&A&B implements main::E /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
}

class C extends main::_C&Object&A&B&E {
}

Mixin实现链式调用

当一个类混入了一个带限定条件的mixin时,如果两个mixin恰巧又有共同的方法,那么默认会调用最后一个mixin的方法,实例:

mixin A{
	void fun(){
		print("a");
	}
}

mixin B on A{
	void fun(){
		print("b");
	}
}

class C with A,B{
}

C此时调用fun方法会打印b

现在改造一下mixin B

mixin B on A{
	void fun(){
		///添加A
		super.fun();
		print("b");
	}
}

C此时调用fun方法会打印a和b 这种链式调用充分运用了mixin限定条件的依赖关系,可以依次调用接口方法。

具体应用:

  • Flutter有一个BindingBase类,定义了一个initInstances接口方法。
  • WidgetsFlutterBinding, GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding这些胶水类都依赖于BindingBase,并重写了initInstances方法。
  • runApp()会依次调用这些胶水类的initInstances方法,初始化应用相关的一系列服务。