Web/JS

[JS] 디자인 패턴(Design Pattern)_팩토리 패턴(Factory Pattern)

메바동 2023. 2. 23. 22:02
728x90

"JavaScript Design Pattern" 포스팅은 ChatGPT에게 "Essential design patterns for JavaScript developers to learn"로 질문한 뒤 나온 내용에 대해 정리하는 포스팅입니다.

 


 

1. 팩토리 패턴(Factory Pattern)

팩토리 패턴은 생성할 객체의 정확한 클래스나 생성자 함수를 지정하지 않고도 객체를 생성할 수 있는 방법을 제공하는 생성 패턴이다.

팩토리 패턴은 객체를 생성하기 위한 일반 인터페이스를 제공하고 하위 클래스가 생성될 객체의 유형을 변경할 수 있도록 한다.

 

팩토리 패턴은 다음과 같은 이점을 제공한다.

  • 캡슐화: 객체 생성이 애플리케이션의 로직과 분리되어 보다 모듈화 되고 유지 관리가 용이한 아키텍처를 제공한다.
  • 유연성: 팩토리 패턴을 사용하면 새 구현 클래스를 추가하기만 하면 새 객체를 쉽게 생성할 수 있다.
  • 추상화: 팩토리 패턴은 객체 생성을 위한 높은 수준의 인터페이스를 제공하며 구현 세부 사항을 클라이언트 코드에서 숨길 수 있다.

 

JavaScript에서 팩토리 패턴은 함수나 객체를 사용하여 구현할 수 있다.

두 경우 모두 매개변수 집합 또는 configuration 옵션을 기반으로 다른 객체를 생성하고 반환할 수 있는 함수 또는 객체를 생성하는 것이 핵심이다.

 

JavaScript에서 팩토리 패턴의 가장 기본적인 구현은 팩토리 함수를 사용하는 것이다. 팩토리 함수는 전달된 인수를 기반으로 새 객체를 생성하고 반환하는 일반 함수이다.

 

다음은 팩토리 함수의 예이다:

 

function createPerson(name, age) {
  return {
    name: name,
    age: age,
    sayHello: function() {
      console.log("Hello, my name is " + this.name);
    }
  };
}

const person1 = createPerson("Alice", 30);
const person2 = createPerson("Bob", 25);

person1.sayHello(); // output: Hello, my name is Alice
person2.sayHello(); // output: Hello, my name is Bob

 

이 예제에서 createPerson 함수는 두 개의 인자 name과 age를 받는 팩토리 함수로, name과 age 속성을 가진 새 객체와 sayHello() 메서드를 반환한다.

 

JavaScript에서 팩토리 메서드를 구현하는 또 다른 방법은 팩토리 객체를 사용하는 것이다.

이 경우 팩토리 객체는 다양한 유형의 객체를 생성하는 메서드가 있는 객체이다. 각 메서드에는 생성할 객체의 특성을 정의하는 매개변수 집합이 사용된다.

 

다음은 팩토리 객체의 예이다:

 

const carFactory = {
  createCar: function(model, year, color) {
    return {
      model: model,
      year: year,
      color: color,
      start: function() {
        console.log("Starting " + this.model);
      },
      stop: function() {
        console.log("Stopping " + this.model);
      }
    };
  }
};

const car1 = carFactory.createCar("Ford Mustang", 2022, "red");
const car2 = carFactory.createCar("Tesla Model S", 2021, "white");

car1.start(); // output: Starting Ford Mustang
car2.stop(); // output: Stopping Tesla Model S

 

이 예제에서 carFactory는 새 자동차 객체를 생성하는 createCar() 메서드가 있는 팩토리 객체이다.

createCar() 메서드는 model, year, color의 세 가지 인수를 받고, 이러한 속성과 start() 및 stop() 메서드를 가진 새 객체를 반환한다.

 

JavaScript에서 팩토리 패턴을 사용할 때의 주요 장점 중 하나는 객체 생성 로직을 캡슐화할 수 있어 생성할 수 있는 객체 유형을 더 쉽게 수정하거나 확장할 수 있다는 점이다.

 

 

 

2. Factory Pattern 연습 코드

ChatGPT에게 "Give me a challenge to practice the Factory Pattern in JavaScript."라고 질문한 뒤 나온 문제와 그에 대한 답이다.

 

 

스켈레톤 코드는 다음과 같다.

 

// 요리 유형 정의
const DISH_TYPES = {
  BURGER: 'burger',
  PIZZA: 'pizza',
  PASTA: 'pasta'
};

// 각 요리 유형에 사용 가능한 토핑과 소스 정의
const TOPPINGS = {
  [DISH_TYPES.BURGER]: ['lettuce', 'tomato', 'cheese', 'bacon'],
  [DISH_TYPES.PIZZA]: ['mushrooms', 'olives', 'peppers', 'sausage'],
  [DISH_TYPES.PASTA]: ['meatballs', 'mushrooms', 'tomato sauce', 'cream sauce']
};

const SAUCES = {
  [DISH_TYPES.BURGER]: ['ketchup', 'mayonnaise', 'mustard'],
  [DISH_TYPES.PIZZA]: ['tomato sauce', 'bbq sauce', 'ranch'],
  [DISH_TYPES.PASTA]: ['tomato sauce', 'cream sauce', 'pesto']
};

// DishFactory 클래스 정의
class DishFactory {
  // 생성 메서드를 구현
  // 요리 유형, 토핑, 소스를 받아야 함
  // 요리 유형이 유효하고 토핑/소스가 요리 유형에 유효하면 요리 객체를 반환
  // 그렇지 않으면 null 또는 오류 메시지를 반환
}

// Usage
const dishFactory = new DishFactory();

// Create a burger with lettuce, tomato, cheese and ketchup
const burger = dishFactory.create(DISH_TYPES.BURGER, ['lettuce', 'tomato', 'cheese'], ['ketchup']);
console.log(burger); // { type: 'burger', toppings: ['lettuce', 'tomato', 'cheese'], sauces: ['ketchup'] }

// Create a pasta with meatballs, tomato sauce and cream sauce
const pasta = dishFactory.create(DISH_TYPES.PASTA, ['meatballs'], ['tomato sauce', 'cream sauce']);
console.log(pasta); // { type: 'pasta', toppings: ['meatballs'], sauces: ['tomato sauce', 'cream sauce'] }

// Try to create a salad with cheese and ranch (invalid dish type and invalid sauce for pizza)
const salad = dishFactory.create('salad', ['cheese'], ['ranch']);
console.log(salad); // null or an error message

 

이를 구현한 코드는 다음과 같다.

 

// 요리 유형 정의
const DISH_TYPES = {
  BURGER: 'burger',
  PIZZA: 'pizza',
  PASTA: 'pasta',
};

// 각 요리 유형에 사용 가능한 토핑과 소스 정의
const TOPPINGS = {
  [DISH_TYPES.BURGER]: ['lettuce', 'tomato', 'cheese', 'bacon'],
  [DISH_TYPES.PIZZA]: ['mushrooms', 'olives', 'peppers', 'sausage'],
  [DISH_TYPES.PASTA]: ['meatballs', 'mushrooms', 'tomato sauce', 'cream sauce'],
};

const SAUCES = {
  [DISH_TYPES.BURGER]: ['ketchup', 'mayonnaise', 'mustard'],
  [DISH_TYPES.PIZZA]: ['tomato sauce', 'bbq sauce', 'ranch'],
  [DISH_TYPES.PASTA]: ['tomato sauce', 'cream sauce', 'pesto'],
};

// DishFactory 클래스 정의
class DishFactory {
  // 생성 메서드를 구현
  // 요리 유형, 토핑, 소스를 받아야 함
  // 요리 유형이 유효하고 토핑/소스가 요리 유형에 유효하면 요리 객체를 반환
  // 그렇지 않으면 null 또는 오류 메시지를 반환
  create(dishType, toppings, sauces) {
    if (!Object.values(DISH_TYPES).includes(dishType)) {
      console.error('존재하지 않는 요리 유형입니다.');
      return null;
    }

    if (!toppings.every((topping) => TOPPINGS[dishType].includes(topping))) {
      console.error('요리 유형에 맞지 않는 토핑입니다.');
      return null;
    }

    if (!sauces.every((sauce) => SAUCES[dishType].includes(sauce))) {
      console.error('요리 유형에 맞지 않는 소스입니다.');
      return null;
    }

    return {
      type: dishType,
      toppings,
      sauces,
    };
  }
}

// Usage
const dishFactory = new DishFactory();

// Create a burger with lettuce, tomato, cheese and ketchup
const burger = dishFactory.create(
  DISH_TYPES.BURGER,
  ['lettuce', 'tomato', 'cheese'],
  ['ketchup']
);
console.log(burger); // { type: 'burger', toppings: ['lettuce', 'tomato', 'cheese'], sauces: ['ketchup'] }

// Create a pasta with meatballs, tomato sauce and cream sauce
const pasta = dishFactory.create(
  DISH_TYPES.PASTA,
  ['meatballs'],
  ['tomato sauce', 'cream sauce']
);
console.log(pasta); // { type: 'pasta', toppings: ['meatballs'], sauces: ['tomato sauce', 'cream sauce'] }

// Try to create a salad with cheese and ranch (invalid dish type and invalid sauce for pizza)
const salad = dishFactory.create('salad', ['cheese'], ['ranch']);
console.log(salad); // null or an error message
728x90