Alex Liang

在新創公司擔任軟體工程師,記錄我的學習歷程

[Design Pattern] 工廠方法模式(Factory)介紹與應用

上一篇講到簡單工廠方法,這篇文章介紹它的延申: 工廠方法

適合場景

有二個以上、功能相近但細節稍有不同的資料結構或實作,且隨著業務增長會不斷增加實作的情況。例如本來只服務台灣的電商平台,需要擴展到海外,其寄送方式和服務商會大幅增加。

範例

一樣以電商的物流為例,如何使用工廠模式解決不斷增加的寄送方式。

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
46
47
// 宣告基本的物流類別
class Delivery {
constructor(shipAddress) {
this.shipAddress = shipAddress;
this.shippingRate = 0;
this.freeShipmentThreshold = 0;
this.traceNumber = '';
}

get shippingRate() {
return this.shippingRate;
}

get freeShipmentThreshold() {
return this.freeShipmentThreshold;
}

set traceNumber() {}

get traceNumber() {
return this.traceNumber;
}
}

// 宣告超商取貨
class ConvenienceStoreDelivery : Delivery {
constructor(shipAddress) {
this.shipAddress = shipAddress;
this.shippingRate = 100;
this.freeShipmentThreshold = 2000;
this.traceNumber = '';
}

set traceNumber() {}
}

// 宣告郵局寄送
class PostOfficeDelivery : Delivery {
constructor(shipAddress) {
this.shipAddress = shipAddress;
this.shippingRate = 80;
this.freeShipmentThreshold = 1000;
this.traceNumber = '';
}

set traceNumber() {}
}

宣告各物流服務後,接下來是建立工廠類別介面和實作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 宣告工廠類別介面
class IDeliveryFactory {
createDeliveryService(shipAddress) {
throw new Error('Implement me!');
}
}

// 宣告超商取貨工廠
class ConvenienceStoreDeliveryFactory : IDeliveryFactory {
createDeliveryService(shipAddress) {
return new ConvenienceStoreDelivery(shipAddress);
}
}

// 宣告郵局寄送工廠
class PostOfficeDeliveryFactory : IDeliveryFactory {
createDeliveryService(shipAddress) {
return new PostOfficeDelivery(shipAddress);
}
}

用戶端

1
2
3
// 如果要換其它物流方式,修改這裡即可
const factory = new ConvenienceStoreDeliveryFactory();
const convenienceStoreDelivery = factory.createDeliveryService(shipAddress);

乍看之下多了不少程式碼,但工廠方法的優點在於新增實作不會違反開放-封閉原則,不用每次都要修改switch case,將判斷拉到用戶端。

注意事項

  • 不建議一開始就使用工廠方法,也許簡單工廠方法就能應付,等到業務需求大增再改就好
  • 此方法對用戶端有額外的判斷,若太多用戶端都需要使用會造成負擔,要靠其它方法解決