這篇文章會以 JavaScript 的 Creational Pattern 開始介紹,建立型模式是處理物件建立的設計模式,實作上會根據實際情況使用合適的方式建立物件,幾種方式如下
- Class Design Pattern
- Constructor Pattern
- Singleton Pattern
- Factory Pattern
- Abstract Factory Pattern
Design Patterns 依照目的分成三群:
- Creational Patterns 創建型
- Structural Patterns 結構型,結構型設計模式根據實際情況會分成三種:
- Behavioural Patterns 行為型
Class Design Pattern
這個其實算常見,基於 prototype 的基礎在 ECMAScript 6 被實作,讓我們可以用模擬物件導向方式去建立物件。
Class 是一個定義檔,去定義出 “未來產生的” 物件應該要長什麼樣子以及該怎麼被使用。
在使用 Class 時必須使用 new 這個關鍵字用 Class 去產生出新的 Instance 作為物件來被使用。
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 已經定義過的變數和沿用該變數該有的行為。
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 原始碼裡。
/**
* 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 = {};
// 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 的物件內容。
不過較常使用在後端,讓用於資料庫連線的物件只會有一個。
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. 定義基礎 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 版本的
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. 定義基礎 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’}
喜歡這篇文章,請幫忙拍拍手喔 🤣