JavaScript Creational Pattern 開箱 JavaScript 設計模式 (1)

me
林彥成
2023-02-25 | 4 min.
文章目錄
  1. 1. Class Design Pattern
    1. 1.1. Constructor Pattern
  2. 2. Singleton Pattern
  3. 3. Factory Pattern
  4. 4. Abstract Factory Pattern

這篇文章會以 JavaScript 的 Creational Pattern 開始介紹,建立型模式是處理物件建立的設計模式,實作上會根據實際情況使用合適的方式建立物件,幾種方式如下

  • Class Design Pattern
  • Constructor Pattern
  • Singleton Pattern
  • Factory Pattern
  • Abstract Factory Pattern

Design Patterns 依照目的分成三群:

Class Design Pattern

這個其實算常見,基於 prototype 的基礎在 ECMAScript 6 被實作,讓我們可以用模擬物件導向方式去建立物件。

Class 是一個定義檔,去定義出 “未來產生的” 物件應該要長什麼樣子以及該怎麼被使用。

在使用 Class 時必須使用 new 這個關鍵字用 Class 去產生出新的 Instance 作為物件來被使用。

1
2
3
4
5
6
7
8
9
10
11
class Motorcycle {
constructor(license, volume) {
this.license = license;
this.volume = volume;
}
}

const motor125 = new Motorcycle("white", 125);
const motor50 = new Motorcycle("white", 50);
console.log(motor125);
console.log(motor50);

執行程式碼的結果

Motorcycle {license: ‘white’, volume: 125}
Motorcycle {license: ‘white’, volume: 50}

Constructor Pattern

以 Class Design Pattern 延伸出的就是 Constructor Pattern,讓我們可以沿用父類別 constructor 已經定義過的變數和沿用該變數該有的行為。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Motorcycle {
constructor(license, volume) {
this.license = license;
this.volume = volume;
}
}

class SymMotor extends Motorcycle {
constructor(license, volume) {
super(license, volume);
this.brand = "sym";
}
}

const symMotorGreenLicense = new SymMotor("green", 50);
const symMotorWhiteLicense = new SymMotor("white", 125);
console.log(symMotorGreenLicense);
console.log(symMotorWhiteLicense);

執行程式碼的結果

SymMotor {license: ‘green’, volume: 50, brand: ‘sym’}
SymMotor {license: ‘white’, volume: 125, brand: ‘sym’}

React 初期的 class component 就是運用這個來實作出元件,super 會參照父類別的 constructor,並且要在呼叫過後才可以叫用 this,所以我們可以去使用已經在父類別裡面已經被定義過的 props,以 Motorcycle 當例子就是 license 和 volume 都已經被定義過且被使用。

如果在寫 Super 前就先使用 this 去定義 function 若 function 有使用到 props 的內容就會造成未定義的問題,所以在 constructor 中使用 this,JavaScript 強制必須先呼叫 super。

那為什麼要寫一個 super(props); 當參數,原因就在 React 原始碼裡。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Base class helpers for the updating state of a component.
*/
function Component(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Class-based
class Input extends React.Component {
constructor() {
this.handleInputInit(); // 不被允許
super(props);
this.state = { input: "" };

this.handleInput = this.handleInput.bind(this);
}

handleInputInit() {
// 以 Motorcycle 來說這裡就會看不到 license 和 volume
}

handleInput(e) {
this.setState({ input: e.target.value });
}

render() {
<input onChange={handleInput} value={this.state.input} />;
}
}

Singleton Pattern

單體模式算是小編接觸的第一個設計模式,目標很清楚就是要持久化一個物件,當未來每次要產生物件時其實都是使用同一個 Instance 的物件內容。

不過較常使用在後端,讓用於資料庫連線的物件只會有一個。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let instance = null;

class Motorcycle {
constructor(license, volume) {
if (instance) return instance;
if (!instance) {
this.license = license;
this.volume = volume;
instance = this;
}
}
}

const motor125 = new Motorcycle("white", 125);
const motor50 = new Motorcycle("white", 50);
console.log(motor125);
console.log(motor50);

執行程式的結果可以發現都是相同物件的內容,新的 new 其實並沒有產生新的物件出來。

Motorcycle {license: ‘white’, volume: 125}
Motorcycle {license: ‘white’, volume: 125}

Factory Pattern

工廠模式顧名思義就是去定義出類似工廠概念的 class,工廠是按照規格把東西做出來。

所以 Factory Pattern 就會是專注定義規格,並且提供 function 讓使用者可以將物件製造出來。

底下的例子我們就來定義機車的規格

  • 50cc 綠牌車
  • 125cc 白牌車
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
// 1. 定義基礎 Class 一台機車包含牌照及 CC 數
class Motorcycle {
constructor(license, volume) {
this.license = license;
this.volume = volume;
}
}

// 2. 定義 Sym 工廠並且定義 createMotor function
class MotorFactory {
createMotor({ volume }) {
if (volume <= 50) {
return new Motorcycle("green", volume);
}

if (volume < 150) {
return new Motorcycle("white", volume);
}
}
}

// 建立工廠物件
const motorFactory = new MotorFactory();

// 透過工廠物件生產機車
const motorGreenLicense = motorFactory.createMotor({ volume: 50 });
const motorWhiteLicense = motorFactory.createMotor({ volume: 125 });

console.log(motorGreenLicense);
console.log(motorWhiteLicense);

執行程式碼的結果

Motorcycle {license: ‘green’, volume: 50}
Motorcycle {license: ‘white’, volume: 125}

如果要寫出一個 react 版本的

1
2
3
4
5
6
7
8
9
function factory(params) {
const condition = "some condition";

if (condition) {
return <ProductA params={params} />;
} else {
return <ProductB params={params} />;
}
}

Abstract Factory Pattern

Abstract Factory 只是 Factory Pattern 延伸的概念,以現實狀況來說就是一個代工廠。

小編現在剛好就在代工廠上班,代工廠做的事情是在某個規格的基礎上,依照各品牌去做相關的客製化做代工,舉例來說常見的鞋子、筆電等等,其實都是由代工廠為各個品牌進行製造的。

以廣達當作 Abstract Factory 來看,實際的代工出來的就會有 HP、Toshiba、Sony、Lenovo 等等品牌筆電。

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// 1. 定義基礎 Class 一台機車包含牌照及 CC 數
class Motorcycle {
constructor(license, volume) {
this.license = license;
this.volume = volume;
}
}

// 2. 定義各品牌
class SymMotor extends Motorcycle {
constructor(license, volume) {
super(license, volume);
this.brand = "sym";
}
}

class KymcoMotor extends Motorcycle {
constructor(license, volume) {
super(license, volume);
this.brand = "Kymco";
}
}

// 3. 定義品牌工廠
class SymMotorFactory {
createMotor(volume) {
if (volume <= 50) {
return new SymMotor("green", volume);
}

if (volume < 150) {
return new SymMotor("white", volume);
}
}
}

class KymcoMotorFactory {
createMotor(volume) {
if (volume <= 50) {
return new KymcoMotor("green", volume);
}

if (volume < 150) {
return new KymcoMotor("white", volume);
}
}
}

// 建立工廠物件
const symMotorFactory = new SymMotorFactory();
const kymcoMotorFactory = new KymcoMotorFactory();

// 建立抽象工廠 (代工廠)
const motorFactory = ({ brand, volume }) => {
if (brand === "sym") {
return symMotorFactory.createMotor(volume);
}
if (brand === "kymco") {
return kymcoMotorFactory.createMotor(volume);
}
};

// 讓代工廠進行製造
const symMotorGreenLicense = motorFactory({ brand: "sym", volume: 50 });
const kymcoMotorGreenLicense = motorFactory({ brand: "kymco", volume: 50 });
const symMotorWhiteLicense = motorFactory({ brand: "sym", volume: 125 });

console.log(symMotorGreenLicense);
console.log(symMotorWhiteLicense);
console.log(kymcoMotorGreenLicense);

程式執行結果

SymMotor {license: ‘green’, volume: 50, brand: ‘sym’}
SymMotor {license: ‘white’, volume: 125, brand: ‘sym’}
KymcoMotor {license: ‘green’, volume: 50, brand: ‘Kymco’}


喜歡這篇文章,請幫忙拍拍手喔 🤣


share