本篇純為個人見解,可能還有不少沒理解清楚的地方,歡迎大家提出指正哦!

-

這篇本來要跟上一篇一起寫

直到我發現Dart中Mixin的觀念其實多到可以自成一篇QQ

觀念

先來講講一些觀念:

  • Mixin是物件導向語言中的一種類,不過每種語言中的Mixin類特性不盡相同

  • Mixin類自帶方法的實現,無須依賴子類(當然子類要override也不是不行啦)

  • Mixin類責任單一,要寫多種功能請寫多個Mixin類

  • Mixin並非多重繼承(Multiple Inheritance),也不是接口(Interface),不過功能上蠻像的

  • 在Dart中,可實例化的類與無法實例化的抽象類,都能拿來當作Mixin類

  • 在Dart中,可以使用With這個關鍵字Mixin多個Mixin類,但不破壞單繼承的特性

看完上述這幾點,是不是心裡只有 “馬的,工三小?

舉個例子

接下來的話就來舉個例子好了

現在有兩種技能: Teach & Drive

而且有兩種職業: Teacher & Driver

現在假設這兩個職業都各持有Teach & Drive這兩個技能

Java v.s. Dart

Java版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
abstract class Human {
// Some properties and methods...
}

interface Teach {
void canTeach();
}

interface Drive {
void canDrive();
}

class Teacher extends Human implements Teach, Drive {
@Override
public void canTeach() {
System.out.println("Yes, a human can teach.");
}

@Override
public void canDrive() {
System.out.println("Yes, a human can drive.");
}
}

class Driver extends Human implements Teach, Drive {
@Override
public void canTeach() {
System.out.println("Yes, a human can teach.");
}

@Override
public void canDrive() {
System.out.println("Yes, a human can drive.");
}
}

public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.canTeach();
teacher.canDrive();

Driver driver = new Driver();
driver.canTeach();
driver.canDrive();
}

Dart版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
abstract class Human { 
// Some properties and methods...
}

class Teach {
void canTeach() {
print("Yes, a human can teach.");
}
}

class Drive {
void canDrive() {
print("Yes, a human can drive.");
}
}

class Teacher extends Human with Teach, Drive {}

class Driver extends Human with Teach, Drive {}

void main() {
Teacher teacher = new Teacher();
teacher.canTeach();
teacher.canDrive();

Driver driver = new Driver();
driver.canTeach();
driver.canDrive();
}

輸出均為:

1
2
3
4
Yes, a human can teach.
Yes, a human can drive.
Yes, a human can teach.
Yes, a human can drive.

這裡可以看到在Java版本的兩個職業中,實作方法(技能)時出現重複的程式碼

而這裡Dart版本中的Mixin類自帶方法實現,從而解決了這個問題

(不過Java 8以上的default method也有同樣的效果)

使Mixin類無法實例化

這裡可以使用關鍵字 mixin

1
2
3
4
5
6
mixin Teach {
// 這裡就是一個標準的Mixin類,無法實例化
void canTeach() {
print("Yes, a human can teach.");
}
}

或者是改為abstract class

然後給定該類一個private constructor(dash代表私有),並設回傳值為 null

1
2
3
4
5
6
7
8
abstract class Drive {
// 這樣一來Drive既不能擴充也不能實例化了
factory Drive._() => null;

void canDrive() {
print("Yes, a human can drive.");
}
}

mixin on

現在我們修改一下設定

假設說只有會教書的人能使用Teach這個技能呢?

這時我們可以用到 mixin on 這個關鍵字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
abstract class Human {
// Some properties and methods...
}

class WhoCanTeach extends Human {
// 會教書的人
}

mixin Teach on WhoCanTeach {
// 這裡就指定了只有extends WhoCanTeach或implements WhoCanTeach的類才能with Teach
void canTeach() {
print("Yes, a teacher can teach.");
}
}

abstract class Drive {
factory Drive._() => null;

void canDrive() {
print("Yes, a human can drive.");
}
}

class Teacher extends WhoCanTeach with Teach, Drive {
// Correct.
}

class Driver extends Human with Teach, Drive {
// Error: Driver does not implement WhoCanTeach.
}

class Driver extends Human with Drive {
// Correct.
}

void main() {
Teacher teacher = new Teacher();
teacher.canTeach();
teacher.canDrive();

Driver driver = new Driver();
driver.canDrive();
}

輸出:

1
2
3
Yes, a teacher can teach.
Yes, a human can drive.
Yes, a human can drive.

線性化

看一下這個例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Human {
String getStatus() => "Full of energy!";
}

class Student {
String getStatus() => "I'm tired!";
}

class Engineer {
String getStatus() => "I'm dying...";
}

class Me extends Human with Student, Engineer {}

void main() {
Me me = new Me();
print(me.getStatus());
}

好的,這裡究竟會印出什麼東西來呢?

答案是 “I’m dying…” QQ

先講結論: 越後面的Mixin類優先級別越高,等於是倒過來看啦~

拆解

上面的東西相當於這樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Human {
String getStatus() => "Full of energy!";
}

class Student {
String getStatus() => "I'm tired!";
}

class Engineer {
String getStatus() => "I'm dying...";
}

class HumanWhoIsStudent extends Human with Student {}

class HumanWhoIsStudentAndEngineer extends HumanWhoIsStudent with Engineer {}

class Me extends HumanWhoIsStudentAndEngineer {}

void main() {
Me me = new Me();
print(me.getStatus());
}

簡單來說,Mixin像是在實現一條線性的繼承鏈

實例化後的類型

一樣是上面的例子

1
2
3
4
5
6
7
void main() {
Me me = new Me();
print(me is Me);
print(me is Engineer);
print(me is Student);
print(me is Human);
}

輸出:

1
2
3
4
true
true
true
true