深入理解 Dart 中的 Mixin

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

  1. 什么是 Mixin ?
  2. Mixin為什么會被設計出來,它解決了什么問題?
  3. 在 Mixin 被設計出來之前是如何解決此類問題?
  4. Mixin 使用場景是什么?
  5. Mixin 具體如何使用?

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


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

假設一個類可以繼承多個父類,那我們可以創建三個類:Walk、Swim、Fly。只需要 Dove 和 Cat 都再繼承 Walk 就可以滿足。但是 Dart 是單繼承的,所以此種方法無法實現。

那我們是不是可以創建三個接口類:Walk、Swim、Fly,讓后讓子類去實現對應的接口呢?答案是當然可以的,但是每個子類都要去實現一個或者多個接口,這并不是一個優雅的解決方案。

那有沒有更優雅的解決方案呢?有,那就是 Mixinmixin是面向對象程序設計語言中的類,提供了方法的實現。其他類可以訪問mixin類的方法、變量而不必成為其子類。Mixin 的作用就是在多個類層次結構中重用類的代碼。光聽概念,我們可能不太好理解,先來第一個例子的代碼

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);
}

我們先不看程序運行的結果,我們先來看看 類 AB 定義

class AB extends P with A, B {}

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

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

class AB extends PAB {}

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

在 AB 和 P 之間創建新類,這些新類是超類 P 與 A 類和 B 類之間的混合類。

現在,我們再來看下上面代碼執行的結果:答案是 “BA”

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

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

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

  • with 后面的類會覆蓋前面的類的同名方法

為了加深對這個規律的理解,我再來看第二個例子

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();
}

在這個例子中,由于 D 并沒有繼承類,則默認繼承 Object 類。

  1. 第一步 with A 就是 Object with A,此時 super 就是 Object 類。
  2. 第二步 with B,由于 mixin B 是 on A 的,所以對于 B 來說,其 super 就是 A。則 B 的 printMessage() 中會調用 A 的 printMessage()。
  3. 第三步 with C,由于 mixin C 是 on B 的,所以對于 C 來說,其 super 就是 B。則 C 的 printMessage() 中會調用 B 的 printMessage()。
  4. 第四步,D 繼承的就是 ABC 的混合類,由于 A、B、C 三個類都有同名方法,則 B 會覆蓋 A 的同名方法,C 會覆蓋 B 的同名方法,最終ABC 的混合類中的方法就是 C 的 printMessage()。D 中的 super 就是 ABC 的混合類。

經過上面四個步驟,D().printMessage()最終的輸出結果就是:

我們將上面的例子代碼稍微改動下

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();
}

大家思考一下,這個例子的輸出結果是什么???
|
|

|

|

|

|

|

|

|

|

|
|
|
|
|
|
|
輸出的結果是


有人可能會說,mixin C on A,那么 C 中的 super 不就是 A 嗎,結果應該是 AC 才對啊。這是因為在 with Awith C之間還有一個with B,C 里面的 super 其實是 with A,B,所以 B 的 printMessage() 覆蓋了 A 的 printMessage(),所以with A,B里面的 printMessage() 是 B 的 printMessage(),所以,輸出的結果時 BC。若將 D 的聲明改為

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

后,輸出的結果才是 AC。

使用 mixin 還有一點需要注意,mixin C on A,則 D 在 with C 之前一定要先 with A,否則編譯器會給出相應的錯誤提示

錯誤提示中已經給出了相應的解釋,C 不能被混入到 Object 中,因為 Object 并沒有實現 A。

這里對 mixin 做下簡單的總結:

  • mixin 可以理解為對類的一種“增強”,但它與單繼承兼容,因為它的繼承關系是線性的。
  • with 后面的類會覆蓋前面的類的同名方法
  • 當我們想要在不共享相同類層次結構的多個類之間共享行為時,可以使用 mixin
  • 當聲明一個 mixin 時, on 后面的類是使用 這個mixin 的父類約束。也就是說一個類若是要 with 這個 mixin,則這個類必須繼承或實現這個 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 的線性化繼承關系如下圖:


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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。