Published on

Ice Factory Pattern

Javascript Class 키워드로 생성한 객체는 가끔 다음과 같이 의도치 않게 동작할 수 있다.

  • Class의 method가 re-assign 될 수 있다.
  • Class method 내부에서 사용하는 this가 해당 인스턴스가 아니라 다른것으로 바뀔 수 도 있다. (class의 method를 click EventListener로 등록한다거나..)

이러한 기존 Javascript Class에서 발생할 수 있는 문제를 막고자 Ice Factory Pattern 을 활용한다.

Ice Factory Pattern

function createShoppingCart({ db }) {
  return Object.freeze({
    addProduct,
    empty,
    getProducts,
    removeProduct,
  });

  function addProduct(product) {
    db.push(product);
  }

  function empty() {
    db = [];
  }

  function getProducts() {
    return Object.freeze([...db]);
  }

  function removeProduct(id) {
    // remove a product
  }
}

const db = [];
const cart = createShoppingCart({ db });
cart.addProduct({
  name: 'foo',
  price: 9.99,
});

Class 사용과 비교하여 Ice Factory Pattern에서 바뀐 부분은 다음과 같다.

  • 객체를 생성할 때 new 키워드를 더 이상 사용하지 않는다. factory method만 호출하여 객체를 생성할 수 있다.

  • 객체 내부에서 this를 더 이상 사용하지 않는다. 파라미터로 전달받은 db 변수에 직접 접근하여 사용한다. (Closure를 이용)

  • 생성한 객체는 immutable하다. Object.freeze 를 사용하여 새로운 property추가나, 기존 property 변경을 할 수 없게 만들어서 리턴하였다.

또한 아래와 같이 private멤버(secret) 추가도 쉽게 할 수 있다.

function makeThing(spec) {
  const secret = 'shhh!';

  return Object.freeze({
    doStuff,
  });

  function doStuff() {
    // We can use both spec
    // and secret in here
  }
}

상속대신 합성

코드 재사용을 위하여 상속보다는 합성을 이용한다.

아래와 같이 ProductList 팩토리 메소드가 있다.

function createProductList({ productDb }) {
    return Object.freeze({
        addProduct,
        empty,
        getProducts,
        removeProduct
    )}

    //...
}

ShoppingCart 팩토리에서 ProductList를 합성으로 전달받아 items property에 넣어 리턴한다.

function createShoppingCart(productList) {
    return Object.freeze({
      items: productList,
      //...
    })

    //...
)}

ShoppingCart 인스턴스 cart에서 ProductList의 addProduct method를 items property를 통해 호출한다.

const productDb = [];
const productList = createProductList({ productDb });
const cart = createShoppingCart(productList);

cart.items.addProduct();

물론 Ice Factory를 사용하는데에는 trade-off가 있다.

Class를 사용하는 것보다 Ice Factory는 더 느리고, memory를 더 많이 사용한다. (prototype 사용 X)

그렇지만 아주 많은 수의 인스턴스를 생성하는 것이 아니라면, 그 차이는 크지 않을 것이다.

그때 상황에 맞게 개발자가 어떤 옵션을 사용할지 결정되면 될 것이다.