🎨Front-End Design Pattern
Ở phần này chúng ta sẽ cùng tìm hiểu các design pattern phổ biến với JS.
Last updated
Ở phần này chúng ta sẽ cùng tìm hiểu các design pattern phổ biến với JS.
Last updated
Module
là một đoạn code độc lập mà chúng ta có thể chỉnh sửa mà không làm ảnh hưởng đến các phần khác của code. Module
cũng cho phép chúng ta tránh lạm dụng namespace bằng cách cho phép tạo các scope riêng biệt cho các biến. Chúng ta cũng có thể tái sử dụng module
trong các project khác vì bản chất của module
là tách biệt, không phụ thuộc vào các phần code khác.
Trong các ứng dụng JavaScript hiện nay, Module
là một phần không thể thiếu. Module
giúp code sạch hơn, tách biệt và có tổ chức hơn.
Không giống như những ngôn ngữ lập trình khác, JavaScript không có các phương thức để định nghĩa biến public, private (hay còn gọi là access modifier). Vậy nên Module Pattern
được sử dụng để giả lập tính chất đóng gói của hướng đối tượng.
Pattern này thường sử dụng IIFE (immediately - invoked function expression), closures và function scope để giả lập concept này. Ví dụ:
Bằng cách sử dụng IIFE, đoạn code trên được thực thi ngay lập tức, và trả về một object để gán vào biến myModule
. Nhờ có closure, object trả về vẫn có thể truy cập vào các hàm và biến được định nghĩa bên trong IIFE ngay cả khi IIFE đã thực thi xong.
Vậy nên các biến và hàm định nghĩa trong IIFE được giấu đi khỏi outer scope và từ đó trở nên private với biến myModule
Sau khi thực thi xong, biến myModule
sẽ có dạng như sau:
Khi đó chúng ta có thể gọi publicMethod(), phương thức này sẽ gọi tới privateMethod()
Revealing Module Pattern
có thể coi là phiên bản cải tiến của Module Pattern. Vấn đề của module pattern
là chúng ta phải tạo các public function chỉ để gọi tới các private function và variable.
Trong pattern này, chúng ta map các thuộc tính của object trả về với các private function mà chúng ta muốn public. Đó cũng chính là lí do nó được gọi là Revealing Module Pattern
. Ví dụ:
Pattern này làm cho code dễ đọc hiểu hơn, sau khi thực thi myRevealingModule
sẽ có dạng như sau:
Chúng ta có thể gọi myRevealingModule.setName('Mark')
, tham chiếu tới hàm nội tại publicSetName
và myRevealingModule.getName()
, tham chiếu tới hàm nội tại publicGetName
. Ví dụ:
Trước sự xuất hiện của ES6, JavaScript không hề có feature tạo module, vậy nên các lập trình viên phải dựa vào các thư viện thứ ba hoặc Module Pattern
để implement module. Nhưng với ES6, mọi chuyện đã khác.
ES6 Module
được lưu các file riêng biệt. Chỉ duy nhất một module trong một file. Mọi thứ trong một module mặc định là private. Function, variable, và class được expose ra ngoài bằng cách sử dụng export
keyword. Và code trong một module luôn ở strict mode
.
Có 2 cách để export một khai báo function và variable:
Sử dụng keyword export
trước khai báo function và variable. Ví dụ:
Sử dụng export
keyword ở cuối file kết hợp với tên function và variable muốn export. Ví dụ:
Tương tự như export một module, có hai cách để import một module bằng cách sử dụng import
keyword. Ví dụ:
Import nhiều item một lần
Import cả một module
Chức năng này được sinh ra để tránh các conflict trong naming. Ví dụ:
Alias export
Alias import
Singleton
là một object chỉ khởi tạo duy nhất một lần, nghĩa là nó chỉ tạo một instance mới của một class nếu chưa tồn tại instance nào, còn nếu có thì nó chỉ việc trả lại instance đó. Nhờ vậy mà dù có gọi hàm khởi tạo nhiều lần thì chúng ta cũng chỉ nhận được một object duy nhất, giúp tránh lãng phí bộ nhớ.
JavaScript đã xây dựng sẵn singleton
như là một tính năng, được gọi là object literal
. Ví dụ:
Bởi vì mỗi object trong JavaScript chiếm một vùng trong bộ nhớ và khi gọi tới object user
, chúng ta nhận được một tham chiếu tới nó. Nếu thử gán biến user
vào một biến khác và thay đổi biến đó. Ví dụ:
Điều này làm thay đổi cả 2 object bởi vì JavaScript truyền tham chiếu chứ không phải truyền giá trị. Vậy nên vẫn chỉ có duy nhất một object trong bộ nhớ:
Chúng ta cũng có thể implement Singleton
bằng Module pattern
. Ví dụ:
Trong đoạn code trên, chúng ta tạo một instance mới bằng cách gọi hàm singleton.getInstance
. Nếu một instance đã tồn tại, hàm này đơn giản chỉ trả về instance đó, nếu instance chưa tồn tại, nó tạo một instance mới bằng hàm init()
Factory Pattern
là một pattern sử dụng phương thức đặc biệt để tạo các object mà không cần chỉ định rõ chính xác class hay constructor nào,
Pattern này có ích trong trường hợp chúng ta cần khởi tạo nhiều loại object phụ thuộc vào một số điều kiện nhất định. Ví dụ:
Ở đây chúng ta tạo các class Car
và Truck
(với một vài giá trị mặc định) là nguyên mẫu cho các object. Sau đó định nghĩa thêm class VehicleFactory
có nhiệm vụ khởi tạo và trả về một trong hai object ở trên dựa vào thuộc tính vehicleType
trong options
Tạo một object factory
từ class VehicleFactory
. Sau đó chúng ta tạo một object của Car
hoặc Truck
bằng hàm factory.createVehicle
và truyền object options
là tham số bao gồm thuộc tính vehicleType
là car
hoặc truck
.
Decorator pattern được sử dụng để mở rộng chức năng của một object mà không làm thay đổi class hiện tại hay hàm tạo. Pattern này có thể được sử dụng để thêm feature mới vào object.
Ví dụ đơn giản:
Một ví dụ thực tế khác
Giả sử giá của xe phụ thuộc vào số tính năng nó có. Nếu không sử dụng decorator pattern, chúng ta sẽ phải tạo nhiều class khác nhau cho tượng trưng cho mỗi loại xe, mỗi class lại định nghĩa một cost method để tính giá trị.
Nhưng với decorator pattern, chúng ta chỉ cần tạo một base class Car
và tính toán giá dựa vào decorator function. Ví dụ:
Class Car
để khởi tạo các object, sau đó được truyền vào decorator function để override hàm cost
tính giá mới và thêm các thuộc tính xác định tính năng được thêm vào cho car
instance. Khi sử dụng: