포스트

[Creational Pattern] 추상 팩토리 패턴 (Abstract Factory Pattern)

Design Pattern / Creational Pattern

추상 팩토리 패턴의 정의와 해당 디자인 패턴의 예제 코드를 통한 이해 및 설명 정리

개념

  • 연관성있는 객체 군이 여러개 있을 경우 이들을 묶어 추상화하고, 어떤 구체적인 상황이 주어지면 팩토리 객체에서 집합으로 묶은 객체 군을 구현화하는 생성 패턴

  • 클라이언트에서 특정 객체를 사용할 때 팩토리 클래스만을 참조하여 특정 객체에 대한 구현부를 감추어 역할과 구현을 분리시킬 수 있음

  • 즉, 추상 팩토리 패턴의 핵심은 제품 “군” 집합을 타입 별로 찍어낼 수 있다는 점이 포인트

패턴 구조

abstract_factory

  • AbstractFactory

    • 최상위 공장 클래스

    • 여러 개의 제품들을 생성하는 여러 메소드들을 추상화함

  • ConcreteFactory

    • 서브 공장 클래스들은 타입에 맞는 제품 객체를 반환하도록 메소드들을 재정의함
  • AbstractProduct

    • 각 타입의 제품들을 추상화한 인터페이스
  • ConcreteProduct (ProductA ~ ProductB)

    • 각 타입의 제품 구현체들

    • 이들은 팩토리 객체로부터 생성됨

  • Client

    • 추상화된 인터페이스만을 이용하여 제품을 받기 때문에 구체적인 제품, 공장에 대해서는 모름 (사용만 할 줄 알고 원리는 모름)

Abstract Factory VS Factory Method

패턴 예제

버튼 만들기

abstract_factory_example

예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
const abstractFactory = (() => {
  let jobs = {};
  return {
    addJob: (job, Character) => {
      if (Character.prototype.attack) {
        // attack 메소드가 있어야만 등록 가능
        jobs[job] = Character;
      }
    },
    create: (job, options) => {
      // 등록한 작업을 바탕으로 실제 객체 생성
      let Character = jobs[job];
      return Character ? new Character(options) : null;
    },
  };
})();

const Emperor = (() => {
  class Emperor {
    constructor(options) {
      this.name = options.name;
    }
    attack(target) {
      console.log(this.name, "attacks", target, "\n");
    }
    proclaim() {
      console.log(this.name, "proclaims emperor", "\n");
    }
  }
  return Emperor;
})();

const Governor = (() => {
  class Governor {
    constructor(options) {
      this.name = options.name;
    }
    attack(target) {
      console.log(this.name, "attacks", target, "\n");
    }
    betray() {
      console.log(this.name, "betrays emperor", "\n");
    }
  }
  return Governor;
})();

const Runner = (() => {
  class Runner {
    constructor(options) {
      this.name = options.name;
    }
    attack() {
      return;
    }
    run() {
      console.log(this.name, "runs", "\n");
    }
  }
  return Runner;
})();

module.exports = { abstractFactory, Emperor, Governor, Runner };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const {
  abstractFactory,
  Emperor,
  Governor,
  Runner,
} = require("./abstract_factory");

abstractFactory.addJob("emperor", Emperor);
abstractFactory.addJob("governor", Governor);
abstractFactory.addJob("runner", Runner);

let emperorInfo = {
  nero: { name: "Nero" },
};
let governorInfo = {
  vindex: { name: "Vindex" },
  galba: { name: "Galba" },
  otho: { name: "Otho" },
  vitellius: { name: "Vitellius" },
  rufus: { name: "Rufus" },
};

const nero = abstractFactory.create("emperor", emperorInfo.nero);
const runner_nero = abstractFactory.create("runner", emperorInfo.nero);
const vindex = abstractFactory.create("governor", governorInfo.vindex);
const galba = abstractFactory.create("governor", governorInfo.galba);
const otho = abstractFactory.create("governor", governorInfo.otho);
const vitellius = abstractFactory.create("governor", governorInfo.vitellius);
const rufus = abstractFactory.create("governor", governorInfo.rufus);

nero.proclaim();
nero.attack("All");

vindex.betray();
vindex.attack(emperorInfo.nero.name);

galba.betray();
galba.attack(emperorInfo.nero.name);

otho.betray();
otho.attack(emperorInfo.nero.name);

vitellius.betray();
vitellius.attack(emperorInfo.nero.name);

rufus.betray();
rufus.attack(emperorInfo.nero.name);

runner_nero.run();

참고한 출처 사이트

Refactoring GURU

Inpa Dev Blog (디자인 패턴)

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.