Design pattern trong Javascript có những gì thú vị? (Phần 2)

Qua phần 1, mình đã giới thiệu cho các bạn về các mẫu design pattern cơ bản mà bạn sẽ hay sử dụng đến chúng. Sang đến phần 2 này mình sẽ tiếp tục gửi đến các bạn thêm 2 design patterns nâng cao, tuy mình sẽ ít khi sử dụng, nhưng biết đến nó và tìm hiểu qua vẫn hơn đúng không? Biết đâu sẽ có lúc dùng.

Đầu tiên mình xin chia sẻ là. Toàn bộ những kiến thức mình viết trong blog này hoàn toàn là mình đọc được từ cuốn Javascript pattern (cụ thể là chương 7 trong cuốn sách này) chứ mình không có tự nghĩ ra rồi chia sẻ (mình đâu pro đến vậy). Vì vậy nếu mà mọi người muốn tìm hiểu kỹ hơn về design pattern hoặc là cuốn sách đó thì hoàn toàn có thể truy cập vào đường link mình để dưới bài blog để có thể đọc thêm nha (khuyến khích). Ok bắt đầu thôi.

Interator

Trong mẫu iterator, bạn có một đối tượng chứa một số loại dữ liệu tổng hợp. Dữ liệu này có thể được lưu trữ nội bộ trong một cấu trúc phức tạp và bạn muốn cung cấp quyền truy cập dễ dàng vào từng thành phần dựa trên cấu trúc đó. Người dùng không cần phải biết cách bạn cấu trúc dữ liệu của mình. Tất cả những gì họ muốn là lấy được với các yếu tố riêng lẻ.

Trong mẫu iterator, đối tượng của bạn cần cung cấp phương thức next(). Việc gọi next() theo thứ tự phải trả về phần tử liên tiếp tiếp theo, trong đó nó sẽ tùy thuộc vào bạn để quyết định ý nghĩa của tiếp theo là gì trong cấu trúc dữ liệu cụ thể của bạn.

Giả sử rằng đối tượng của bạn được gọi là agg, bạn có thể truy cập từng phần tử dữ liệu bằng cách gọi next () trong một vòng lặp như sau:

var element;
while (element = agg.next()) {
    // do something with the element ...
    console.log(element);
}

Trong mẫu pattern, đối tượng tổng hợp cũng thường cung cấp phương thức xác định xem họ đã đạt đến cuối dữ liệu chưa đó là hasNext()

while (agg.hasNext()) {
    // do something with the next element...
    console.log(agg.next());
}

Bây giờ chúng ta có các trường hợp sử dụng, hãy xem cách triển khai một đối tượng tổng hợp như vậy. Khi thực hiện mẫu interator, việc quan trọng là lưu trữ dữ liệu và con trỏ (chỉ mục) đến phần tử có sẵn tiếp theo:

var agg = (function () {
    var index = 0,
        data = [1, 2, 3, 4, 5],
        length = data.length;
    return {
        next: function () {
            var element;
            if (!this.hasNext()) {
                return null;
            }
            element = data[index];
            index = index + 2;
            return element;
        },
        hasNext: function () {
            return index < length;
        }
    };
}());

Để cung cấp bổ sung hỗ trợ cho truy cập data bạn cũng có thể thêm method dạng như:

  • rewind() : Reset lại con trỏ về vị trí ban đầu.
  • current(): Trả lại giá trị hiện tại của con trỏ đang đặt đến.
var agg = (function () {
    // [snip...]
    return {
        // [snip...]
        rewind: function () {
            index = 0;
        },
        current: function () {
            return data[index];
        }
    };
}());

Strategy

Mẫu strategy cho phép bạn chọn các thuật toán trong khi runtime. Các code của bạn có thể hoạt động với cùng một interface nhưng được lựa chọn từ một số thuật toán có sẵn để xử lý tác vụ cụ thể của chúng. Một ví dụ về việc sử dụng mẫu strategy sẽ giải quyết vấn đề xác thực form. Bạn có thể tạo một đối tượng validator bằng phương thức validate(). Phương thức này sẽ xác thực với from và trả về cùng kết quả hoặc trả về lỗi không thể validate. Nhưng tùy thuộc vào loại form cụ thể và dữ liệu sẽ được xác thực, validator của bạn có thể chọn nhiều loại khác nhau để kiểm tra và lựa chọn thuật toán thích hợp nhất để thực hiện Giả định rằng bạn có một đoạn dữ liệu và bạn muốn xác minh xem nó có hợp lệ hay không :

var data = {
     first_name: "Super",
     last_name: "Man",
     age: "unknown",
     username: "o_O"
};

Để validator biết strategy tốt nhất để sử dụng trong ví dụ cụ thể này là gì, bạn cần định cấu hình trình xác thực trước và đặt quy tắc về những gì bạn cho là hợp lệ và có thể chấp nhận.

validator.config = {
    first_name: 'isNonEmpty',
    age: 'isNumber',
    username: 'isAlphaNum'
};

Bây giờ, đối tượng validator được cấu hình để xử lý dữ liệu của bạn, gọi phương thức validate() và in bất kỳ lỗi xác thực lên console:

validator.validate(data);
if (validator.hasErrors()) {
    console.log(validator.messages.join("\n"));
}

Bây giờ hãy xem đoạn code thực thi validator:

// checks for non-empty values
validator.types.isNonEmpty = {
    validate: function (value) {
        return value !== "";
    },
    instructions: "the value cannot be empty"
};
// checks if a value is a number
validator.types.isNumber = {
    validate: function (value) {
        return !isNaN(value);
    },
    instructions: "the value can only be a valid number, e.g. 1, 3.14 or 2010"
};
// checks if the value contains only letters and numbers
validator.types.isAlphaNum = {
    validate: function (value) {
        return !/[^a-z0-9]/i.test(value);
    },
    instructions: "the value can only contain characters and numbers, no special symbols"
};

Và cuối cùng là phần chính của đối tượng validtor :

var validator = {
    // all available checks
    types: {},
    // error messages in the current
    // validation session
    messages: [],
    // current validation config
    // name: validation type
    config: {},
    // the interface method
    // `data` is key => value pairs
    validate: function (data) {
        var i, msg, type, checker, result_ok;
        // reset all messages
        this.messages = [];
        for (i in data) {
            if (data.hasOwnProperty(i)) {
                type = this.config[i];
                checker = this.types[type];
                if (!type) {
                    continue; // no need to validate
                }
                if (!checker) { // uh-oh
                    throw {
                        name: "ValidationError",
                        message: "No handler to validate type " + type
                    };
                }
                result_ok = checker.validate(data[i]);
                if (!result_ok) {
                    msg = "Invalid value for *" + i + "*, " + checker.instructions;
                    this.messages.push(msg);
                }
            }
        }
        return this.hasErrors();
    },
    // helper
    hasErrors: function () {
        return this.messages.length !== 0;
    }
}

Như bạn có thể thấy, đối tượng validator nhận là generic và có thể thực hiện cho tất cả các trường hợp sử dụng validation. Nếu bạn muốn thêm một số trình validate khác chỉ cần xác định thêm cấu hình chạy và validate() method.
OK. Trên đây là 2 design pattern mà mình muốn giới thiệu đến các bạn trong nội dung bài blog này. Hẹn gặp lại các bạn trong những bài blog tiếp theo.

Một lần nữa thì nếu các bạn muốn tìm hiểu thêm về những gì mình đã viết trong bài blog thì các bạn có thể tham khảo link ở bên dưới nha. Cảm ơn vì đã đọc.

Tài liệu tham khảo:

http://sd.blackball.lv/library/JavaScript_Patterns_%282010%29.pdf

Add a Comment

Scroll Up