Alex Liang

[Design Pattern] 利用dependency injection降低耦合

前一篇文章介紹如何使用工廠方法。本篇介紹另一種技巧能切開商業邏輯和資料存取的程式碼。

以之前電商平台的物流系統為例,假如此專案是外包的需求,A客戶指定用MySQL;而B客戶習慣PostgreSQL。在實作部分一開始為:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const db = require('./db');

// 宣告超商取貨
class ConvenienceStoreDelivery : Delivery {
constructor(shipAddress) {
// 略
}

get shippingRate() {
return (async() =>
db.query('SELECT rate FROM tbl_shipment_info WHERE type = convenience_store');
)();
}

get freeShipmentThreshold() {
return (async() =>
db.query('SELECT free_shipment_threshold FROM tbl_shipment_info WHERE type = convenience_store');
)();
}
}

在上例中,我們依賴 db 這個檔案建立連線和設定。而這也帶來缺點: 寫測試時必須依賴實體的 database、不同的實作要使用另一個連線時要給額外的參數。

此時可以應用 dependency injection。在 Node.js 裡有三種方式可以達成

  1. constructor
  2. setter
  3. bind

本文使用 constructor 當範例

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
48
49
50
51
52
class ConvenienceStoreDelivery : Delivery {
constructor(shipAddress, db) {
this.db = db;
this.shipAddress = shipAddress;
}

get shippingRate() {
return (async() =>
this.db.query('SELECT rate FROM tbl_shipment_info WHERE type = convenience_store');
)();
}

get freeShipmentThreshold() {
return (async() =>
this.db.query('SELECT free_shipment_threshold FROM tbl_shipment_info WHERE type = convenience_store');
)();
}
}

// database.js
class Database {
constructor(config) {
this._config = config;
}

connect() { // connect db }

query() { // 略 }
}

// config.js
const config = {
MYSQL_CONFIG: {
server: process.env.DB_SERVER,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PW
},
POSTGRES_CONFIG: {
// 略
}
}

// 用戶端
const config = require('./config');
const Database = require('./database');

const db = new Database(config.MYSQL_CONFIG);
db.connect();

const factory = new ConvenienceStoreDeliveryFactory();
const convenienceStoreDelivery = factory.createDeliveryService(shipAddress, db);

這樣就能把 database 和實作隔開,如果專案要換其它的 db,只要改設定值 (或用環境變數) 即可。
應用此技巧能把二個不必要的耦合分開來,讓之後的修改更有彈性,nest.js也採用這種方式設計框架。

ref: Dependency Injection in nodejs