在開(kāi)始閱讀此篇文章之前,我們可以先思考下如下問(wèn)題:
- 什么是 Mixin ?
- Mixin為什么會(huì)被設(shè)計(jì)出來(lái)休偶,它解決了什么問(wèn)題辜羊?
- 在 Mixin 被設(shè)計(jì)出來(lái)之前是如何解決此類問(wèn)題椅贱?
- Mixin 使用場(chǎng)景是什么只冻?
- 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 類。
- 第一步
with A
就是Object with A
合溺,此時(shí) super 就是 Object 類缀台。 - 第二步
with B
棠赛,由于 mixin B 是 on A 的膛腐,所以對(duì)于 B 來(lái)說(shuō),其 super 就是 A辩涝。則 B 的 printMessage() 中會(huì)調(diào)用 A 的 printMessage()勘天。 - 第三步
with C
怔揩,由于 mixin C 是 on B 的脯丝,所以對(duì)于 C 來(lái)說(shuō),其 super 就是 B晕拆。則 C 的 printMessage() 中會(huì)調(diào)用 B 的 printMessage()材蹬。 - 第四步,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 A
和 with 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