什麼是 Mixins
Mixin 是一種程式碼複用機制,能夠在彼此不相關的類別繼承體系當中共用程式碼,目的是批次為類別提供成員實作。概念上類似多重繼承,但它主要用途是為類別附加各種模組化功能。
使用 Mixin
透過關鍵字 with,後面接一個或多個 Mixin 名稱。
class Musician extends Performer with Musical {
// 程式碼
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
Code language: Dart (dart)
定義 Mixin
使用 mixin 關鍵字進行宣告。
補充說明:
mixin class(Dart 3.0 以上版本)可同時作為一般類別、Mixin 使用。
強制規範
mixin/mixin class不能撰寫extends- 不可宣告任何產生建構子(generative constructor)
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
class Musician with Musical {}
void main() {
var m = Musician();
m.canConduct = true;
m.entertainMe(); // Waving hands
}
Code language: Dart (dart)
執行結果
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playingCode language: CSS (css)
Mixin 需要呼叫外部成員
Mixin 自身無法實作的方法 / 屬性
Mixin 經常需要呼叫本身沒有實作的成員,底下提供四種標準做法。
在 Mixin 內宣告抽象成員
在 Mixin 宣告抽象方法,所有套用此 Mixin 的類別都必須實作這個抽象方法。
mixin Musician {
// 抽象方法,使用端必須實作
void playInstrument(String instrumentName);
void playPiano() {
playInstrument('Piano');
}
void playFlute() {
playInstrument('Flute');
}
}
// 使用類別必須覆寫實作
class Virtuoso with Musician {
@override
void playInstrument(String instrumentName) {
print('Plays the $instrumentName beautifully');
}
}
void main() {
var artist = Virtuoso();
artist.playPiano();
artist.playFlute();
}
Code language: Dart (dart)
Plays the Piano beautifully
Plays the Flute beautifully
透過抽象 Getter 存取宿主類別狀態
Mixin 定義抽象 getter,藉此讀取宿主類別的欄位,實現跨類別狀態存取。
/// 可以混入任何具有 name 屬性的類別,自動提供以 name 為基礎的 == 與 hashCode
mixin NameIdentity {
String get name;
@override
int get hashCode => name.hashCode;
@override
bool operator ==(Object other) =>
other is NameIdentity && name == other.name;
}
class Person with NameIdentity {
final String name;
Person(this.name);
}
void main() {
var p1 = Person("Alice");
var p2 = Person("Alice");
var p3 = Person("Bob");
print(p1 == p2); // true
print(p1 == p3); // false
}
Code language: JavaScript (javascript)
Mixin 實作介面 implements
Mixin 使用 implements 約束介面,強制套用的類別補齊介面所有方法。
abstract interface class Tuner {
void tuneInstrument();
}
mixin Guitarist implements Tuner {
void playSong() {
tuneInstrument();
print('Strums guitar majestically.');
}
}
class PunkRocker with Guitarist {
@override
void tuneInstrument() {
print("Don't bother, being out of tune is punk rock.");
}
}
void main() {
PunkRocker().playSong();
}
Code language: JavaScript (javascript)
Don't bother, being out of tune is punk rock.
Strums guitar majestically.
on 子句:限制 Mixin 僅能被特定父類別的子類別使用
on 的作用:只有繼承指定型別的類別,才能套用這個 Mixin。
同時 Mixin 內部可以呼叫父類別成員、執行 super.xxx()。
只有當你需要在 Mixin 內使用
super呼叫父類別方法時,才需要使用on!
class Musician {
void musicianMethod() {
print('Playing music!');
}
}
// 僅能套用在 Musician 的子類別
mixin MusicalPerformer on Musician {
void performerMethod() {
print('Performing music!');
super.musicianMethod(); // 呼叫被 on 約束的父類別方法
}
}
// SingerDancer 繼承 Musician,因此可以套用 MusicalPerformer
class SingerDancer extends Musician with MusicalPerformer {}
void main() {
SingerDancer().performerMethod();
}
Code language: Dart (dart)
Performing music!
Playing music!
SingerDancer 套用了 MusicalPerformer,因此可以使用裡面的 performerMethod 方法
混入的概念大致如此,一個類別可以直接取用另一個 Mixin 內的方法,直接進行使用。
⚠️ 錯誤範例:
如果類別沒有繼承
Musician,直接撰寫with MusicalPerformer會發生編譯錯誤。
class / mixin / mixin class 的差異
mixin:只能透過with使用,不能使用extends繼承class:可使用extends,無法直接當作 mixin 使用mixin class:- 可以透過
with作為 Mixin 使用 - 也可以透過
extends做為一般父類別
- 可以透過
規範同樣適用:
- mixin / mixin class 不能撰寫 extends、with
- class / mixin class 不能使用 on
mixin class Musician { void play() { print("Musician is playing"); } } // 作為 Mixin 使用 class Novice1 with Musician {} // 作為一般父類別繼承 class Novice2 extends Musician {} void main() { Novice1().play(); Novice2().play(); }Code language: Dart (dart)執行結果
D:\dartdemo\firstdart>dart run Building package executable... Built firstdart:firstdart. Musician is playing Musician is playingCode language: CSS (css)Mixins