추상 팩토리 (Abstract Factory)
1. 의도
- 상세한 서브 클래스를 정의하지 않고 서로 관련성이 있거나 독립적인 여러 객체의 군을 생성하기 위한 인터페이스를 제공
- 한마디로 공통 특징을 가지는 객체를 생산하기 위한 포괄적인 "공장" 객체이다.
2. 활용
- 비슷한 특징을 가지는 제품을 하나의 제품군으로 묶고 추상 팩토리 클래스에서 생성자를 정의하지 않고 객체를 생성하는 인터페이스(가상 함수)를 제공
- 각 제품을 만드는 구체 팩토리(Concrete Factory)는 추상 팩토리(Abstract Factory)를 상속받아 제품에 대한 생성하는 인터페이스를 구현
- 사용자는 구체적인 제품 명을 알 필요 없이 제품군이 공통적으로 가지는 인터페이스를 통해 조작
ex) Widget을 찍어내는 WidgetFactory
상속 관계
MotifWidgetFactory : WidgetFactory | PMWidgetFactroy : WidgetFactory |
- PMWindow : Window - PMScrollBar : ScrollBar |
- MotifWindow : Window - MotifScrollBar : ScrollBar |
- WidgetFactory를 상속받아 구체 팩토리(MotifWidgetFactory, PMWidgetFactroy)에서 제품과 관련된 생성을 구현한다. (생성하는 책임을 가진다.)
- Window, ScrollBar는 제품과 관련된 추상 객체로서 사용자는 추상 객체의 인터페이스를 통해 제어할 수 있다.
3. 참여자
- AbstractFactory: 제품군을 생산할 수 있는 기본적인 인터페이스 제공
- ConcreteFactory: 제품군에 속하는 특정 제품에 대한 자세한 생산 내용을 구현
- AbstractProduct: 제품군이 공통적으로 가지는 기능을 추상화하고(Abstraction) 인터페이스를 제공
- ConcreteProduct: AbstractProduct를 상속받아 추상화한 인터페이스를 구현
- Client: AbstractFactory와 AbstractProduct의 인터페이스를 사용
4. 결과
- 구체적인 클래스를 분리: 구체 팩토리가 알아서 생산하기 때문에 사용자는 구체 제품을 알 필요없게 된다.
- 제품군을 쉽게 대체 가능: 구체 팩토리 종류만 바꾸면 원하는 제품 생산을 할 수 있다.
- 제품 사이의 일관성을 증진: 구체 팩토리들은 모두 동일한 특징을 가지는 제품을 생산한다.
- 새로운 종류의 제품을 제공하기 어려움: 반대로 같은 제품군이더라도 다른 특징을 보유하고 있으면 추상 팩토리에 인터페이스를 추가해야한다.
5. 구현
1) 팩토리를 단일체로 정의
굳이 제품을 찍어내는 공장을 두 개 이상 둘 필요 없다. -> 싱글톤 패턴
2) 추상 팩토리에서 제품 생산에 관한 추상화 (인터페이스 제공)
AbstractFactory는 제품을 생성하기 위한 인터페이스를 선언
ConcreteFactory를 정의하여 제품 생성을 위한 메서드를 override하여 인스턴스를 생성
3) 여러가지 가능한 구체 팩토리를 구현
MotifWidgetFactory, PMWidgetFactroy등의 구체 팩토리
추상 객체(Window, ScrollBar)를 상속하는 구체 객체(MotifiWindow, MotifScrollBar)를 생성
- 단점
class ConcreteFactory
CreateScrollBar() override
CreateWindow() override
CreateToolBar() -> 새로운 연산을 구체 팩토리에 정의한다면 사용자 측면에서는 알 수 없다. (ToolBar 사용 불가)
예시 코드
미로게임을 만들기 위한 기본 구성 요소(객체)
- MapSite : Wall, Door, Room을 위한 Enter() 메소드를 제공하는 인터페이스
- Wall : 미로의 벽
- Room : 미로의 방
- Door : 두 방을 연결하는 문
- Maze : Room, Door, Wall로 이루어진 Maze 객체
- MazeGame (Maze Game 전체를 관장하는 객체, 디자인 패턴과 상관 없음)
1
2
3
4
5
6
7
8
|
#include "Maze.h"
#include "MazeFactory.h"
class MazeGame
{
public:
Maze* CreateMaze(MazeFactory&);
};
|
cs |
목적: 여러 종류의 Maze를 찍어내는 공장을 추상화하고 내용을 구현하자
추상 팩토리 (Abstract Factory)
MazeFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include "Maze.h"
#include "Wall.h"
#include "Room.h"
#include "Door.h"
class MazeFactory
{
public:
MazeFactory();
virtual Maze* MakeMaze() const
{ return new Maze; }
virtual Wall* MakeWall() const
{ return new Wall; }
virtual Room* MakeRoom(int n) const
{ return new Room(n);}
virtual Door* MakeDoor(Room* r1, Room* r2) const
{ return new Door(r1, r2);}
};
|
cs |
- 추상 팩토리 객체로 기본 적인 Maze를 생성하기위해 필요한 인터페이스를 제공한다.
- 앞으로 나올 다양한 종류의 Maze를 만드는 구체 팩토리의 공통 기능이라 보면 되겠다.
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
|
Maze* MazeGame::CreateMaze(MazeFactory& factory)
{
//Maze* aMaze = new Maze;
//Room* r1 = new Room(1);
//Room* r2 = new Room(2);
//Door* theDoor = new Door(r1, r2);
//aMaze->AddRoom(r1);
//aMaze->AddRoom(r2);
//r1->SetSide(North, new Wall);
//r1->SetSide(South, theDoor);
//r1->SetSide(South, new Wall);
//r1->SetSide(West, new Wall);
//r2->SetSide(North, new Wall);
//r2->SetSide(South, new Wall);
//r2->SetSide(South, new Wall);
//r2->SetSide(West, theDoor);
/* 추상 팩토리의 인터페이스를 통한 Maze 생성 */
Maze* aMaze = factory.MakeMaze();
Room* r1 = factory.MakeRoom(1);
Room* r2 = factory.MakeRoom(2);
Door* aDoor = factory.MakeDoor(r1, r2);
aMaze->AddRoom(r1);
aMaze->AddRoom(r2);
r1->SetSide(North, factory.MakeWall());
r1->SetSide(South, aDoor);
r1->SetSide(South, factory.MakeWall());
r1->SetSide(West, factory.MakeWall());
r2->SetSide(North, factory.MakeWall());
r2->SetSide(South, factory.MakeWall());
r2->SetSide(South, factory.MakeWall());
r2->SetSide(West, aDoor);
return aMaze;
}
|
cs |
- MazeGame의 CreateMaze 메소드는 MazeFactory 객체를 이용하여
- MakeMaze(), MakeRoom(n), MakeWall() 등의 구성 요소를 생산하는 것을 추상 팩토리의 인터페이스를 통해 실현한다.
- 공장(factory) 객체를 매개변수로 던져줬기 때문에 사용자는 원하는 구체적인 공장에 따라 자동 생산된 Maze가 탄생된다.
구체 팩토리 (Concrete Factory)
- MazeFactory를 상속받아 특별한 미로를 생성하는 구체 팩토리를 정의할 수 있다.
- 미로를 구성하는 기본 객체를 상속하는 Concrete Product를 생산한다.
1. EnchantedMazeFactory
- 기본 문 -> 마법의 주문을 외워야 열리는 마법의 문 DoorNeedingSpell
- 기본 방 -> 마법이 걸린 방 EnchantedRoom 객체
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include "MazeFactory.h"
// ConcreteFactory implements AbstractFactory
class EnchantedMazeFactory : public MazeFactory
{
public:
EnchantedMazeFactory();
virtual Room* MakeRoom(int n) const override
{
return new EnchantedRoom(n, CastSpell());
}
virtual Door* MakeDoor(Room* r1, Room* r2) const override
{
return new DoorNeedingSpell(r1, r2);
}
protected:
Spell* CastSpell() const;
};
|
cs |
2. BombedMazeFactory
- 기본 벽 - > 폭탄이 설치된 벽
- 기본 방 - > 폭탄이 설치된 방
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include "MazeFactory.h"
class BombedMazeFactory : public MazeFactory
{
public:
BombedMazeFactory();
virtual Wall* MakeWall() const override {
return new BombedWall();
}
virtual Room* MakeRoom(int n) const override {
return new RoomWithABomb(n);
}
};
|
cs |
Abstract Factory 사용 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include "EnchantedMazeFactory.h"
#include "BombedMazeFactory.h"
#include "MazeGame.h"
int main()
{
MazeGame game;
// concrete factory to produce concrete products
BombedMazeFactory factory;
Maze* myMaze = game.CreateMaze(factory);
return 0;
}
|
cs |
- 원하는 제품(Maze)를 생성하기 위한 factory를 정의해주고 game의 CreateMaze 메소드를 통해 매개변수로 전달해주기만 하면 원하는 Maze가 생산된다.
- 사용자는 Maze가 구체적으로 어떤 미로인 지, 어떤 문, 어떤 방, 어떤 벽 인지 전혀 알 필요가 없다.
- 어떤 종류의 미로라도 Maze가 제공하는 인터페이스를 통해 문을 연다든가, 방을 들어가는 기능을 가질 수 있다.
참고 자료 : GoF 디자인 패턴
'디자인 패턴 > GoF' 카테고리의 다른 글
[디자인 패턴] 구조 패턴 (1) 적응자 Adapter (0) | 2021.01.26 |
---|---|
[디자인 패턴] 생성 패턴 (5) 싱글톤 Singleton (2021.02.19 수정) (0) | 2021.01.25 |
[디자인 패턴] 생성 패턴 (4) 원형 Prototype (0) | 2021.01.25 |
[디자인 패턴] 생성 패턴 (3) 팩토리 메서드 Factory Method (0) | 2021.01.24 |
[디자인 패턴] 생성 패턴 (2) 빌더 Builder (0) | 2021.01.24 |