팩토리 패턴

1. 팩토리 패턴이란?

객체를 생성하는 인터페이스는 미리 정의하되, 인스턴스를 만들 클래스의 결정은 서브 클래스 쪽에서 내리는 패턴이다.

즉, 여러개의 서브 클래스를 가진 슈퍼 클래스가 있을 때 인풋에 따라 하나의 자식 클래스의 인스턴스를 리턴해주는 방식이다.

이런 팩토리 패턴은

  • 어떤 클래스가 자신이 생성해야 하는 객체의 클래스를 예측할 수 없을 때

  • 생성할 객체를 기술하는 책임을 자신의 서브클래스가 지정했으면 할 때

사용된다.


2. 자바스크립트에서의 팩토리 패턴

2-1. 팩토리 패턴 적용 전

class Car {
  constructor(props) {
    this.name = props.name;
    this.year = props.year;
    this.price = props.price;
  }
  getDiscount() {
    this.price -= this.sale;
  }
  getInfo() {
    return this.name + "의 가격은 " + this.price + "원 입니다.";
  }
}

class Avante extends Car {
  constructor(props) {
    super(props);
    this.sale = Math.floor(props.price / 50);
  }
}

class Sonata extends Car {
  constructor(props) {
    super(props);
    this.sale = Math.floor(props.price / 60);
  }
}

class Grandeur extends Car {
  constructor(props) {
    super(props);
    this.sale = Math.floor(props.price / 70);
  }
}

const getFinalPrice = (info) => {
  let car;
  if (info.name.toLowerCase() === "avante") {
    car = new Avante(info);
  } else if (info.name.toLowerCase() === "sonata") {
    car = new Sonata(info);
  } else if (info.name.toLowerCase() === "grandeur") {
    car = new Grandeur(info);
  }
  car.getDiscount();
  return car.getInfo();
};

이와 같은 코드가 있다고 가정하자. 자동차 주문이 들어오면 자동차의 이름에 따라 car 인스턴스를 생성하고, 할인하고, 최종 정보를 리턴하는 간단한 코드이다.

하지만 자동차의 종류가 더 많아진다면 코드변화에 대응하기엔 좋지 않는 코드이다. 즉 변화에 닫힌코드이다.

코드를 구현할 때에는 변하는 부분과 변하지 않는 부분을 분리해야 하는데 위의 코드에선 if문이 바로 변하는 부분이다. 이를 따로 분리하여 공장으로 만들어보자.


2-2. 팩토리 패턴 적용 후

class Car {
  constructor(props) {
    this.name = props.name;
    this.year = props.year;
    this.price = props.price;
  }
  getDiscount() {
    this.price -= this.sale;
  }
  getInfo() {
    return this.name + "의 가격은 " + this.price + "원 입니다.";
  }
}

class Avante extends Car {
  constructor(props) {
    super(props);
    this.sale = Math.floor(props.price / 50);
  }
}

class Sonata extends Car {
  constructor(props) {
    super(props);
    this.sale = Math.floor(props.price / 60);
  }
}

class Grandeur extends Car {
  constructor(props) {
    super(props);
    this.sale = Math.floor(props.price / 70);
  }
}

class CarFactory {
  nameMap = {
    avante: Avante,
    sonata: Sonata,
    grandeur: Grandeur,
  };
  create(props) {
    try {
      const Type = this.nameMap[props?.name?.toLowerCase()];
      return new Type(props);
    } catch (error) {
      console.error("error create new car", error);
    }
  }
}

CarFactory에 대해 자세히 살펴보자.

위의 코드를 살펴보면 CarFactory 클래스가 객체를 찍어내는 역할을 한다. 즉, 객체를 만드는 공장이다. CarFactory의 메서드엔 create가 있고 이를 호출하여 원하는 자동차를 만들 수 있다. 예를 들어 보자.

const factory = new CarFactory();
const car1 = factory.create({ name: "Avante", year: 2016, price: 10000000 });

먼저 factory인스턴스를 하나 생성하고 create 메서드를 호출하는데 매개변수로 객체로 전달한다. 이 객체엔 자동차의 정보가 담겨져 있다.

create 메서드가 실행되는 과정을 자세히 살펴보자.

  1. create 메서드가 호출되면 매개변수의 name의 값에 따라 Type이 정해진다.

    const Type = this.nameMap[props?.name?.toLowerCase()];

    Type에 따라 어떤 종류의 자동차가 만들어지는지 정해진다. 위의 예시에선 name의 값이 Avante이기 때문에 아반때 자동차가 만들어진다.

  2. Type에 맞는 자동차 인스턴스가 만들어지고 이를 반환한다.

    return new Type(props);
  3. 예제에서는 아반때 자동차가 만들어 지므로 Avante 클레스가 생성이되고 이 클래스는 Car 클래스를 상속 받고있다.

    class Avante extends Car {
      constructor(props) {
        super(props);
        this.sale = Math.floor(props.price / 50);
      }
    }
    
    class Car {
      constructor(props) {
        this.name = props.name;
        this.year = props.year;
        this.price = props.price;
      }
      getInfo() {
        return this.name + "의 가격은 " + this.price + " 입니다.";
      }
    }

2-3. 자동차의 최종 가격을 반환하는 함수

const getFinalPrice = (info) => {
  const factory = new CarFactory();
  const car = factory.create(info);
  car.getDiscount();
  return car.getInfo();
};

팩토리 패턴을 적용함으로써 자동차 인스턴스를 만드는 부분을 따로 분리하여 관리를 할 수 있게 되었다.


2-4. Array.map() 메서드를 사용한 인스턴스 생성

만약 여러 대의 자동차 정보가 담긴 data가 받았다고 생각해보자. 우리는 이 data를 가지고 모든 자동차 인스턴스를 만들어야 한다. data의 예는 아래와 같다.

const data = [
  { name: "Avante", year: 2012, price: 10000000 },
  { name: "Sonata", year: 2022, price: 30000000 },
  { name: "Avante", year: 2016, price: 18000000 },
  { name: "Grandeur", year: 2008, price: 15000000 },
  { name: "Genesis", year: 2020, price: 40000000 },
];

data를 바탕으로 자동차 인스턴스를 만드는 코드는 아래와 같다.

const factory = new CarFactory();
const cars = data.map((item) => factory.create(item));

인덱스4번 데이터에 해당하는 인스턴스는 생성되지 않고 나머지 자동차 인스턴스는 생성된다. errorcreate 메서드 내에서 발생한 것이다.

만약 create 메서드를 static 으로 정의하면 정적 메서드가 되며 이는 클래스의 인스턴트 없이 호출이 가능하게 되 메모리를 절약할 수 있고 개별 인스턴스에 묶이지 않으며 클래스 내의 함수를 정의할 수 있다.


출처

[생성 패턴] 팩토리 패턴(Factory Pattern) 이해 및 예제 Javascript Factory Pattern 팩토리 패턴 반복 객체 생성 패턴 팩토리메소드 패턴(Factory Method Pattern)

도움이 많이 되었던 유튜브 🎬


📅 2022-09-20

Last updated