Chém gió về JavaScript Design Pattern - Part 3

Image result for Javascript patterns

Sau phần 1 và 2, bài này tiếp tục giới thiệu một số JS design patterns thông dụng, rất có ích trong việc lập trình JS

Bridge Pattern

Nếu bạn muốn lập trình JS theo hướng sự kiện, event-driven programming, hoặc xây dựng JavaScript API, pattern này khác hữu dụng, khiến code của bạn gọn nhẹ, dễ bảo trì
Dưới đây là 1 số ví dụ về Bridge Pattern
Event Listeners
Khi lập trình xử lý sự kiện, bạn quen thuộc với các hàm callback. Ví dụ, như user click vào 1 nút thì 1 hàm sẽ được gọi
addEvent(element, 'click', getBeerById);

function getBeerById(e) {
   var id = this.id;
   asyncRequest('GET', 'beer.uri?id=' + id, function (resp) {
       // Callback response.
       console.log('Requested Beer: ' + resp.responseText);
   });
}
Với cách code như trên, event object sẽ được pass vào method getBeerById, object ở đây có thể là 1 nút, 1 text box trên web page mà user click vào. Sau đó method này sẽ lấy thuộc tính id của object để xử lý tiếp. Tuy nhiên nếu như object không có thuộc tính id sẽ gây lỗi, cách truyền tham số trên không optimize, ngoài ra bạn sẽ gặp khó khăn trong việc lấy kết quả trả về khi gọi method trên trong chương trình của bạn.
Dưới đây là cách cải tiến, truyền thẳng id vào method, kết quả trả về qua hàm callback
function getBeerById(id, callback) {
   // Make request for beer by ID, then return the beer data.
   asyncRequest('GET', 'beer.uri?id=' + id, function (resp) {
       // callback response
       callback(resp.responseText);
   });
}

Cách code trên thì bạn có thể gọi API getBeerById ở bất kì đâu như ở dưới
getBeerById(this.id, function (beer) {
   console.log('Requested Beer: ' + beer);
});

Cách code trên được mình sử dụng khá nhiều và thấy rất hiệu quả, các bạn có thể tham khảo

Composite Pattern

Trước hết cũng cần phải hiểu composite là cái gì. Đại loại là cấu trúc dạng cây, ông bà , bố mẹ, đến con cháu, nhánh cuối cùng không có nhánh con thì gọi là Leaf, còn lại là Composite hết
Ý tưởng của composite là group các objects lại theo dạng cha, con như trên. Điều này cần thiết khi bạn phải xử lý 1 số lượng lớn các objects. Pattern này được thiết kế để tổ chức object và pass operation từ level cao đến level thấp hơn. Sử dụng pattern này cho phép bạn tạo object loosely coupled hơn
Ví dụ về pattern này, tưởng tượng bạn cần tạo 1 form nhập liệu, trong đó các input được validate và lưu trữ. Tuy nhiên tùy vào user, form này có thể thay đổi chẳng hạn user thuộc loại doanh nghiệp thì sẽ không hiển thị text box website chẳng hạn. Tức là bạn cần phải tạo ra một dynamic form
Rõ ràng, là không tạo form theo kiểu hardcode được, và composite pattern phát huy tác dụng của nó trong trường hợp này
Ta có thể tạo composite object như sau, top level chính là cái form của bạn
Bạn sẽ tạo ra 1 form cho phép add thêm hoặc remove các field mà không cần phải viết lại code
Implement code theo class diagram như sau
Ví dụ class CompositeForm
var CompositeForm = function (id, method, action) { // implements Composite, FormItem
   this.formComponents = [];
   this.element = document.createElement('form');
   this.element.id = id;
   this.element.method = method || 'POST';
   this.element.action = action || '#';
};
CompositeForm.prototype.add = function (child) {
   Interface.ensureImplements(child, Composite, FormItem);
   this.formComponents.push(child);
   this.element.appendChild(child.getElement());
};

CompositeForm.prototype.getChild = function (i) {
   return this.formComponents[i];
};
CompositeForm.prototype.save = function () {
   for (var i = 0, len = this.formComponents.length; i < len; i++) {
       this.formComponents[i].save();
   }
};
CompositeForm.prototype.getElement = function () {
   return this.element;
};


Các field của form được lưu trong 1 array formComponents nên dễ dàng thêm bớt field
Field class (leaf node) không có thuộc tính add, remove, getChild ví nó là Leaf node trong cấu trúc cây composite
var Field = function (id) { // implements Composite, FormItem
   this.id = id;
   this.element;
};
Field.prototype.add = function () {};
Field.prototype.remove = function () {};
Field.prototype.getChild = function () {};
Field.prototype.save = function () {
   setCookie(this.id, this.getValue);
};
Field.prototype.getElement = function () {
   return this.element;
};

InputField, TextAreaField, SelectField là 3 class đại diên cho 3 GUI component là Input Text. TextArea, Dropdownbox, bạn có thể add các html element vào
Ví dụ InputField

var InputField = function (id, label) { // implements Composite, FormItem
   Field.call(this, id);
   this.input = document.createElement('input');
   this.input.id = id;
   this.label = document.createElement('label');
   var labelTextNode = document.createTextNode(label);
   this.label.appendChild(labelTextNode);
   this.element = document.createElement('div');
   this.element.className = 'input-field';
   this.element.appendChild(this.label);
   this.element.appendChild(this.input);
};
extend(InputField, Field); // Inherit from Field.
InputField.prototype.getValue = function () {
   return this.input.value;
};

Và cuối cùng bạn có thể tạo dynamic form như sau
var contactForm = new CompositeForm('contact-form', 'POST', 'contact.php');
contactForm.add(new InputField('first-name', 'First Name'));
contactForm.add(new InputField('last-name', 'Last Name'));
contactForm.add(new InputField('address', 'Address'));
contactForm.add(new InputField('city', 'City'));
contactForm.add(new SelectField('state', 'State', stateArray));
// var stateArray =[{'al', 'Alabama'}, ...];
contactForm.add(new InputField('zip', 'Zip'));
contactForm.add(new TextareaField('comments', 'Comments'));
addEvent(window, 'unload', contactForm.save);

Trên đây 2 pattern khá hữu ích trong JS, bài tiếp sẽ giới thiệu nhiều pattern khác độc đáo hơn
Powered by Blogger.