深入理解 Dart 中的 Mixin

在開(kāi)始閱讀此篇文章之前,我們可以先思考下如下問(wèn)題:

  1. 什么是 Mixin ?
  2. Mixin為什么會(huì)被設(shè)計(jì)出來(lái)休偶,它解決了什么問(wèn)題辜羊?
  3. 在 Mixin 被設(shè)計(jì)出來(lái)之前是如何解決此類問(wèn)題椅贱?
  4. Mixin 使用場(chǎng)景是什么只冻?
  5. Mixin 具體如何使用?

帶著這5個(gè)問(wèn)題再去閱讀本篇文章山橄,會(huì)讓你對(duì) Mixin 理解更加深刻。本篇文章主要理解Dart 中的 Mixin機(jī)制航棱,后面有一篇文章分析Mixin機(jī)制在Flutter Framework 層的應(yīng)用:Flutter APP 啟動(dòng)過(guò)程源碼分析萌衬。


一個(gè)名為Animal的超類,它有三個(gè)子類:Mammal秕豫、Bird、Fish祠墅。在他們下面有一些具體的子類,每個(gè)子類都有不同的行為毁嗦,這些行為是walk回铛、swim、fly的子集茵肃。有些動(dòng)物有共同的行為:如貓和鴿子都會(huì)行走,但貓不能飛免姿。這些行為與具體的子類都有交集榕酒,所以我們無(wú)法在他們的父類中實(shí)現(xiàn)這些行為故俐。

假設(shè)一個(gè)類可以繼承多個(gè)父類紊婉,那我們可以創(chuàng)建三個(gè)類:Walk、Swim喻犁、Fly。只需要 Dove 和 Cat 都再繼承 Walk 就可以滿足还栓。但是 Dart 是單繼承的,所以此種方法無(wú)法實(shí)現(xiàn)剩盒。

那我們是不是可以創(chuàng)建三個(gè)接口類:Walk慨蛙、Swim、Fly期贫,讓后讓子類去實(shí)現(xiàn)對(duì)應(yīng)的接口呢?答案是當(dāng)然可以的通砍,但是每個(gè)子類都要去實(shí)現(xiàn)一個(gè)或者多個(gè)接口,這并不是一個(gè)優(yōu)雅的解決方案垢揩。

那有沒(méi)有更優(yōu)雅的解決方案呢敛瓷?有叁巨,那就是 Mixin呐籽。mixin是面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言中的類,提供了方法的實(shí)現(xiàn)庶橱。其他類可以訪問(wèn)mixin類的方法贪惹、變量而不必成為其子類苏章。Mixin 的作用就是在多個(gè)類層次結(jié)構(gòu)中重用類的代碼。光聽(tīng)概念泉孩,我們可能不太好理解,先來(lái)第一個(gè)例子的代碼

class A {
  String getMessage() => 'A';
}

class B {
  String getMessage() => 'B';
}

class P {
  String getMessage() => 'P';
}

class AB extends P with A, B {}

class BA extends P with B, A {}

void main() {
  String result = '';

  AB ab = AB();
  result += ab.getMessage();

  BA ba = BA();
  result += ba.getMessage();

  print(result);
}

我們先不看程序運(yùn)行的結(jié)果寓搬,我們先來(lái)看看 類 AB 定義

class AB extends P with A, B {}

由于 Mixin 的線性化特性县耽,上述 AB 類的聲明可以分解為如下幾個(gè)步驟

class PA = P with A;
class PAB = PA with B;

class AB extends PAB {}

BA 類也是同理,最終的繼承關(guān)系可以用下圖表示

在 AB 和 P 之間創(chuàng)建新類唾琼,這些新類是超類 P 與 A 類和 B 類之間的混合類澎剥。

現(xiàn)在父叙,我們?cè)賮?lái)看下上面代碼執(zhí)行的結(jié)果:答案是 “BA”

P with A得到了 PA肴裙,并且 如果 A 與 P 中有同名方法,則 A 會(huì)覆蓋 P 的同名方法甜癞,也就是說(shuō) PA 中的與 P 同名的方法就是 A 中的方法宛乃。同理悠咱,PAB 中的與 P 同名的方法就是 B 中的方法.

因?yàn)?PA 繼承 P征炼,PA 與 P 有同名方法 getMessage(),則 PA 重載了 P 的 getMessage()眼坏,同理 PAB 重載了 PA 的 getMessage(),由于 AB 是繼承自 PAB 并且 AB 并沒(méi)有重載 getMessage()宰译,所以 ab.getMessage() 是調(diào)用 PAB 的 getMessage()魄懂,而 PAB 的 getMessage() 就是 B 的 getMessage(),所以 ab.getMessage() 輸出的結(jié)果是 B市栗,同理 ba.getMessage() 輸出的結(jié)果是 A咳短。

通過(guò)上面的例子的結(jié)果蛛淋,我們來(lái)總結(jié)下 Mixin 非常重要的一個(gè)特性:

  • with 后面的類會(huì)覆蓋前面的類的同名方法

為了加深對(duì)這個(gè)規(guī)律的理解,我再來(lái)看第二個(gè)例子

class A {
  printMessage() => print('A');
}

mixin B on A {
  printMessage() {
    super.printMessage();
    print('B');
  }
}

mixin C on B {
  printMessage() {
    super.printMessage();
    print('C');
  }
}

class D with A, B, C {
  printMessage() => super.printMessage();
}

void main() {
  D().printMessage();
}

在這個(gè)例子中铣鹏,由于 D 并沒(méi)有繼承類哀蘑,則默認(rèn)繼承 Object 類。

  1. 第一步 with A 就是 Object with A合溺,此時(shí) super 就是 Object 類缀台。
  2. 第二步 with B棠赛,由于 mixin B 是 on A 的膛腐,所以對(duì)于 B 來(lái)說(shuō),其 super 就是 A辩涝。則 B 的 printMessage() 中會(huì)調(diào)用 A 的 printMessage()勘天。
  3. 第三步 with C怔揩,由于 mixin C 是 on B 的脯丝,所以對(duì)于 C 來(lái)說(shuō),其 super 就是 B晕拆。則 C 的 printMessage() 中會(huì)調(diào)用 B 的 printMessage()材蹬。
  4. 第四步,D 繼承的就是 ABC 的混合類赚导,由于 A、B吼旧、C 三個(gè)類都有同名方法,則 B 會(huì)覆蓋 A 的同名方法掂为,C 會(huì)覆蓋 B 的同名方法,最終ABC 的混合類中的方法就是 C 的 printMessage()勇哗。D 中的 super 就是 ABC 的混合類。

經(jīng)過(guò)上面四個(gè)步驟欲诺,D().printMessage()最終的輸出結(jié)果就是:

我們將上面的例子代碼稍微改動(dòng)下

class A {
  printMessage() => print('A');
}

class B {
  printMessage() => print('B');
}

mixin C on A {
  printMessage() {
    super.printMessage();
    print('C');
  }
}

class D with A, B, C {
  printMessage() => super.printMessage();
}

void main() {
  D().printMessage();
}

大家思考一下扰法,這個(gè)例子的輸出結(jié)果是什么???
|
|

|

|

|
塞颁,
|
結(jié)
|

|

|

|

|
|
|
|
|
|
|
輸出的結(jié)果是


有人可能會(huì)說(shuō),mixin C on A酷窥,那么 C 中的 super 不就是 A 嗎伴网,結(jié)果應(yīng)該是 AC 才對(duì)啊。這是因?yàn)樵?with Awith C之間還有一個(gè)with B是偷,C 里面的 super 其實(shí)是 with A,B,所以 B 的 printMessage() 覆蓋了 A 的 printMessage()蛋铆,所以with A,B里面的 printMessage() 是 B 的 printMessage(),所以留特,輸出的結(jié)果時(shí) BC玛瘸。若將 D 的聲明改為

class D with A, C {
  printMessage() => super.printMessage();
}

后,輸出的結(jié)果才是 AC糊渊。

使用 mixin 還有一點(diǎn)需要注意,mixin C on A渺绒,則 D 在 with C 之前一定要先 with A菱鸥,否則編譯器會(huì)給出相應(yīng)的錯(cuò)誤提示

錯(cuò)誤提示中已經(jīng)給出了相應(yīng)的解釋氮采,C 不能被混入到 Object 中,因?yàn)?Object 并沒(méi)有實(shí)現(xiàn) A鹊漠。

這里對(duì) mixin 做下簡(jiǎn)單的總結(jié):

  • mixin 可以理解為對(duì)類的一種“增強(qiáng)”茶行,但它與單繼承兼容,因?yàn)樗睦^承關(guān)系是線性的拢军。
  • with 后面的類會(huì)覆蓋前面的類的同名方法
  • 當(dāng)我們想要在不共享相同類層次結(jié)構(gòu)的多個(gè)類之間共享行為時(shí)怔鳖,可以使用 mixin
  • 當(dāng)聲明一個(gè) mixin 時(shí), on 后面的類是使用 這個(gè)mixin 的父類約束结执。也就是說(shuō)一個(gè)類若是要 with 這個(gè) mixin,則這個(gè)類必須繼承或?qū)崿F(xiàn)這個(gè) mixin 的父類約束

最后懂傀,看一下文章開(kāi)頭提到的 Animal 的完整示例

abstract class Animal {}

abstract class Mammal extends Animal {}

abstract class Bird extends Animal {}

abstract class Fish extends Animal {}

mixin Walker {
  void walk() {
    print("I'm walking");
  }
}

mixin Swimmer {
  void swim() {
    print("I'm swimming");
  }
}

mixin Flyer {
  void fly() {
    print("I'm flying");
  }
}
class Dolphin extends Mammal with Swimmer {}

class Bat extends Mammal with Walker, Flyer {}

class Cat extends Mammal with Walker {}

class Dove extends Bird with Walker, Flyer {}

class Duck extends Bird with Walker, Swimmer, Flyer {}

class Shark extends Fish with Swimmer {}

class FlyingFish extends Fish with Swimmer, Flyer {}

使用 mixin 之后蜡感,這些 mixin 的線性化繼承關(guān)系如下圖:


參考文章
https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末郑兴,一起剝皮案震驚了整個(gè)濱河市犀斋,隨后出現(xiàn)的幾起案子情连,更是在濱河造成了極大的恐慌,老刑警劉巖虫几,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挽拔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡螃诅,警方通過(guò)查閱死者的電腦和手機(jī)始腾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門空执,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人辨绊,你說(shuō)我怎么就攤上這事⌒桑” “怎么了默蚌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)绸吸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)锦茁,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任度帮,我火速辦了婚禮稿存,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瓣履。我一直安慰自己,他們只是感情好拂苹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布瓢棒。 她就那樣靜靜地躺著,像睡著了一般脯宿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上连霉,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天嗡靡,我揣著相機(jī)與錄音窟感,去河邊找鬼。 笑死柿祈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蜜自。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼重荠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼虚茶!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起媳危,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤冈敛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后暮蹂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仰泻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年滩届,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棠枉。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泡挺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娄猫,到底是詐尸還是另有隱情生闲,我是刑警寧澤月幌,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站扯躺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏缅帘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一逗栽、第九天 我趴在偏房一處隱蔽的房頂上張望失暂。 院中可真熱鬧彼宠,春花似錦弟塞、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)索昂。三九已至,卻和暖如春椒惨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背康谆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工嫉到, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屯碴。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像忱叭,于是被迫代替她去往敵國(guó)和親隔崎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子韵丑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345