深入理解 Dart 中的 Mixin

在開始閱讀此篇文章之前,我們可以先思考下如下問題:

  1. 什么是 Mixin ?
  2. Mixin為什么會(huì)被設(shè)計(jì)出來,它解決了什么問題?
  3. 在 Mixin 被設(shè)計(jì)出來之前是如何解決此類問題?
  4. Mixin 使用場(chǎng)景是什么?
  5. Mixin 具體如何使用?

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


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

假設(shè)一個(gè)類可以繼承多個(gè)父類,那我們可以創(chuàng)建三個(gè)類:Walk、Swim、Fly。只需要 Dove 和 Cat 都再繼承 Walk 就可以滿足。但是 Dart 是單繼承的,所以此種方法無法實(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)雅的解決方案。

那有沒有更優(yōu)雅的解決方案呢?有,那就是 Mixin。mixin是面向?qū)ο蟪绦蛟O(shè)計(jì)語言中的類,提供了方法的實(shí)現(xiàn)。其他類可以訪問mixin類的方法、變量而不必成為其子類。Mixin 的作用就是在多個(gè)類層次結(jié)構(gòu)中重用類的代碼。光聽概念,我們可能不太好理解,先來第一個(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é)果,我們先來看看 類 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è)賮砜聪律厦娲a執(zhí)行的結(jié)果:答案是 “BA”

P with A得到了 PA,并且 如果 A 與 P 中有同名方法,則 A 會(huì)覆蓋 P 的同名方法,也就是說 PA 中的與 P 同名的方法就是 A 中的方法。同理,PAB 中的與 P 同名的方法就是 B 中的方法.

因?yàn)?PA 繼承 P,PA 與 P 有同名方法 getMessage(),則 PA 重載了 P 的 getMessage(),同理 PAB 重載了 PA 的 getMessage(),由于 AB 是繼承自 PAB 并且 AB 并沒有重載 getMessage(),所以 ab.getMessage() 是調(diào)用 PAB 的 getMessage(),而 PAB 的 getMessage() 就是 B 的 getMessage(),所以 ab.getMessage() 輸出的結(jié)果是 B,同理 ba.getMessage() 輸出的結(jié)果是 A。

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

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

為了加深對(duì)這個(gè)規(guī)律的理解,我再來看第二個(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 并沒有繼承類,則默認(rèn)繼承 Object 類。

  1. 第一步 with A 就是 Object with A,此時(shí) super 就是 Object 類。
  2. 第二步 with B,由于 mixin B 是 on A 的,所以對(duì)于 B 來說,其 super 就是 A。則 B 的 printMessage() 中會(huì)調(diào)用 A 的 printMessage()。
  3. 第三步 with C,由于 mixin C 是 on B 的,所以對(duì)于 C 來說,其 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)過上面四個(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ì)說,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 并沒有實(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 的父類約束。也就是說一個(gè)類若是要 with 這個(gè) mixin,則這個(gè)類必須繼承或?qū)崿F(xiàn)這個(gè) mixin 的父類約束

最后,看一下文章開頭提到的 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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容