Javascript Decorator, Anotation

Nếu bạn chưa đọc bài viết mẫu thiết kế Decorator Pattern, thì mình nghĩ các bạn nên đọc qua để nắm được tư tưởng của bài toán. Decorator trong Javascript cũng thuộc về mẫu Decorator Pattern.

Decorator là gì?

Mang tư tưởng của các bài toán được giải quyết bằng mẫu Decorator Pattern. Đây là cách để wrap một đoạn code, để bổ sung thêm tính năng cho đoạn code đó và không thay đổi code cũ. Ví dụ:
+ Thêm logs trước và sau khi thực hiện method gốc.
+ Authentication, Authorization. Kiểm tra quyền trước khi chạy một method.
+ Bổ dung thông số cho đoạn code cũ…

Ví dụ dưới đây, chúng ta sử dụng Decorator là loggingDecorator, nhận tham số là method ban đầu. Khi đã wrap lại method gốc, mình có thể điều khiển được khi nào gọi method đó. Trong trường hợp này, thì trước khi mình gọi method, mình log “Starting” và sau khi thực hiện xong thì log “Finished”

function doSomething(name) {
  console.log('Hello, ' + name);
}

function loggingDecorator(wrapped) {
  return function() {
    console.log('Starting');
    const result = wrapped.apply(this, arguments);
    console.log('Finished');
    return result;
  }
}

const wrapped = loggingDecorator(doSomething);

doSomething('Graham');
// Hello, Graham

wrapped('Graham');
// Starting
// Hello, Graham
// Finished

Làm sao để sử dụng  Javascript Decorator?

Trong Javascript có một syntax gọi là Anotation, bắt đầu với ký tự @. Nếu các bạn đã làm việc với Java, thì sẽ khá quen thuộc với Anotation rồi. Có thể nói là Javascript bắt chước điểm hay đó qua.

@log()
@immutable()
class Example {
  @time('demo')
  doSomething() {
    //
  }
}

Tại sao phải sử dụng Decorator?

Trong quá trình phát triển phần mềm, nhu cầu thêm một tính năng cho một đoạn code khác là luôn luôn có. Decorator, Anotation cho ta một cách dễ dàng để decorate Class, Property, và quan trọng là vẫn giữ cho code khá clean, dễ đọc, dễ maintain.

Các loại Decorator?

Hiện tại Decorator được apply cho Class và các member của Class: Property, Method, Getter, Setter.

Cách viết Decorator cho Class Members

function decorator(target, name, descriptor) {
  // target: Class chứa member
  // name: Tên của member trong target
  // descriptor: Là descriptor cho member, sẽ được sử dụng để tạo ra "Trang trí" cho member.
  return descriptor;
}


function readonly(target, name, descriptor) {
  descriptor.writable = false;
  return descriptor;
}

class Example {
  a() {}
  @readonly
  b() {}
}

const e = new Example();
e.a = 1;
e.b = 2;
// TypeError: Cannot assign to read only property 'b' of object '#<Example>'

Ở ví dụ trên chúng ta chỉ tạo ra một wrapped method chỉ cho phép read, không cho phép re-assign. Ở ví dụ tiếp chúng ta sẽ thấy, decorator có thể điều khiển việc gọi method gốc.

function log(target, name, descriptor) {
  const original = descriptor.value;
  if (typeof original === 'function') {
    descriptor.value = function(...args) {
      console.log(`Arguments: ${args}`);
      try {
        const result = original.apply(this, args);
        console.log(`Result: ${result}`);
        return result;
      } catch (e) {
        console.log(`Error: ${e}`);
        throw e;
      }
    }
  }
  return descriptor;
}

Cách viết Decorator cho Class

Class Decorator nói chính xác là decorate, trang trí cho Constructor method. Decorator nhận vào duy nhất một tham số là Constructor của Class ban đầu.

function decorator(Class) {
  // Class is constructor method
  return (...args) => {
    // Do blabla
    
    return new Class(...args);
  };
}

Nguồn tham khảo:
- https://www.sitepoint.com/javascript-decorators-what-they-are

Leave A Reply

Your email address will not be published. Required fields are marked *