Dart如何實現工廠法
前情提要
所謂的工廠法,在前端設計模式上面算是行之有年,目的是為了達到元件低耦合,做出組件化好管理的Code,那讓我們來看一下Dart是怎麼做的。
模擬http請求情況
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void getAnimalList() {
const jsonArray = '''
[{"name": "happy", "type": "dog", "age": 18, "no": 4},
{"name": "chiBe", "type": "cat", "age": 18, "hobby": "sleeping"},
{"name": "carry", "type": "cow", "age": 5, "bobby": "eat", "color": "white"}]
''';
/// 這邊模擬非結構化的資料(NoSQL)
final List<dynamic> dynamicList = jsonDecode(jsonArray);
/// 當我們發出請求時,後端通常會Response Map類型
List animalList = dynamicList.map<AnimalModel>((v) => AnimalModel.fromJson(v)).toList();
/// 在Dart由於管理和可視性,我們都我宣告modal做管理,這邊透過Map的方式將資料結構化
print(animalList);
/// output: [Instance of 'AnimalModel', Instance of 'AnimalModel', Instance of 'AnimalModel']
}
建立一個動物的Modal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class AnimalModel {
final String name, type;
final int age;
AnimalModel({
required this.name,
required this.type,
required this.age,
});
static AnimalModel fromJson(Map<String, dynamic> jsonData) {
return AnimalModel(
name: jsonData['name'],
type: jsonData['type'],
age: jsonData['age'],
);
}
}
關鍵字 factory
Dart有提供factory語法糖,我們將fromJson()改成factory的方式,會發現他們都會回傳一個Instance,但factory不需要額外寫額外的類別,而這也是他們之間的差異,一般函數是可以自訂要返回的類別(或者不要),而factory函數則是綁定返回類別。
1
2
3
4
5
6
7
factory AnimalModel.fromJson(Map<String, dynamic> jsonData) {
return AnimalModel(
name: jsonData['name'],
type: jsonData['type'],
age: jsonData['age'],
);
}
Abstract Class
抽象類別的用處在於,他會去限制你的Class必須要有哪些功能(Function),有寫過TypeScript的人應該會聯想到interface(介面),其實他們是同樣的意思,但在Dart當中interface並沒有被定義為關鍵字,每個Class都有Implicit interfaces的特性,在建構時,跟一般的Class一樣,都可以設Constructor(建構子)、Field(欄位)、Method(方法),只是差別在他一定要有抽象方法(只有方法名沒有內容),這邊我直接舉個例子。
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 AnimalAbstract {
void eat(); // 每個動物都會吃飯,我把它定義為抽象方法
void sleep() {
print('愛睏了,去睡覺');
}
}
class DogClass extends AnimalAbstract {
// 可以定義更多變數或是方法
void run(){
print('跑起來');
}
// 一定要將父類別的抽象方法都實作,不然會報錯,透過override進行覆寫
@override
void eat() {
print('我好乖,今天吃西沙');
}
}
main() {
DogClass happy = DogClass();
happy.run(); // print 跑起來
happy.eat(); // print 我好乖,今天吃西沙
happy.sleep(); // print 愛睏了,去睡覺
}
Implements
這邊示範一般的Class如何變成interface,讓子類別只能按照指定方式去建構。
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
class Animal {
final String type;
Animal(this.type);
// 每個動物都會吃飯
void eat(String food) {
print('$type,只能吃$food');
}
}
// 為了讓子類別有所限制,我們會加上implements。
class DogClass implements Animal {
// 當宣告Animal為介面,就要把它本身的方法或屬性都覆寫一遍
final String name;
@override
get type => '小狗';
DogClass(this.name);
void eat(String food) {
print('$name好乖,今晚吃$food');
}
// 可以定義更多變數或是方法
void run(){
print('跑起來');
}
}
main() {
Animal('企鵝').eat('魚'); // print 企鵝,只能吃魚
DogClass('happy').eat('西沙'); // print happy好乖,今晚吃西沙
}
Mixin
這邊先舉個例子,有些動物會跑步,有些動物會游泳,有時候我們為了管理會把這些屬性獨立出來,那我們來看一下這個是怎麼運作的。
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
class Animal {
final String type;
Animal(this.type);
void eat(String food) {
print('$type,只能吃$food');
}
}
mixin CanRun {
void run() {
print('跑起來');
}
}
mixin CanSwim {
void swimming() {
print('游起來');
}
}
class FishClass extends Animal with CanSwim {
String name;
FishClass(this.name) : super('fish');
}
main() {
FishClass('尼莫').swimming(); //print 游起來
}
結語
從上面的例子,你可以發現到,當動物種類變多,我們可以運用單一類別來實現不同的功能,而良好的拆分也可以讓偶和度降低,而Abstract的概念也是在告訴我們,會做這些限制並不是平白無故,當Code越來越複雜,之後要再回去找eat到底是誰在吃,會變得困難起來,而在協作上面,他也會增加程式碼的可讀性,以上內容算是一小部分,其他細節可以到官方文件Dart Language閱讀。
This post is licensed under CC BY 4.0 by the author.