【譯】Dart | 什么是Mixin

This article is from Medium written by Romain Rastel, Thank you Romain for allowing me translate your awesome article into Chinese!

這篇文章來(lái)自Romain Rastel撰寫的Medium,感謝Jorge允許我將你精彩的文章翻譯成中文

原文鏈接:https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3

當(dāng)我開始學(xué)習(xí)Dart時(shí),mixins對(duì)我來(lái)說(shuō)是一個(gè)新的的概念。
我從C#轉(zhuǎn)過(guò)來(lái),Mixin這個(gè)概念是不存在的(據(jù)我所知,至少在C#8.0之前不存在)。
起初,我發(fā)現(xiàn)這個(gè)概念有點(diǎn)難以理解,直到現(xiàn)在我才意識(shí)到它有多么強(qiáng)大。

免責(zé)聲明: Mixins在Dart 2中不斷發(fā)展。本文一些內(nèi)容未來(lái)可能將會(huì)不再適用。

??為什么我們需要Mixin

我們先來(lái)看看下面的類繼承圖:

image

我們這里有一個(gè)名為Animal的超類,它有三個(gè)子類(Mammal,Bird和Fish)。在底部,我們有具體的一些子類。
小方塊代表行為。例如,藍(lán)色方塊表示具有此行為的類的實(shí)例可以swim。

有些動(dòng)物有共同的行為:貓和鴿子都可以行走,但是貓不能飛(除了Nyan Cat??)。
這些行為與此分類正交,因此我們無(wú)法在超類中實(shí)現(xiàn)這些行為。

如果一個(gè)類可以擁有多個(gè)超類,那就很容易辦到了。我們可以創(chuàng)建另外三個(gè)類:Walker,Swimmer,F(xiàn)lyer。在那之后,我們只需從Walker類繼承Dove和Cat。但在Dart中,每個(gè)類(除了Object類)都只有一個(gè)超類。

我們可以實(shí)現(xiàn)它,而不是繼承自Walker類,因?yàn)樗且粋€(gè)接口,但我們必須在多個(gè)類中實(shí)現(xiàn)行為,因此它并不是一個(gè)好的解決方案。

我們需要一種在多個(gè)類層次結(jié)構(gòu)中重用類的代碼的方法。
Mixin就能夠辦到這一點(diǎn)!

'Mixins are a way of reusing a class’s code in multiple class hierarchies.
— dartlang.org'

??限制

mixin功能有一些限制(來(lái)自dartlang.org):

  • 在Dart 1.12或更低版本使用Mixin時(shí)必須繼承至Object,并且不能調(diào)用super()方法。
  • SuperMixin:Dart 1.13或更高版本支持繼承至Object以外的類并使用Mixin,而且可以調(diào)用super.method()。這項(xiàng)支持僅在DartVM中默認(rèn)開啟,并且在標(biāo)志后面的Analyzer中才能夠使用。更具體地說(shuō),它位于命令行分析器中的--supermixin標(biāo)志之后。它也可以在分析服務(wù)器中,在客戶端可配置選項(xiàng)后面。Dart2js和dartdevc不支持SuperMixin。
  • 在Dart 2.1中,mixins預(yù)計(jì)會(huì)有更少的限制。例如,F(xiàn)lutter支持mixins調(diào)用super()并從Object以外的類擴(kuò)展,但是這些在出現(xiàn)在所有Dart SDK中之前,語(yǔ)法有可能會(huì)發(fā)生變化。

??語(yǔ)法

我們明白了mixins為什么如此有用,下面讓我們看看如何創(chuàng)建和使用它們。

Mixins通過(guò)普通的類聲明隱式定義:

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

如果我們不想讓我們創(chuàng)建的mixin被實(shí)例化或擴(kuò)展,我們可以像這樣定義它:

abstract class Walker {
  // This class is intended to be used as a mixin, and should not be
  // extended directly.
  factory Walker._() => null;

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

要使用mixin的話,你需要使用with關(guān)鍵字,后跟一個(gè)或多個(gè)mixin的名稱:

class Cat extends Mammal with Walker {}

class Dove extends Bird with Walker, Flyer {}

我在Cat類上定義了Walker mixin,它允許我們調(diào)用walk方法而不是fly方法(在Flyer中定義)。

main(List<String> arguments) {
  Cat cat = Cat();
  Dove dove = Dove();

  // A cat can walk.
  cat.walk();

  // A dove can walk and fly.
  dove.walk();
  dove.fly();

  // A normal cat cannot fly.
  // cat.fly(); // Uncommenting this does not compile.
}

?? 詳情

我告訴過(guò)你我發(fā)現(xiàn)這個(gè)概念有點(diǎn)難以理解,但是到目前為止它看上去并不那么難是嗎?

哈哈。

那么,你能告訴我們以下程序的輸出是什么嗎???

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和BA類都使用A和B mixins繼承至P類,但順序不同。
所有的A(3個(gè)),B和P類都有一個(gè)名為getMessage的方法。

首先,我們調(diào)用AB類的getMessage方法,然后調(diào)用BA類的getMessage方法。

那么你認(rèn)為,結(jié)果是什么?

  • A. It does not compile
  • B. BA
  • C. AB
  • D. BAAB
  • E. ABBA

...

??????當(dāng)當(dāng)當(dāng)~答案是B!這個(gè)程序?qū)⒋蛴A。

我想你在猜測(cè)mixins的聲明順序非常重要。

Why?它是如何工作的?

?線性化

當(dāng)您將mixin混入類中時(shí),請(qǐng)記住下面這句話:

'Dart中的Mixins通過(guò)創(chuàng)建一個(gè)新類來(lái)實(shí)現(xiàn),該類將mixin的實(shí)現(xiàn)層疊在一個(gè)超類之上以創(chuàng)建一個(gè)新類 ,它不是“在超類中”,而是在超類的“頂部”,因此如何解決查找問(wèn)題不會(huì)產(chǎn)生歧義。

— Lasse R. H. Nielsen on StackOverflow.'

實(shí)際上,這段代碼

class AB extends P with A, B {}

class BA extends P with B, A {}

在語(yǔ)義上等同于

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

class AB extends PAB {}

class PB = P with B;
class PBA = PB with A;

class BA extends PBA {}

最終的繼承關(guān)系可以用下圖表示:

image

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

正如你所看到的那樣,我們并沒(méi)有使用多重繼承!

  • Mixins不是一種在經(jīng)典意義上獲得多重繼承的方法。
  • Mixins是一種抽象和重用一系列操作和狀態(tài)的方法。
  • 它類似于擴(kuò)展類所獲得的重用,但它與單繼承兼容,因?yàn)樗蔷€性的。

— Lasse R. H. Nielsen on StackOverflow.

聲明mixins的順序代表了從最高級(jí)到最高級(jí)的繼承鏈,這件事非常重要,你需要記住。

??類型

mixin應(yīng)用程序?qū)嵗念愋褪鞘裁矗?/h2>

通常,它是其超類的子類型,也是mixin名稱本身表示的類的子類型,即原始類的類型。
— dartlang.org

所以這意味著這個(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() {
  AB ab = AB();
  print(ab is P);
  print(ab is A);
  print(ab is B);

  BA ba = BA();
  print(ba is P);
  print(ba is A);
  print(ba is B);
}

將在控制臺(tái)中打印六行true。

詳細(xì)解釋

Lasse R. H. Nielsen給了我一個(gè)很棒的解釋:

由于每個(gè)mixin應(yīng)用程序都創(chuàng)建一個(gè)新類,它還會(huì)創(chuàng)建一個(gè)新接口(因?yàn)樗蠨art類也定義了接口)。
如上所述,新類擴(kuò)展了超類并包含了mixin類成員的副本,但它也實(shí)現(xiàn)了mixin類接口。

在大多數(shù)情況下,無(wú)法引用mixin-application類或其接口。

Super with Mixin的類只是類的匿名超類,聲明類似C類使用Mixin {}擴(kuò)展Super。
如果你將一個(gè)mixin應(yīng)用程序命名為類CSuper = Super with Mixin {},那么你可以參考mixin應(yīng)用程序類及其接口,它將是Super和Mixin的子類型。

— Lasse R. H. Nielsen

??什么時(shí)候應(yīng)該使用mixins?

當(dāng)我們想要在不共享相同類層次結(jié)構(gòu)的多個(gè)類之間共享行為時(shí),或者在超類中實(shí)現(xiàn)此類行為沒(méi)有意義時(shí),Mixins非常有用。

通常情況下是序列化(例如,查看jaguar_serializer)或持久化。
但是你也可以使用mixins來(lái)提供一些實(shí)用功能(比如Flutter中的RenderSliverHelpers)。

花點(diǎn)時(shí)間玩這個(gè)功能,我相信你會(huì)找到新的用例??。
不要局限于無(wú)狀態(tài)mixins,你絕對(duì)可以存儲(chǔ)變量并使用它們!

??Mixins的規(guī)范正在發(fā)展

如果你對(duì)Dart語(yǔ)言的演變感興趣,你應(yīng)該知道它的規(guī)范將在Dart 2.1中發(fā)展,他們會(huì)喜歡你的反饋。
有關(guān)詳細(xì)信息,請(qǐng)閱讀此內(nèi)容。

為了讓您了解未來(lái)的一些趨勢(shì),請(qǐng)考慮以下源代碼:

abstract class Super {
  void method() {
    print("Super");
  }
}

class MySuper implements Super {
  void method() {
    print("MySuper");
  }
}

mixin Mixin on Super {
  void method() {
    super.method();
    print("Sub");
  }
}

class Client extends MySuper with Mixin {}

void main() {
  Client().method();
}

第13行到第18行的mixin聲明表示Super上的超類約束。
這意味著為了將這個(gè)mixin用在這里,這個(gè)類必須繼承或?qū)崿F(xiàn)Super,因?yàn)閙ixin使用了Super提供的功能。

這個(gè)程序的輸出是:

MySuper
Sub

如果你想知道為什么,請(qǐng)回憶mixins是如何線性化的:

image

第15行調(diào)用super.method()實(shí)際上調(diào)用了第8行聲明的方法。

??完整的 Animal example

你可以在下面找到我用它介紹mixins的完整示例:

abstract class Animal {}

abstract class Mammal extends Animal {}

abstract class Bird extends Animal {}

abstract class Fish extends Animal {}

abstract class Walker {
  // This class is intended to be used as a mixin, and should not be
  // extended directly.
  factory Walker._() => null;

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

abstract class Swimmer {
  // This class is intended to be used as a mixin, and should not be
  // extended directly.
  factory Swimmer._() => null;

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

abstract class Flyer {
  // This class is intended to be used as a mixin, and should not be
  // extended directly.
  factory Flyer._() => null;

  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是如何線性化的:

image

??總結(jié)

我們看到mixins是一個(gè)強(qiáng)大的概念,允許您跨多個(gè)類層次結(jié)構(gòu)重用代碼。

Flutter經(jīng)常使用到這個(gè)功能,我覺(jué)得更好地理解它非常重要,這就是為什么我跟你分享我的理解。

感謝Jeroen Meijer的校對(duì)。

如果您對(duì)Mixin十分感興趣,歡迎在下方與我留言,或者聯(lián)系我,我們一起討論!??

?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

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