【譯】Dart | 什麼是Mixin

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

這篇文章來自Romain Rastel撰寫的Medium,感謝Romain容許我將你精彩的文章翻譯成中文!github

原文連接:medium.com/flutter-com…bash

當我開始學習Dart時,mixins對我來講是一個新的的概念。 我從C#轉過來,Mixin這個概念是不存在的(據我所知,至少在C#8.0以前不存在)。 起初,我發現這個概念有點難以理解,直到如今我才意識到它有多麼強大。服務器

免責聲明: Mixins在Dart 2中不斷髮展。本文一些內容將來可能將會再也不適用。app

🤔爲何咱們須要Mixin

咱們先來看看下面的類繼承圖:學習

咱們這裏有一個名爲Animal的超類,它有三個子類(Mammal,Bird和Fish)。在底部,咱們有具體的一些子類。 小方塊表明行爲。例如,藍色方塊表示具備此行爲的類的實例能夠swim。

有些動物有共同的行爲:貓和鴿子均可以行走,可是貓不能飛(除了Nyan Cat😀)。 這些行爲與此分類正交,所以咱們沒法在超類中實現這些行爲。ui

若是一個類能夠擁有多個超類,那就很容易辦到了。咱們能夠建立另外三個類:Walker,Swimmer,Flyer。在那以後,咱們只需從Walker類繼承Dove和Cat。但在Dart中,每一個類(除了Object類)都只有一個超類。this

咱們能夠實現它,而不是繼承自Walker類,由於它是一個接口,但咱們必須在多個類中實現行爲,所以它並非一個好的解決方案。spa

咱們須要一種在多個類層次結構中重用類的代碼的方法。 Mixin就可以辦到這一點!命令行

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

🔒限制

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

  • 在Dart 1.12或更低版本使用Mixin時必須繼承至Object,而且不能調用super()方法。
  • SuperMixin:Dart 1.13或更高版本支持繼承至Object之外的類並使用Mixin,並且能夠調用super.method()。這項支持僅在DartVM中默認開啓,而且在標誌後面的Analyzer中才可以使用。更具體地說,它位於命令行分析器中的--supermixin標誌以後。它也能夠在分析服務器中,在客戶端可配置選項後面。Dart2js和dartdevc不支持SuperMixin。
  • 在Dart 2.1中,mixins預計會有更少的限制。例如,Flutter支持mixins調用super()並從Object之外的類擴展,可是這些在出如今全部Dart SDK中以前,語法有可能會發生變化。

📝語法

咱們明白了mixins爲何如此有用,下面讓咱們看看如何建立和使用它們。

Mixins經過普通的類聲明隱式定義:

class Walker {
  void walk() {
    print("I'm walking");
  }
}
複製代碼

若是咱們不想讓咱們建立的mixin被實例化或擴展,咱們能夠像這樣定義它:

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關鍵字,後跟一個或多個mixin的名稱:

class Cat extends Mammal with Walker {}

class Dove extends Bird with Walker, Flyer {}
複製代碼

我在Cat類上定義了Walker mixin,它容許咱們調用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.
}
複製代碼

🔎 詳情

我告訴過你我發現這個概念有點難以理解,可是到目前爲止它看上去並不那麼難是嗎?

哈哈。

那麼,你能告訴咱們如下程序的輸出是什麼嗎😵?

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個),B和P類都有一個名爲getMessage的方法。

首先,咱們調用AB類的getMessage方法,而後調用BA類的getMessage方法。

那麼你認爲,結果是什麼?

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

...

🥁🥁🥁噹噹噹~答案是B!這個程序將打印BA。

我想你在猜想mixins的聲明順序很是重要。

Why?它是如何工做的?

✨線性化

當您將mixin混入類中時,請記住下面這句話:

'Dart中的Mixins經過建立一個新類來實現,該類將mixin的實現層疊在一個超類之上以建立一個新類 ,它不是「在超類中」,而是在超類的「頂部」,所以如何解決查找問題不會產生歧義。

— Lasse R. H. Nielsen on StackOverflow.'

實際上,這段代碼

class AB extends P with A, B {}

class BA extends P with B, A {}
複製代碼

在語義上等同於

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 {}
複製代碼

最終的繼承關係能夠用下圖表示:

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

正如你所看到的那樣,咱們並無使用多重繼承!

  • Mixins不是一種在經典意義上得到多重繼承的方法。
  • Mixins是一種抽象和重用一系列操做和狀態的方法。
  • 它相似於擴展類所得到的重用,但它與單繼承兼容,由於它是線性的。

— Lasse R. H. Nielsen on StackOverflow.

聲明mixins的順序表明了從最高級到最高級的繼承鏈,這件事很是重要,你須要記住。

📄類型

mixin應用程序實例的類型是什麼?

一般,它是其超類的子類型,也是mixin名稱自己表示的類的子類型,即原始類的類型。 — dartlang.org

因此這意味着這個程序

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);
}
複製代碼

將在控制檯中打印六行true。

詳細解釋

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

因爲每一個mixin應用程序都建立一個新類,它還會建立一個新接口(由於全部Dart類也定義了接口)。 如上所述,新類擴展了超類幷包含了mixin類成員的副本,但它也實現了mixin類接口。

在大多數狀況下,沒法引用mixin-application類或其接口。

Super with Mixin的類只是類的匿名超類,聲明相似C類使用Mixin {}擴展Super。 若是你將一個mixin應用程序命名爲類CSuper = Super with Mixin {},那麼你能夠參考mixin應用程序類及其接口,它將是Super和Mixin的子類型。

— Lasse R. H. Nielsen

🙄何時應該使用mixins?

當咱們想要在不共享相同類層次結構的多個類之間共享行爲時,或者在超類中實現此類行爲沒有意義時,Mixins很是有用。

一般狀況下是序列化(例如,查看jaguar_serializer)或持久化。 可是你也可使用mixins來提供一些實用功能(好比Flutter中的RenderSliverHelpers)。

花點時間玩這個功能,我相信你會找到新的用例😉。 不要侷限於無狀態mixins,你絕對能夠存儲變量並使用它們!

📈Mixins的規範正在發展

若是你對Dart語言的演變感興趣,你應該知道它的規範將在Dart 2.1中發展,他們會喜歡你的反饋。 有關詳細信息,請閱讀此內容

爲了讓您瞭解將來的一些趨勢,請考慮如下源代碼:

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上的超類約束。 這意味着爲了將這個mixin用在這裏,這個類必須繼承或實現Super,由於mixin使用了Super提供的功能。

這個程序的輸出是:

MySuper
Sub
複製代碼

若是你想知道爲何,請回憶mixins是如何線性化的:

第15行調用super.method()實際上調用了第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是如何線性化的:

📗總結

咱們看到mixins是一個強大的概念,容許您跨多個類層次結構重用代碼。

Flutter常用到這個功能,我以爲更好地理解它很是重要,這就是爲何我跟你分享個人理解。

感謝Jeroen Meijer的校對。

若是您對Mixin十分感興趣,歡迎在下方與我留言,或者聯繫我,咱們一塊兒討論!😃

相關文章
相關標籤/搜索