Flutter學習系列(一)—mixins

若少軒發表於2021-05-13

本文參考並翻譯至: medium.com/flutter-com…

什麼是Mixins

Mixins are a way of reusing a class's code in multiple class hierarchies.

Mixins的字面意思是混入的意思,官方給出的定義是:Mixins是在多繼承層次中重用程式碼的一種方式。

mixin是通過普通的類宣告隱式定義的,要使用mixin,需要使用with關鍵字來實現。 mixin的使用非常簡單,通過下面的例子來看下mixin的使用:

class MixinClass {
    void method() {
        print("mixin method");
    }
}

class User with MixinClass {

}
複製程式碼

在我們的User類中混入MixinClass類,我們就可以使用MixinClass類中的方法。輸出User().method()將會列印出mixin method。

Mixins的詳細用法

如果只是在我們的類中混入一個類,使用起來非常簡單。但是Mixins在特殊情況下的使用,並不那麼簡單,下面我將就其進行詳細說明。

1、混入類中的順序

首先,我們看一個例子

// 父類
class Super {
    void method() {
        print("Super class method");
    }
}

// 混入類1
class M1 {
    void method() {
        print("M1 class method");
    }
}

// 混入類2
class M2 {
    void method() {
        print("M2 class method");
    }
}

// 子類1實現
class Child1 extends Super with M1, M2 {}
// 子類2實現 
class Child2 extends Super with M2, M1 {}

void main() {
    Child1().method();
    Child2().method();
}
複製程式碼

在上面的這個例子中,Child1和Child2都是混入了M1和M2類,但是M1和M2的順序是不一樣的,執行他們的輸出結果:

M2 class method
M1 class method
複製程式碼

我們可以看出來,mixins宣告的順序影響了方法的輸出結果,同時後面mixins的類方法會覆蓋前面mixins類的方法,針對這種情況,Lasse R.H.Nielsen大神給出瞭解釋:

Mixins in Dart work by creating a new class that layers the implementation of the mixin on top of a superclass to create a new class — it is not “on the side” but “on top” of the superclass, so there is no ambiguity in how to resolve lookups.
複製程式碼

翻譯成中文為:在Dart語言中,Mixins通過建立一個新類來工作,這個新類將mixin的實現層放在了父類之上。這裡重點強調了這個新類是在父類之上,並不是在旁邊。因此,在解析查詢方面沒有任何歧義。

結合Lasse R.H.Nielsen大神的解釋,我們重新看下這段程式碼:

class Child1 extends Super with M1, M2 {}

class Child2 extends Super with M2, M1 {}
複製程式碼

上面這段程式碼可以等價於:

// Child1
class Mixin1_1 => Super with M1;
class Mixin1_2 => Mixin1_1 with M2;
class Child1 extends Mixin1_2;

// Child2
class Mixin2_1 => Super with M2;
class Mixin2_2 => Mixin2_1 with M1;
class Child2 extends Mixin2_2;
複製程式碼

通過這樣寫,我們基本上已經找到了上面輸出結果的原因了。這些新類建立在父類和子類中間,是一個mix_in類,並沒有表現出多重繼承。

Lasse R.H.Nielsen大神又給出了這麼一種解釋:

Mixins is not a way to get multiple inheritance in the classical sense. Mixins is a way to abstract and reuse a family of operations and state. It is similar to the reuse you get from extending a class, but it is compatible with single-inheritance because it is linear.
複製程式碼

這裡翻譯成中文為:Mixins並不是傳統意義上實現多重繼承的一種方式。Mixin是實現抽象和重用一系列方法和狀態的一種方式。Mixins和擴充套件類中的重用是相似的,Mixins也和單繼承相容,它是一種線性關係。

注意:mixins的順序代表著從父類到子類繼承的順序

通過上面的解釋,我們繼續剛才的例子,如果我們在Child1或者Child2中實現了method方法

class Child1 extends Super with M1, M2 {
    void method() => print("Child1 class method");
}

class Child2 extends Super with M2, M1 {
    void method() => print("Child2 class method");
}
複製程式碼

執行後的輸入結果同樣也是顯而易見的:

Child1 class method
Child2 class method
複製程式碼

最後,我們用繼承關係圖(並不是真正的繼承關係,想象成繼承關係)更加直觀的表示下這個例子: image.png

2、混入類中新增限定

首先,我們來看一個例子

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

mixin MixinClass on Super {
    void method() => print("Mixin class method");
}

class Child extends Super with MixinClass {}
複製程式碼

在這個例子中我們通過mixin關鍵字宣告瞭混入類Mixin,通過關鍵字on新增了父類限制,這意味著如果一個類想要引入這個混入類,這個類必須繼承或者實現這個父類。 在這個例子中如果寫成這樣:

class Child with MixinClass {}
複製程式碼

則會報編譯錯誤:

'MixinClass' can't be mixed onto 'Object' because 'Object' doesn't implement 'MixinClass'.
Try extending the class 'MixinClass'
複製程式碼

現在我們繼續修改下這個例子,給混入類新增super方法

mixin MixinClass on Super {
    void method() {
        super.method();
        print("Mixin class method");
    }
}
複製程式碼

執行Child().method()後輸出為:

Super class method
Mixin class method
複製程式碼

回過頭去看下1、混入類中的順序中的繼承關係圖,那麼得出這個結果就是顯而易見的。

WidgetsFlutterBinding中mixins的應用

在前面我們學習到了Mixins使用,Flutter中關於Mixins的最重要的使用就是WidgetsFlutterBinding類,WidgetsFlutterBinding在Flutter進行初始化的時候建立,依次進行了手勢、排程、繪製等的繫結。我們這次的目的就是說明下各個Binding的執行順序。 WidgetsFlutterBinding具體實現如下:

// 1、BindingBase
abstract class BindingBase {
  BindingBase() {
    initInstances();
  }
}

// 2、GestureBinding
mixin GestureBinding on BindingBase {
  void initInstances() {
    super.initInstances();
  }
}

// 3、WidgetsFlutterBinding
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding();
    return WidgetsBinding.instance;
  }
}
複製程式碼

針對以上程式碼,我們分析過程如下:

  1. 定義了一個抽象類BindingBase,其建構函式執行了初始化initInstances()。
  2. 定義了一個混入類GestureBinding(ServuceBinding等類實現原理相同),新增了父類限制BindingBase,引入類中必須實現繼承/實現BindingBase類。
  3. 定義了一個實現類WidgetsFlutterBinding,繼承BindingBase,混入了GestureBinding、ServiceBinding等類。
  4. Flutter初始化時執行WidgetsFlutterBinding.ensureInitialized()單例方法,建立WidgetsFlutterBinding物件,執行其建構函式,這裡執行的是BindingBase建構函式。
  5. BindingBase建構函式執行initInstance(),通過前面繼承關係圖可知,繼承關係如下:WidgetsFlutterBinding->WidgetsBinding->RendererBinding->...->GestureBinding->BindingBase。執行的是WidgetsBinding的initInstance()方法。
  6. WidgetsBinding中initInstance()方法呼叫了super.initInstance()方法,最終initInstance()執行順序為GestureBinding->ServiceBinding->...->WidgetsBinding。

總結

通過以上我們瞭解到了Mixins的用法,以及其在WidgetsFlutterBinding類中的使用。通過閱讀本篇文章希望能對大家瞭解Mixins有所幫助。新人第一次嘗試寫文章,其中不當的地方也歡迎大家指正。

相關文章