본문 바로가기
디자인패턴GoF/생성패턴

자바스크립트로 이해해보는 추상팩토리패턴 (Abstract Factory Pattern) In 생성패턴

by 봉이로그 2023. 11. 14.

추상팩토리 패턴에 대해서 팩토리메서드 패턴과 비교하여 살펴보자.

 

구분 팩토리 메서드 패턴 추상팩토리 패턴
중점 단일 객체의 생성 중점 관련된 객체 집합의 생성 중점
구성 주로 단일 추상 클래스에 있는
하나의 팩토리 메서드 를 사용하여 객체를 생성
여러 관련된 팩토리 메서드들을 가진 하나의 추상 팩토리가 존재
각 팩토리 메서드
는 다른 종류의 객체를 생성
확장성 서브클래스가 팩토리 메서드를 오버라이딩하여 객체 생성을 변경가능 새로운 종류의 객체 집합을 생성하기 위해 새로운 추상 팩토리를 생성가능

 

 

단순하게 설명해보자면,

 

추상 팩토리패턴은 여러개의 팩토리 클래스가 존재한다.

 

그 팩토리 클래스들은 가장 최상단의 추상팩토리클래스를 상속받아 구현한다.

 

공통화된 인터페이스는 가장 최상단의 추상팩토리클래스에서 정의를 하고, 각각의 팩토리클래스에서 코드를 확장하면 된다.

 

팩토리메서드패턴은 하나의 팩토리클래스를 이용하지만, 추상팩토리패턴은 여러개의 팩토리클래스를 이용하게 되어 팩토리메서드 패턴보다 좀 더 추상화와 캡슐화를 하게 된다.

 

코드를 통해 살펴보자.

 

회사별로 셔츠를 만든다고 가정해보자.

// Shirts 클래스

class Shirts {
  private tag: string;
  constructor(_tag) {
    this.tag = _tag;
  }

  setTag(_tag) {
    this.tag = _tag;
  }

  getTag() {
    return this.tag;
  }
}

 

// 팩토리메소드 패턴
// Shirts 객체를 생성하는 팩토리를 구현

class 옷공장 {
  typeMap: {
    A회사: Shirts;
    B회사: Shirts;
  };

  createShirts(_type) {
    if (_type === "A회사") {
      return new Shirts(_type);
    } else if (_type === "B회사") {
      return new Shirts(_type);
    }
  }
}

const factoryMethodMain = () => {
  const data = [
    { type: "A회사", attrs: { price: 50000 } },
    { type: "B회사", attrs: { price: 400000 } },
  ];

  const factory = new 옷공장();

  const 회사리스트 = data.map((v) => factory.createShirts(v.type));
  console.log("회사리스트", 회사리스트);
};

factoryMethodMain();

 

팩토리메소드 패턴은 회사가 추가될시 `createShirts` 메서드에 코드가 계속 추가 되어지게 된다.

 

새로운 종류의 회사가 추가될때마다 조건절을 추가해야하므로 코드의 확장성이 떨어질수 있다.

 

위의 팩토리메소드패턴을 추상클래스패턴으로 변경해본 코드이다.

// 추상팩토리 패턴

interface AbstractClothesFactory {
  createShirts: () => Shirts;
}

class A회사Factory implements AbstractClothesFactory {
  createShirts() {
    return new Shirts("A회사");
  }
}

class B회사Factory implements AbstractClothesFactory {
  createShirts() {
    return new Shirts("B회사");
  }
}

const abstractFactoryMain = () => {
  const createShirts = (factory: AbstractClothesFactory) => {
    return factory.createShirts();
  };

  const a = createShirts(new A회사Factory());
  const b = createShirts(new B회사Factory());

  console.log(a);
  console.log(b);
};

 

우선 구현에 필요한 것들을 나열해보자.

 

- Shirts 클래스

- createShirts를 추상화한 추상클래스

- 회사팩토리클래스

  - 확장 될시에, C회사팩토리 ... Z회사팩토리를 만들면된다.

 

구현방식을 풀어보면 이렇다.

 

먼저 추상클래스(AbstractClothesFactory) 에서 구체화해야할 메서드를 추상화한다.

 

팩토리패턴에서는 하나의 팩토리클래스에서 `createShirts`메서드의 조건절을 수정하여 회사를 추가했었다.

 

추상팩토리패턴에서는 회사별로 팩토리클래스를 생성하고, 그 클래스에서 추상클래스를 상속받아 `createShirts` 메소드를 오버라이딩 한다.

 

이제 객체를 생성할 메인함수 에서는 `createShirts` 함수를 생성한다.

 

함수의 매개변수는 추상 팩토리 클래스를 선언하고 추상팩토리클래스의 `createShirts` 메서드를 실행하여 회사별 객체를 생성할 수 있다.

 

결과적으로 추상팩토리패턴으로 구현을 하게 될시의 이점은 이렇다.

 

회사가 추가될때마다, 조건문을 추가하여 회사객체를 생성하지 않아 `createShirts` 부분에서 코드추가가 불필요해진다.

 

회사가 추가될때마다, C회사Factory 클래스를 생성하고, 추상클래스(AbstractClothesFactory) 를 상속받고 구현하면된다.

 

팩토리클래스추가되는 객체만 추가하면 된다.

 

예를들어, Shirts말고도 Jacket, Pants등 여러종류가 추가될수도 있을것이다.

class Jacket {
  private tag: string;
  constructor(_tag) {
    this.tag = _tag;
  }

  setTag(_tag) {
    this.tag = _tag;
  }

  getTag() {
    return this.tag;
  }
}

interface AbstractClothesFactory {
  createShirts: () => Shirts;
  createJacket: () => Jacket;
}

class A회사Factory implements AbstractClothesFactory {
  createShirts() {
    return new Shirts("A회사");
  }

  createJacket() {
    return new Jacket("A회사");
  }
}

class B회사Factory implements AbstractClothesFactory {
  createShirts() {
    return new Shirts("B회사");
  }

  createJacket() {
    return new Jacket("B회사");
  }
}

class C회사Factory implements AbstractClothesFactory {
  createJacket() {
    return new Jacket("C회사");
  }
  createShirts() {
    return new Shirts("C회사");
  }
}

const abstractFactoryMain = () => {
  const createShirts = (factory: AbstractClothesFactory) => {
    return factory.createShirts();
  };


  const data = [new A회사Factory(), new B회사Factory(), new C회사Factory()];
  const 회사리스트 = data.map((v) => createShirts(v));
  console.log(회사리스트);

/**
 * [ Shirts { tag: 'A회사' }, Shirts { tag: 'B회사' }, Shirts { tag: 'C회사' } ]
 */

};