Flutter 中不得不会的 mixin

Stella981
• 阅读 528

Flutter 中不得不会的 mixin

老孟导读mixin 是 Dart 中非常重要的概念,对于未接触过此概念的Coder来说尤其重要,最近看源码的时候,由于对 mixin 不熟悉导致理解出现偏差,走了很多弯路,所以这篇文章介绍一下 mixin 概念。

Dart 及 Engine 版本:

Engine • revision ae90085a84 Tools • Dart 2.10.4

请注意版本,不同的版本可能存在差异。

先来看下官方的定义:

Mixins are a way of reusing a class’s code in multiple class hierarchies.

Mixins 是一种在多个类层次结构中重用类代码的方法。

在来看下 Wiki 的解释:

In object-oriented programming languages, a mixin (or mix-in) is a class that contains methods for use by other classes without having to be the parent class of those other classes. How those other classes gain access to the mixin's methods depends on the language. Mixins are sometimes described as being "included" rather than "inherited".

Mixins encourage code reuse and can be used to avoid the inheritance ambiguity that multiple inheritance can cause (the "diamond problem"), or to work around lack of support for multiple inheritance in a language. A mixin can also be viewed as an interface with implemented methods. This pattern is an example of enforcing the dependency inversion principle.

翻译如下:

在面向对象的编程语言中,mixin(或mix-in)是一个类,其中包含供其他类使用的方法,而不必成为其他类的父类。这些其他类如何获得对mixin方法的访问权限取决于语言。混合素有时被描述为“包含”而不是“继承”。

Mixins鼓励代码重用,并且可用于避免多重继承可能导致的继承歧义(“钻石问题”),或解决语言中对多重继承的支持不足的问题。混合也可以看作是已实现方法的接口。此模式是强制执行依赖关系反转原理的示例。

看完这两段介绍,可能依然对其比较模糊,不要紧,现在只需对其有个概念即可,下面会详细介绍 Mixins 的用法,我个人的理解就是:Mixins 解决了无法多重继承的问题。

什么时候需要使用 Mixins

有如下场景:

定义一个基类人(Person),它有吃(eat)的方法。

有3个实际的人A、B、C,它们都继承 Person,但是3个人有不同的技能:

  • A :会唱歌、跳舞

  • B:会跳舞、写代码

  • C:会唱歌、写代码

上面的场景中唱歌、跳舞、写代码是一种技能,并不是每一个人都会的,所以将其定义在 Person 中是不合适的,如果各自定义为一个类,又不能同时继承Person和唱歌、跳舞、写代码,如果将唱歌、跳舞、写代码定义为 Interface ,那么A、B、C中要各自实现其方法,

那要如何实现呢?Mixins 出场啦。

定义一个 Person 基类和功能类唱歌、跳舞、写代码:

`class Person {
  eat() {
    print('Person eat');
  }
}

class Dance {
  dance() {
    print('Dance dance');
  }
}

class Sing {
  sing() {
    print('Sing sing');
  }
}

class Code {
  code() {
    print('Code code');
  }
}
`

定义A、B、C:

`class A extends Person with Dance, Sing {}

class B extends Person with Sing, Code {}

class C extends Person with Code, Dance {}
`

注意:混合使用 with 关键字。

使用:

A a = A(); a.eat(); a.dance(); a.sing();

输出日志:

flutter: Person eat flutter: Dance dance flutter: Sing sing

可以看到 A 中有了Dance 和Sing的相关的方法。

Dance 是一个 class,如果给其添加构造函数会如何?

给 Dance 添加构造函数,修改如下,

Flutter 中不得不会的 mixin

此时发现 A 和 C 无法编译,出现如下错误:

Flutter 中不得不会的 mixin

很明显,需要 mixin 的类无法定义构造函数。

所以一般会将需要 mixin 的类使用 mixin 关键字:

Flutter 中不得不会的 mixin

添加限定条件,使用关键字 on

接着上面的场景继续,这时定义一个狗的类,目前狗这个类也可以混合 Dance 、Sing 和 Code,

class Dog with Code{}

但是,Code 是人类独有的技能,不希望 Dog 这个类可以mixin,所以给 Code 添加限定条件:

Flutter 中不得不会的 mixin

使用关键字 on 限定Code 只能被 Person 或者其子类 mixin。

Flutter 中不得不会的 mixin

此时 Dog 无法 mixin Code。

添加限定后,可以重写其方法, Code 重写 Person 的方法:

Flutter 中不得不会的 mixin

super 表示调用父类(Person)的方法。

如何处理多个类有同一方法的情况

假设有D 和 D1 两个类,有同一个方法 d,E mixin D 和 D1:

Flutter 中不得不会的 mixin

此时,调用 e.d 方法:

E e = E(); e.d();

输出:

flutter: D1 d

说明后面的将前面的覆盖了,调换下D 和 D1的顺序:

class E with D1, D {}

输出:

flutter: D d

此时在 E 中也添加 d 方法:

Flutter 中不得不会的 mixin

输出:

flutter: E d

说明 E 中 方法覆盖了原来的。

E 中 d 方法可以调用 super.d()

Flutter 中不得不会的 mixin

输出:

flutter: D d flutter: E d

假设现在有F、G、H 三个类,都有 a 方法,

Flutter 中不得不会的 mixin

有如下定义的类:

Flutter 中不得不会的 mixin

那么下面会输出什么值:

Flutter 中不得不会的 mixin

答案是:

flutter: G a

记住:混合类时,进行混合的多个类是线性的,这是他们共有方法不冲突的原因,混合的顺序非常重要,因为它决定了混合时相同的方法的处理逻辑。

再次看下 FG 的混合情况:

Flutter 中不得不会的 mixin

FG 继承 H,混合 F 和 G,对于相同方法的优先级为:G > F > H,因此共有方法 a,最后执行的是 G 类中的 a 方法。

那么如果 FG 中也有 a 方法会如何?

Flutter 中不得不会的 mixin

如果本身(FG)也存在相同的方法那么优先级:FG > G > F > H。super.a() 执行的是 G 中的 a 方法。

输出结果:

flutter: G a flutter: FG a

更复杂的来啦,请看如下混合关系:

Flutter 中不得不会的 mixin

BB 为一个抽象类,有一个构造函数,其中执行 init 方法,GB 和 PB 为一个混合类型,限定了只有 BB 或者其子类才能混合,WFB 继承 BB,并混合GB、PB,此时创建 WFB 对象,

WFB wfb = WFB();

输出结果是什么?

flutter: BB Constructor flutter: BB init flutter: GB init flutter: PB init

是不是很诧异,按照上面的逻辑不是应该只调用 PB 的 init 方法吗?

你理解的没有错,的确只调用了PB 的 init 方法,但是 PB 的 init 方法中调用了**super.init()**,这个才是重点,PB 通过 super.init 调用到了GB中的 init 方法, GB 通过 super.init 调用到了 BB 中的 init 方法,所以最终输出的就是上面的结果。

这个一定要理解其中的调用顺序,因为的 Flutter Framework 的入口函数 runApp 中就是此形式:

Flutter 中不得不会的 mixin

WidgetsFlutterBinding.ensureInitialized 方法如下:

Flutter 中不得不会的 mixin

WidgetsFlutterBinding 混合结构如下:

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

BindingBase 及构造函数如下:

Flutter 中不得不会的 mixin

其执行了 initInstances 和 initServiceExtensions 方法。看下面混合的顺序:

Flutter 中不得不会的 mixin

从后到前依次执行其 initInstances 和 initServiceExtensions(如果有) 方法,由于 initInstances 和 initServiceExtensions 方法中首先执行 super.initInstances()super.initServiceExtensions() ,所以最后执行的顺序为:BindingBase -> GestureBinding -> SchedulerBinding -> ServicesBinding -> PaintingBinding -> SemanticsBinding -> RendererBindinsg -> WidgetsBinding 。

类型

还是上面的F、G、H 三个类,那么 FG 的类型是什么,看下面的判断会输出什么?

Flutter 中不得不会的 mixin

输出:

flutter: FG is F : true flutter: FG is G : true flutter: FG is H : true

所以混合后的类型是超类的子类型。

总结

  1. Mixins 使我们可以在无需继承父类的情况下为此类添加父类的“功能”,可以在同一个类中具有一个父级和多个 mixin 组件。

  2. Mixins 不可以声明任何构造函数。

  3. Mixins 添加限定条件使用 on 关键字。

  4. 混合使用 with 关键字,with 后面可以是 classabstract classmixin 的类型。

  5. Mixins 不是多重继承,相反,它只是在多个层次结构中重用类中的代码而无需扩展它们的一种方式。

Flutter 中不得不会的 mixin

你可能还喜欢

Flutter 中不得不会的 mixin

关注「老孟Flutter」

让你每天进步一点点

本文分享自微信公众号 - 老孟Flutter(lao_meng_qd)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
凯特林 凯特林
3年前
如何避免JavaScript类型转换
你是否经历过JavaScript中的某些值比较没有得到预期结果的情况?看下面的情况:即使0结果为真,if条件也没有根据结果执行。有没有想过为什么会这样?本文主要说明这些值比较的工作原理以及影响它们的因素。在深入解释之前,大家要熟悉一个概念:类型转换。什么是JavaScript类型转换?这也称为类型强制。对于不熟悉此概念的人来说,它只是将值从一种
待兔 待兔
3年前
Dart官方文档翻译(二)(Dart之旅)
类Dart是一种面向对象的语言,具有类和基于mixin的继承。每个对象都是一个类的实例,所有类都来自Object。(https://links.jianshu.com/go?tohttps%3A%2F%2Fapi.dartlang.org%2Fstable%2Fdartcore%2FObjectclass.html)_基于Mixin的继承
Stella981 Stella981
2年前
Flutter vs React Native vs Native:深度性能比较
!(https://oscimg.oschina.net/oscnet/up5410e759aa8ea7d5747ab3d9dcc200d3ffe.png)老孟导读:这是老孟翻译的付费文章,文章所有权归原作者所有。欢迎加入老孟Flutter交流群,每周翻译23篇付费文章,精彩不容错过。原文地址:https:/
Stella981 Stella981
2年前
Flutter中如何使用WillPopScope
!(https://oscimg.oschina.net/oscnet/up39de92df7002d3e246160ac64f08f7e582b.png)老孟导读:在Flutter中如何实现点击2次Back按钮退出App,如何实现App中多个Route(路由),如何实现Back按钮只退出指定页面,此篇文章将告诉你。WillPopScope
Stella981 Stella981
2年前
JavaScript 非常重要的几个概念
JavaScript是一门比较复杂的语言。如果你是一名JavaScript开发人员,不管处于什么样的水平,都有必要了解JavaScript的基本概念。小编最近的工作涉及到JavaScript,于是本文就介绍了几个非常重要的JavaScript概念,但绝对不是说JavaScript开发人员只需要知道这些就可以了。01变量赋值(值与引用)Java
Wesley13 Wesley13
2年前
DRF
DRFTracking模块源码分析一、drftracking是什么?drftracking是为DRF的view访问提供一个日志记录模块。使用mixin的方式无缝的和DRF相结合。从源码结构上来看也是Django的一个APP项目,提供Model将日志记录到数据库、自定Manger操作等.其核心为该
Stella981 Stella981
2年前
Mixin研究之一
如何利用Mixin对已定义的类进行方法和属性扩展起因类已经定义明确,但希望在创建实例时拓展实例的功能,这种情况下怎么办?目的实现一种机制,能够根据一定的规则,在实例创建时动态的拓展实例的功能(方法和属性);实现1.动态的拓展类的方法和属性,这样所有实例
Wesley13 Wesley13
2年前
MySql数据库的锁
互联网并发编程中,锁的概念时时刻刻都在我们身边,无论是并发编程时候线程锁,还是数据库网络中的锁,都有一些相似之处,都是为了保持数据库的完整性和一致性。不恰当的使用锁,会导致性能下降,出现死锁等情况,所以弄清楚锁的原理概念是必要的。一、锁分类!(https://oscimg.oschina.net/oscnet/59e69906ec
Wesley13 Wesley13
2年前
Java GC详解
最近在抽时间阅读JDK的源码,主要是GC还有Safepoint相关的源码,发现很多我在之前拜读网上各种JVM原理大作时候由于没有看源码导致我对于底层原理的误解。果然,一百个人读水浒传,就有一百种水浒传。还是需要更加深入的了解下源码,才能更好地理解JVM,进行调优。这个系列,将在讲述JavaGC各种原理的基础上,结合对应的源码分析,
Stella981 Stella981
2年前
Rust的多继承和mixin机制
先上一段Rust代码:traitA{fnsay(self);}traitB{fnsay(self);}structS;implAforS{fnsay(self){