dart中的mixin解析

1 开局思考

  • mixin是什么?
  • mixin被设计出来用于解决什么问题?
  • mixin所解决的问题之前是如何解决的?
  • mixin的特点以及使用方法和场景是哪些?

2 举例说明


上图中,一个Animal的父类,有三个子类: Mamal Bird Fish 。每个子类对应的也有一些子类,最下层的子类有不同的行为动作。分别是 walk swimfly

继承自不同父类的子类可能会用有相同的行为动作。那么如果需要将这些行为放在父类
中,也就只能放在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中的mixins通过创建一个新类来实现,该类将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 线性化

  • mixins不是一种在经典意义上获得多重继承的方法。
  • mixins是一种抽象和重用一系列操作和状态的方法。
  • 它类似于扩展类所获得的重用,但它与单继承兼容,因为它是线性的。

— 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

继承者关系图示
在这里插入图片描述
上述代码的调用顺序为:

  1. 调用WidgetFlutterBinding.ensureInitialized(),进入到对应的类方法中。
  2. 判断WidgetBinding.instance为null,则执行WidgetFlutterBinding()构造函数。
  3. 因为构造函数中必须调用父类的构造函数(自动或手动),按照形成的继承者关系图,因为BaseBinding类定义了无名无参构造函数,则BaseBinding的子类会自动调用BaseBinding自定义的构造函数,所以会调用BaseBinding()方法。
  4. BaseBinding()方法中调用了instances()方法。当前类为WidgetFlutterBinding,根据 with 的顺序,那么首先会从WidgetBinding类中去查找instances方法。
  5. WidgetBinding类中查找到instances方法,遇到super.instances(),根据继承者关系图,super.instances方法则会去GestureBinding 类中查找instances方法。
  6. GestureBinding类中查找到instances方法,遇到super.instances(),根据继承者关系图,会去BaseBinding中查找instances方法。
  7. BaseBinding中instances方法,打印"BaseBinding instance",函数调用完毕,返回GestureBinding的instances方法。
  8. GestureBinding中instances方法继续执行,打印"GetureBinding instances",函数调用完毕,返回WidgetBinding的instances方法。
  9. WidgetBinding的instances方法中,将this值复制给instance类变量,打印"WidgetBinding instances"。

The End

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值