一、概念
“Mixins are a way of reusing a class’s code in multiple class hierarchies.” 混合是一種在多個類層次結(jié)構(gòu)中重用類代碼的方法。
從概念上我們可以理解它是為了解決代碼重用的一種方式。學(xué)習(xí)java的小伙伴可能會想到interface,Dart中的類都可以作為接口,這不是已經(jīng)有了
解決方案了嗎?為什么還需要mixin呢?
二、使用
首先我們來解決上面提到的問題,為什么要使用mixin。
我們很多語言都是采用了單繼承+接口多實現(xiàn)的方式,但是這種方式不能很好的適用于所有場景。
我們看下下面這個假設(shè)的例子:
我們通過上圖可以看出,這些學(xué)生都有一個共同的父類Student,然后又有三個抽象子類:文科班學(xué)生,理科班學(xué)生、藝術(shù)班學(xué)生。這些類具有相同的行為和能力,但是有的類又有自己獨有的行為和能力。比如文科、理科、藝術(shù)班學(xué)生都可以上數(shù)學(xué)課程,
但是只有理科班學(xué)生可以上物理課程,文科班和藝術(shù)班學(xué)生可以上地理課程,物理班學(xué)生又不可以上地理課程。那么問題就出現(xiàn)了,部分具有相同能力和行為的子類都要保留一份相同的代碼實現(xiàn),比如文科班學(xué)生類和藝術(shù)班學(xué)生類都要實現(xiàn)一份上地理課的實現(xiàn),
就產(chǎn)生了冗余。這種單繼承模型下,無法把部分子類具有相同行為能力抽象到基類中,因為對其它不具有此行為的子類來說是不合適的,例如把上地理課放在基類student中,因此只能在各自子類中實現(xiàn)。
abstract class Student{
}
abstract class GeographyClass{
void goGeography();
}
abstract class MathClass{
void goMath();
}
abstract class PhysicsClass{
void goPhysics();
}
class ScienceStudent extends Student implements MathClass, GeographyClass {
@override
void goGeography() {
print("上地理課");
}
@override
void goMath() {
print("上數(shù)學(xué)課");
}
}
class LiberalStudent extends Student implements MathClass, PhysicsClass {
@override
void goMath() {
print("上數(shù)學(xué)課");
}
@override
void goPhysics() {
print("上物理課");
}
}
class ArtStudent extends Student implements MathClass, GeographyClass {
@override
void goGeography() {
print("上地理課");
}
@override
void goMath() {
print("上數(shù)學(xué)課");
}
}
從上述實現(xiàn)代碼中可以看出,子類中很多相同的冗余實現(xiàn)代碼。那么我們是不是想有一種方式可以解決這種冗余問題,是的,mixin就能很好的解決這類問題。
下面是使用mixin改寫后的代碼:
abstract class Student{}
mixin GeographyClass{
void goGeography() {
print("上地理課");
}
}
mixin MathClass{
void goMath() {
print("上數(shù)學(xué)課");
}
}
mixin PhysicsClass{
void goPhysics() {
print("上物理課");
}
}
class ScienceStudent extends Student with MathClass, GeographyClass {
}
class LiberalStudent extends Student with MathClass, PhysicsClass {
}
class ArtStudent extends Student with MathClass, GeographyClass {
}
改寫后的代碼可以發(fā)現(xiàn),mixin很好得解決了代碼冗余問題。它能復(fù)用類中的某個功能具體實現(xiàn),而不是像接口一樣需要類去實現(xiàn)哪些能力。因此mixin多繼承模型很好解決了單繼承模型帶來的冗余問題。
注意:對mixin關(guān)鍵字的支持是在Dart 2.1 版本引入的,早期的版本使用abstract class來代替的。
其實在java8和Kotlin中為了解決代碼重復(fù)冗余問題,使用了接口的default實現(xiàn)來解決這個問題:
interface ITest {
default void test(){}
}
三、線性化
mixin是線性化的,這句話如何理解呢?首先我們看下下面的例子:
mixin TA {
void t() {
print("TA");
}
}
mixin TB {
void t() {
print("TB");
}
}
class TC {
void t() {
print("TC");
}
}
class Mix1 with TA, TB {//TB
}
class Mix2 with TB, TA {//TA
}
我們會得到如下結(jié)果:
TB
TA
此時你可能會總結(jié)得到規(guī)律,with后面多個類中有相同的方法,會調(diào)用距離with關(guān)鍵字最遠(yuǎn)的類中的方法。下面我們得到結(jié)論:
mixin混入類中時,Dart中的Mixins通過創(chuàng)建一個新類來實現(xiàn),該類將mixin的實現(xiàn)層疊在一個超類之上以創(chuàng)建一個新類 ,它不是“在超類中”,而是在超類的“頂部。
聲明 mixin 的順序代表了繼承鏈的繼承順序,聲明在后面的 mixin,一般會最先執(zhí)行。
接下來我們再看下面的例子:
class T {
void fun() {
print("A");
}
}
mixin TA on T{
void fun() {
super.fun();
cover();
print("TA");
}
void cover() {
print("cover TA");
}
}
mixin TB on T {
void fun() {
super.fun();
print("TB");
}
void cover() {
print("cover TB");
}
}
class A extends T with TA, TB {} // TB
class B extends T with TB, TA {} // TA
void main() {
A a = A();
a.fun();
}
按照我們剛才的理解,我們會得到如下結(jié)果:
A
cover TA
TA
TB
實際上,我們輸出的結(jié)果是:
A
cover TB
TA
TB
回想我們得到的結(jié)論,在mixin繼承鏈中,最后聲明的mixin會把前面聲明的相同方法覆蓋掉。這時,即使我們代碼中調(diào)用了TA的cover方法,實際上也會被TB類中的cover方法覆蓋掉。因此最終調(diào)用的還是MB中的方法。
四、總結(jié)
我們大致總結(jié)了mixin的機(jī)制和使用,mixin 是一個強(qiáng)大的概念,我們可以跨越類的層次結(jié)構(gòu)重用代碼。在我們看Flutter源碼時,經(jīng)常會看到使用這個功能,我也是在看Flutter代碼時,看到這個關(guān)鍵字然后進(jìn)行補(bǔ)腦的。
1.Mixins并不是經(jīng)典意義上獲得多重繼承的方法。
2.Mixins是一種抽象和復(fù)用一系列操作和狀態(tài)的方式,而且生成多個中間的mixin類。
3.它是線性的,因此與單繼承兼容。
4.Mixins除了繼承Object外,不可以繼承任何其他類; Mixins不可以定義構(gòu)造方法。