在開始閱讀此篇文章之前,我們可以先思考下如下問題:
- 什么是 Mixin ?
- Mixin為什么會被設計出來,它解決了什么問題?
- 在 Mixin 被設計出來之前是如何解決此類問題?
- Mixin 使用場景是什么?
- 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,讓后讓子類去實現對應的接口呢?答案是當然可以的,但是每個子類都要去實現一個或者多個接口,這并不是一個優雅的解決方案。
那有沒有更優雅的解決方案呢?有,那就是 Mixin。mixin是面向對象程序設計語言中的類,提供了方法的實現。其他類可以訪問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 類。
- 第一步
with A
就是Object with A
,此時 super 就是 Object 類。 - 第二步
with B
,由于 mixin B 是 on A 的,所以對于 B 來說,其 super 就是 A。則 B 的 printMessage() 中會調用 A 的 printMessage()。 - 第三步
with C
,由于 mixin C 是 on B 的,所以對于 C 來說,其 super 就是 B。則 C 的 printMessage() 中會調用 B 的 printMessage()。 - 第四步,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 A
和 with 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