你知道 React 本身也有使用設計模式嗎? 其中用到最多的又會是哪一種?
以 React 架構上來說,因為是 Component Based 的函式庫,最常用到的會是 Module Pattern,而實現 Component 的實作則是 Facade Pattern。
上一篇文章介紹過了創建型的 Pattern,這篇文章會繼續開箱結構型,結構型可以幫助我們更好的去組織程式碼,避免隨著專案規模增長找到程式碼的難度也越來越高的現象,接下來會先從優化語法的四種結構型設計模式開始介紹
Module Pattern Mixins Pattern Proxy Pattern Decorator Pattern Design Patterns 依照目的分成三群:
Module Pattern Module pattern 是一種設計模式,主要用於封裝功能,可以將變數和函數進行分組並且限制在一個私有作用域,只將公共介面揭露出來,減少和全域變數、函式發生衝突。
以下是一個簡單的 Module Pattern 範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const moduleCounter = (function ( ) { let count = 0 ; return { getValue : function ( ) { return count; }, increment : function ( ) { return ++count; }, reset : function ( ) { console .log ("reset:" + count); count = 0 ; }, }; })(); moduleCounter.getValue (); moduleCounter.increment (); moduleCounter.reset ();
在這個範例中,moduleCounter 是一個封裝私有變數和函數的模組。
count 是私有變數,無法從模組外部存取 getValue 是一個公開的函式用來存取私有的變數 在 ES6 中,JavaScript 提供了一種內建的模組系統,可以通過 export 和 import 關鍵字來處理。
這種模組系統使得 JavaScript 模組化開發更加容易,提高了可讀性和可維護性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let count = 0 ;export function getValue ( ) { return count; } export function increment ( ) { return ++count; } export function reset ( ) { console .log ("reset:" + count); count = 0 ; }
1 2 3 import { getValue, increment, reset } from "./counter.js" ;console .log (getValue ());
Mixins Pattern Mixins Pattern 像是汽機車改裝零件,在本體不被修改的情況下能夠優化一些功能。
所以以 JavaScript 來說,透過 prototype 的特性可以在不修改現有程式碼的情況下,將物件添加新的功能或屬性。
Mixins Pattern 將多個物件的功能和屬性混合在一起,建立一個新的物件,這個新的物件包含了所有混合在一起的功能和屬性。
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 class Motorcycle { constructor (license, volume ) { this .license = license; this .volume = volume; } } class MotorFactory { createMotor ({ volume } ) { if (volume <= 50 ) { return new Motorcycle ("green" , volume); } if (volume < 150 ) { return new Motorcycle ("white" , volume); } } } const motorComponentMixins = { makeHornSound ( ) { console .log (`${this .license} ba ba` ); }, }; Object .assign (Motorcycle .prototype , motorComponentMixins);const motorFactory = new MotorFactory ();const motorGreenLicense = motorFactory.createMotor ({ volume : 50 });const motorWhiteLicense = motorFactory.createMotor ({ volume : 125 });console .log (motorGreenLicense);console .log (motorWhiteLicense);motorGreenLicense.makeHornSound ();
Proxy Pattern Proxy Pattern 可以監控和控制對一個物件的訪問。
可以把它想像成一個代理人,當有人要訪問這個物件時,代理人可以介入並決定是否允許進行操作以及該如何處理。
參考資料: https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Proxy
舉個例子,假設我們有一個物件 person,裡面有一個屬性 age,並且在 age 屬性加上一些控制
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 let validator = { set : function (obj, prop, value ) { if (prop === "age" ) { if (!Number .isInteger (value)) { throw new TypeError ("The age is not an integer" ); } if (value > 200 ) { throw new RangeError ("The age seems invalid" ); } if (value > 18 ) { console .log ("Can ride motorcycle with license" ); } } obj[prop] = value; return true ; }, }; let person = new Proxy ({}, validator);person.age = 100 ; console .log (person.age ); person.age = "young" ; person.age = 300 ;
Decorator Pattern Decorator Pattern 可以在不改變原有程式碼的情況下,動態幫物件新增額外的功能。
這種模式通常是利用繼承來實現的,但是裝飾者模式強調的是可以在執行時期加上額外的功能,而不是在編譯時期就固定下來。
可以想像成一杯咖啡(被裝飾者)可以通過添加不同的裝飾物(裝飾者)來增加不同的風味。
例如,如果想要加牛奶、加糖,這樣咖啡就同時擁有了糖和牛奶。
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 class Motorcycle { constructor ( ) { this .price = 0 ; this .description = "Basic Motorcycle" ; } getPrice ( ) { return this .price ; } getDescription ( ) { return this .description ; } } class CustomizedMotorcycle { constructor (motorcycle ) { this .motorcycle = motorcycle; } getPrice ( ) { return this .motorcycle .getPrice () + 1000 ; } getDescription ( ) { return this .motorcycle .getDescription () + " with Customized Parts" ; } } class HighPerformanceMotorcycle { constructor (motorcycle ) { this .motorcycle = motorcycle; } getPrice ( ) { return this .motorcycle .getPrice () + 2000 ; } getDescription ( ) { return this .motorcycle .getDescription () + " with High Performance Parts" ; } } let basicMotorcycle = new Motorcycle ();console .log ( basicMotorcycle.getDescription () + " costs $" + basicMotorcycle.getPrice () ); let customizedMotorcycle = new CustomizedMotorcycle (basicMotorcycle);console .log ( customizedMotorcycle.getDescription () + " costs $" + customizedMotorcycle.getPrice () ); let highPerformanceMotorcycle = new HighPerformanceMotorcycle ( customizedMotorcycle ); console .log ( highPerformanceMotorcycle.getDescription () + " costs $" + highPerformanceMotorcycle.getPrice () );
在 TypeScript 中,Decorator 是一個 function, 透過在 class/parameter/method/property 前面加上 @ 來使用,這種寫法在 Angular 中被大量使用。
參考資料: https://www.typescriptlang.org/docs/handbook/decorators.html
1 2 3 4 5 6 7 8 9 10 11 12 import { Component } from "@angular/core" ;@Component ({ selector : "hello-world" , template : ` <h2>Hello World</h2> <p>This is my first component!</p> ` ,}) export class HelloWorldComponent { }
喜歡這篇文章,請幫忙拍拍手喔 🤣