Dart语法Mixin机制

一、概念
“Mixins are a way of reusing a class’s code in multiple class hierarchies.” 混合是一种在多个类层次结构中重用类代码的方法。
从概念上我们可以理解它是为了解决代码重用的一种方式。学习java的小伙伴可能会想到interface,Dart中的类都可以作为接口,这不是已经有了
解决方案了吗?为什么还需要mixin呢?

二、使用
首先我们来解决上面提到的问题,为什么要使用mixin。
我们很多语言都是采用了单继承+接口多实现的方式,但是这种方式不能很好的适用于所有场景。
我们看下下面这个假设的例子:

mixin_extends2.PNG

我们通过上图可以看出,这些学生都有一个共同的父类Student,然后又有三个抽象子类:文科班学生,理科班学生、艺术班学生。这些类具有相同的行为和能力,但是有的类又有自己独有的行为和能力。比如文科、理科、艺术班学生都可以上数学课程,
但是只有理科班学生可以上物理课程,文科班和艺术班学生可以上地理课程,物理班学生又不可以上地理课程。那么问题就出现了,部分具有相同能力和行为的子类都要保留一份相同的代码实现,比如文科班学生类和艺术班学生类都要实现一份上地理课的实现,
就产生了冗余。这种单继承模型下,无法把部分子类具有相同行为能力抽象到基类中,因为对其它不具有此行为的子类来说是不合适的,例如把上地理课放在基类student中,因此只能在各自子类中实现。

abstract class Student{
}

abstract class GeographyClass{
  void goGeography();
}

abstract class MathClass{
  void goMath();
}

abstract class PhysicsClass{
  void goPhysics();
}

class ScienceStudent extends Student implements MathClass, GeographyClass {
  @override
  void goGeography() {
    print("上地理课");
  }

  @override
  void goMath() {
    print("上数学课");
  }
}
class LiberalStudent extends Student implements MathClass, PhysicsClass {
  @override
  void goMath() {
    print("上数学课");
  }

  @override
  void goPhysics() {
    print("上物理课");
  }
}
class ArtStudent extends Student implements MathClass, GeographyClass {
  @override
  void goGeography() {
    print("上地理课");
  }

  @override
  void goMath() {
    print("上数学课");
  }
}

从上述实现代码中可以看出,子类中很多相同的冗余实现代码。那么我们是不是想有一种方式可以解决这种冗余问题,是的,mixin就能很好的解决这类问题。
下面是使用mixin改写后的代码:

abstract class Student{}

mixin GeographyClass{
  void goGeography() {
    print("上地理课");
  }
}

mixin MathClass{
  void goMath() {
    print("上数学课");
  }
}

mixin PhysicsClass{
  void goPhysics() {
    print("上物理课");
  }
}

class ScienceStudent extends Student with MathClass, GeographyClass {
}

class LiberalStudent extends Student with MathClass, PhysicsClass {
}

class ArtStudent extends Student with MathClass, GeographyClass {
}

改写后的代码可以发现,mixin很好得解决了代码冗余问题。它能复用类中的某个功能具体实现,而不是像接口一样需要类去实现哪些能力。因此mixin多继承模型很好解决了单继承模型带来的冗余问题。
注意:对mixin关键字的支持是在Dart 2.1 版本引入的,早期的版本使用abstract class来代替的。
其实在java8和Kotlin中为了解决代码重复冗余问题,使用了接口的default实现来解决这个问题:

interface ITest {
    default void test(){}
   }

三、线性化
mixin是线性化的,这句话如何理解呢?首先我们看下下面的例子:

mixin TA {
  void t() {
    print("TA");
  }
}

mixin TB {
  void t() {
    print("TB");
  }
}

class TC {
  void t() {
    print("TC");
  }
}

class Mix1 with TA, TB {//TB
}
class Mix2 with TB, TA {//TA
}

我们会得到如下结果:

TB
TA

此时你可能会总结得到规律,with后面多个类中有相同的方法,会调用距离with关键字最远的类中的方法。下面我们得到结论:
mixin混入类中时,Dart中的Mixins通过创建一个新类来实现,该类将mixin的实现层叠在一个超类之上以创建一个新类 ,它不是“在超类中”,而是在超类的“顶部。
声明 mixin 的顺序代表了继承链的继承顺序,声明在后面的 mixin,一般会最先执行。
接下来我们再看下面的例子:

class T {
  void fun() {
    print("A");
  }
}

mixin TA on T{
  void fun() {
    super.fun();
    cover();
    print("TA");
  }

  void cover() {
    print("cover TA");
  }
}

mixin TB on T {
  void fun() {
    super.fun();
    print("TB");
  }

  void cover() {
    print("cover TB");
  }
}

class A extends T with TA, TB {} // TB

class B extends T with TB, TA {} // TA

void main() {
  A a = A();
  a.fun();
}

按照我们刚才的理解,我们会得到如下结果:

   A
  cover TA
  TA
  TB

实际上,我们输出的结果是:

    A
    cover TB
    TA
    TB

回想我们得到的结论,在mixin继承链中,最后声明的mixin会把前面声明的相同方法覆盖掉。这时,即使我们代码中调用了TA的cover方法,实际上也会被TB类中的cover方法覆盖掉。因此最终调用的还是MB中的方法。

四、总结
我们大致总结了mixin的机制和使用,mixin 是一个强大的概念,我们可以跨越类的层次结构重用代码。在我们看Flutter源码时,经常会看到使用这个功能,我也是在看Flutter代码时,看到这个关键字然后进行补脑的。
1.Mixins并不是经典意义上获得多重继承的方法。
2.Mixins是一种抽象和复用一系列操作和状态的方式,而且生成多个中间的mixin类。
3.它是线性的,因此与单继承兼容。
4.Mixins除了继承Object外,不可以继承任何其他类; Mixins不可以定义构造方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容