Flutter 中不得不會的 mixin

Flutter 中不得不會的 mixin

mixin 是 Dart 中很是重要的概念,對於未接觸過此概念的Coder來講尤爲重要,最近看源碼的時候,因爲對 mixin 不熟悉致使理解出現誤差,走了不少彎路,因此這篇文章介紹一下 mixin 概念。編程

Dart 及 Engine 版本:編程語言

Engine • revision ae90085a84
Tools • Dart 2.10.4ide

請注意版本,不一樣的版本可能存在差別。函數

先來看下官方的定義:ui

Mixins are a way of reusing a class’s code in multiple class hierarchies.翻譯

Mixins 是一種在多個類層次結構中重用類代碼的方法。3d

在來看下 Wiki 的解釋:日誌

In object-oriented programming languages, a mixin (or mix-in) is a class that contains methods for use by other classes without having to be the parent class of those other classes. How those other classes gain access to the mixin's methods depends on the language. Mixins are sometimes described as being "included" rather than "inherited".code

Mixins encourage code reuse and can be used to avoid the inheritance ambiguity that multiple inheritance can cause (the "diamond problem"), or to work around lack of support for multiple inheritance in a language. A mixin can also be viewed as an interface with implemented methods. This pattern is an example of enforcing the dependency inversion principle.對象

翻譯以下:

在面向對象的編程語言中,mixin(或mix-in)是一個類,其中包含供其餘類使用的方法,而沒必要成爲其餘類的父類。 這些其餘類如何得到對mixin方法的訪問權限取決於語言。 混合素有時被描述爲「包含」而不是「繼承」。

Mixins鼓勵代碼重用,而且可用於避免多重繼承可能致使的繼承歧義(「鑽石問題」),或解決語言中對多重繼承的支持不足的問題。 混合也能夠看做是已實現方法的接口。 此模式是強制執行依賴關係反轉原理的示例。

看完這兩段介紹,可能依然對其比較模糊,沒關係,如今只需對其有個概念便可,下面會詳細介紹 Mixins 的用法,我我的的理解就是:Mixins 解決了沒法多重繼承的問題。

何時須要使用 Mixins

有以下場景:

定義一個基類人(Person),它有吃(eat)的方法。

有3個實際的人A、B、C,它們都繼承 Person,可是3我的有不一樣的技能:

  • A :會唱歌、跳舞
  • B:會跳舞、寫代碼
  • C:會唱歌、寫代碼

上面的場景中唱歌、跳舞、寫代碼是一種技能,並非每個人都會的,因此將其定義在 Person 中是不合適的,若是各自定義爲一個類,又不能同時繼承Person和唱歌、跳舞、寫代碼,若是將唱歌、跳舞、寫代碼定義爲 Interface ,那麼A、B、C中要各自實現其方法,

那要如何實現呢? Mixins 出場啦。

定義一個 Person 基類和功能類唱歌、跳舞、寫代碼:

class Person {
  eat() {
    print('Person eat');
  }
}

class Dance {
  dance() {
    print('Dance dance');
  }
}

class Sing {
  sing() {
    print('Sing sing');
  }
}

class Code {
  code() {
    print('Code code');
  }
}

定義A、B、C:

class A extends Person with Dance, Sing {}

class B extends Person with Sing, Code {}

class C extends Person with Code, Dance {}

注意:混合使用 with 關鍵字。

使用:

A a = A();
a.eat();
a.dance();
a.sing();

輸出日誌:

flutter: Person eat
flutter: Dance dance
flutter: Sing sing

能夠看到 A 中有了Dance 和Sing的相關的方法。

Dance 是一個 class,若是給其添加構造函數會如何?

給 Dance 添加構造函數,修改以下,

此時發現 A 和 C 沒法編譯,出現以下錯誤:

很明顯,須要 mixin 的類沒法定義構造函數。

因此通常會將須要 mixin 的類使用 mixin 關鍵字:

添加限定條件,使用關鍵字 on

接着上面的場景繼續,這時定義一個狗的類,目前狗這個類也能夠混合 Dance 、Sing 和 Code,

class Dog with Code{}

可是,Code 是人類獨有的技能,不但願 Dog 這個類能夠mixin,因此給 Code 添加限定條件:

使用關鍵字 on 限定Code 只能被 Person 或者其子類 mixin。

此時 Dog 沒法 mixin Code。

添加限定後,能夠重寫其方法, Code 重寫 Person 的方法:

super 表示調用父類(Person)的方法。

如何處理多個類有同一方法的狀況

假設有D 和 D1 兩個類,有同一個方法 d,E mixin D 和 D1:

此時,調用 e.d 方法:

E e = E();
e.d();

輸出:

flutter: D1 d

說明後面的將前面的覆蓋了,調換下D 和 D1的順序:

class E with D1, D {}

輸出:

flutter: D d

此時在 E 中也添加 d 方法:

輸出:

flutter: E d

說明 E 中 方法覆蓋了原來的。

E 中 d 方法能夠調用 super.d()

輸出:

flutter: D d
flutter: E d

假設如今有F、G、H 三個類,都有 a 方法,

有以下定義的類:

那麼下面會輸出什麼值:

答案是:

flutter: G a

記住:混合類時,進行混合的多個類是線性的,這是他們共有方法不衝突的緣由,混合的順序很是重要,由於它決定了混合時相同的方法的處理邏輯。

再次看下 FG 的混合狀況:

FG 繼承 H,混合 F 和 G,對於相同方法的優先級爲:G > F > H,所以共有方法 a,最後執行的是 G 類中的 a 方法。

那麼若是 FG 中也有 a 方法會如何?

若是自己(FG)也存在相同的方法那麼優先級:FG > G > F > H。super.a() 執行的是 G 中的 a 方法。

輸出結果:

flutter: G a
flutter: FG a

更復雜的來啦,請看以下混合關係:

BB 爲一個抽象類,有一個構造函數,其中執行 init 方法,GB 和 PB 爲一個混合類型,限定了只有 BB 或者其子類才能混合,WFB 繼承 BB,並混合GB、PB,此時建立 WFB 對象,

WFB wfb = WFB();

輸出結果是什麼?

flutter: BB Constructor
flutter: BB init
flutter: GB init
flutter: PB init

是否是很詫異,按照上面的邏輯不是應該只調用 PB 的 init 方法嗎?

你理解的沒有錯,的確只調用了PB 的 init 方法,可是 PB 的 init 方法中調用了super.init(),這個纔是重點,PB 經過 super.init 調用到了GB中的 init 方法, GB 經過 super.init 調用到了 BB 中的 init 方法,因此最終輸出的就是上面的結果。

這個必定要理解其中的調用順序,由於的 Flutter Framework 的入口函數 runApp 中就是此形式:

WidgetsFlutterBinding.ensureInitialized 方法以下:

WidgetsFlutterBinding 混合結構以下:

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {

BindingBase 及構造函數以下:

其執行了 initInstances 和 initServiceExtensions 方法。看下面混合的順序:

從後到前依次執行其 initInstances 和 initServiceExtensions(若是有) 方法,因爲 initInstances 和 initServiceExtensions 方法中首先執行 super.initInstances()super.initServiceExtensions() ,因此最後執行的順序爲:BindingBase -> GestureBinding -> SchedulerBinding -> ServicesBinding -> PaintingBinding -> SemanticsBinding -> RendererBindinsg -> WidgetsBinding 。

類型

仍是上面的F、G、H 三個類,那麼 FG 的類型是什麼,看下面的判斷會輸出什麼?

輸出:

flutter: FG is F : true
flutter: FG is G : true
flutter: FG is H : true

因此混合後的類型是超類的子類型。

總結

  1. Mixins 使咱們能夠在無需繼承父類的狀況下爲此類添加父類的「功能」,能夠在同一個類中具備一個父級和多個 mixin 組件。
  2. Mixins 不能夠聲明任何構造函數。
  3. Mixins 添加限定條件使用 on 關鍵字。
  4. 混合使用 with 關鍵字,with 後面能夠是 classabstract classmixin 的類型。
  5. Mixins 不是多重繼承,相反,它只是在多個層次結構中重用類中的代碼而無需擴展它們的一種方式。
   
相關文章
相關標籤/搜索