Tìm hiểu về javascript design pattern (P1)

Khái niệm Design Pattern đã quá quen thuộc với các lập trình viên, bài viết sẽ tìm hiểu về một số design pattern và các implement chúng trong javascript

1. Constructor pattern

Đây là pattern dùng khi khởi tạo đối tượng trong javascript cùng với các method và thuộc tính. Đây là một pattern không phải xa lạ bởi nó được sử dụng khá nhiều. Hãy cùng xem xét ví dụ

// Cách khởi tạo bằng function
function Hero(name, specialAbility) {
  this.name = name;
  this.specialAbility = specialAbility;

  this.getDetails = function() {
    return this.name + ' can ' + this.specialAbility;
  };
}

// Implement bằng ES6
class Hero {
  constructor(name, specialAbility) {
    this._name = name;
    this._specialAbility = specialAbility;

    this.getDetails = function() {
      return `${this._name} can ${this._specialAbility}`;
    };
  }
}

// tạo một đối tượng
const IronMan = new Hero('Iron Man', 'fly');

console.log(IronMan.getDetails()); // Iron Man can fly

2. Factory pattern

Factory pattern là một pattern khác dùng để khởi tạo. Trong ví dụ dưới đây ta sẽ tạo một factory class là BallFactory nó sẽ có một method nhận params, tùy thuộc vào các params này, object khi khởi tạo sẽ tương ứng với các class.

    class BallFactory {
      constructor() {
        this.createBall = function(type) {
          let ball;
          if (type === 'football' || type === 'soccer') ball = new Football();
          else if (type === 'basketball') ball = new Basketball();
          ball.roll = function() {
            return `The ${this._type} is rolling.`;
          };

          return ball;
        };
      }
    }

    class Football {
      constructor() {
        this._type = 'football';
        this.kick = function() {
          return 'You kicked the football.';
        };
      }
    }

    class Basketball {
      constructor() {
        this._type = 'basketball';
        this.bounce = function() {
          return 'You bounced the basketball.';
        };
      }
    }

// khởi tạo object
const factory = new BallFactory();

const myFootball = factory.createBall('football');
const myBasketball = factory.createBall('basketball');

console.log(myFootball.roll()); // The football is rolling.
console.log(myBasketball.roll()); // The basketball is rolling.
console.log(myFootball.kick()); // You kicked the football.
console.log(myBasketball.bounce()); // You bounced the basketball

3. Prototype pattern

Trong pattern này, ta sẽ sử dụng một object sẵn có để khởi tạo một object mới. Pattern này rất hữu ích trong javascript bởi nó tận dụng kế thừa prototype thay vì kế thừa bằng class. Hãy cùng xem ví dụ bên dưới

    // sử dụng Object.create 
    const car = {
      noOfWheels: 4,
      start() {
        return 'started';
      },
      stop() {
        return 'stopped';
      },
    };
    
    // Object.create(proto[, propertiesObject])

    const myCar = Object.create(car, { owner: { value: 'John' } });

    console.log(myCar.__proto__ === car); // true

4. Singleton pattern

Pattern này cũng rất phổ biến được sử dụng để khởi tạo đối tượng. Ví dụ

    class Database {
      constructor(data) {
        if (Database.exists) {
          return Database.instance;
        }
        this._data = data;
        Database.instance = this;
        Database.exists = true;
        return this;
      }

      getData() {
        return this._data;
      }

      setData(data) {
        this._data = data;
      }
    }

    // Sử dụng khi khởi tạo
    const mongo = new Database('mongo');
    console.log(mongo.getData()); // mongo

    const mysql = new Database('mysql');
    console.log(mysql.getData()); // mongo

5. Adapter pattern

Pattern này thường được sử dụng khi muốn tạo một "adapter trung gian" để kết nối giữa phần code mới refactor và phần code cũ, để có thể sử dụng một cách bình thường. Ví dụ

    // Ví dụ class cũ về tính toán
    class OldCalculator {
      constructor() {
        this.operations = function(term1, term2, operation) {
          switch (operation) {
            case 'add':
              return term1 + term2;
            case 'sub':
              return term1 - term2;
            default:
              return NaN;
          }
        };
      }
    }

    // Implment một class mới
    class NewCalculator {
      constructor() {
        this.add = function(term1, term2) {
          return term1 + term2;
        };
        this.sub = function(term1, term2) {
          return term1 - term2;
        };
      }
    }

    // Adapter Class giúp sử dụng class mới từ class cũ
    class CalcAdapter {
      constructor() {
        const newCalc = new NewCalculator();

        this.operations = function(term1, term2, operation) {
          switch (operation) {
            case 'add':
              // using the new implementation under the hood
              return newCalc.add(term1, term2);
            case 'sub':
              return newCalc.sub(term1, term2);
            default:
              return NaN;
          }
        };
      }
    }

    // Sử dụng class mới như class cũ
    const oldCalc = new OldCalculator();
    console.log(oldCalc.operations(10, 5, 'add')); // 15

    const newCalc = new NewCalculator();
    console.log(newCalc.add(10, 5)); // 15

    const adaptedCalc = new CalcAdapter();
    console.log(adaptedCalc.operations(10, 5, 'add')); // 15;

6. Decorator pattern

Pattern này sử dụng khi muốn thêm các hành vi hoặc chức năng vào một class. Ví dụ

    class Book {
      constructor(title, author, price) {
        this._title = title;
        this._author = author;
        this.price = price;
      }

      getDetails() {
        return `${this._title} by ${this._author}`;
      }
    }

    // decorator 1
    function giftWrap(book) {
      book.isGiftWrapped = true;
      book.unwrap = function() {
        return `Unwrapped ${book.getDetails()}`;
      };

      return book;
    }

    // decorator 2
    function hardbindBook(book) {
      book.isHardbound = true;
      book.price += 5;
      return book;
    }

    // Sử dụng
    const alchemist = giftWrap(new Book('Book1', 'Author1', 10));

    console.log(alchemist.isGiftWrapped); // true
    console.log(alchemist.unwrap()); // 'Unwrapped Book1 by Author1'

    const inferno = hardbindBook(new Book('Book2', 'Author2', 15));

    console.log(inferno.isHardbound); // true
    console.log(inferno.price); // 20

Trên đây là những pattern sử dụng khá phổ biến trong javascript khi khởi tạo đối tượng trong javascript. Hi vọng bài viết giúp ích cho mọi người

Reference

https://www.toptal.com/javascript/comprehensive-guide-javascript-design-patterns https://addyosmani.com/resources/essentialjsdesignpatterns/book/ https://medium.com/better-programming/javascript-design-patterns-25f0faaaa15