구체적인 클래스에 의존하지 않고 서로 연관되거나 의존적인 객체들의 조합을 만드는 인터페이스를 제공한다. 추상 팩토리 패턴은 서로 관련되 있는 객체들을 묶어서 팩토리 클래스로 만들고, 이들의 팩토리를 조건에 따라 생성하게 다시 팩토리를 만들어 객체를 생성한다.
전반적인 패턴의 구조는 다음과 같다.
위 그림에서 각 클래스는 다음과 같은 역할을 한다.
1. AbstractFactory: 실제 팩토리 클래스의 공통 인터페이스
2. ConcreteFactory: 구체적인 팩토리 클래스로 AbstractFactory 클래스의 추상 메서드를 오버라이드해서 구체적인 제 품을 생성한다.
3. AbstractProduct: 제품의 공통 인터페이스
4. ConcreteProduct: 구체적인 팩토리 클래스에서 생성되는 구체적인 제품
예시:
컴퓨터와 컴퓨터 주변 기기를 생산한다고 해보자. 컴퓨터와 컴퓨터 주변 기기는 모두 같은 기업의 제품이여야 한다. 여기서는 키보드와 마우스를 생산한다고 가정하고 팩토리 패턴을 사용해 설계를 해보자.
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
|
public class ComputerFactory {
public void createComputer(String type) {
KeyboardFactory keyboardFactory = new KeyboardFactory();
MouseFactory mouseFactory = new MouseFactory();
keyboardFactory.createKeyboard(type);
mouseFactory.createMouse(type);
System.out.println(type + " computer created");
}
}
public class KeyboardFactory {
public Keyboard createKeyboard(String type) {
Keyboard keyboard = null;
return switch (type) {
case "LG" -> new LGKeyboard();
case "Samsung" -> new SamsungKeyboard();
default -> throw new IllegalArgumentException();
};
}
}
public interface Keyboard {
}
public class LGKeyboard implements Keyboard {
public LGKeyboard() {
System.out.println("Create LG keyboard");
}
}
public class SamsungKeyboard implements Keyboard {
public SamsungKeyboard() {
System.out.println("Create Samsung keyboard");
}
}
public class MouseFactory {
public Mouse createMouse(String type) {
Mouse mouse = null;
return switch (type) {
case "LG" -> new LGMouse();
case "Samsung" -> new SamsungMouse();
default -> throw new IllegalArgumentException();
};
}
}
public interface Mouse {
}
public class LGMouse implements Mouse {
public LGMouse() {
System.out.println("Create LG mouse");
}
}
public class SamsungMouse implements Mouse {
public SamsungMouse() {
System.out.println("Create Samsung mouse");
}
}
|
cs |
팩토리 패턴을 사용하면 결합도를 낮출 수 있다고 하지만, 위의 경우에는 결합도를 낮출 수 없다. 만약 제조사가 추가된다면 각 기기를 만드는 팩토리에 케이스를 추가해야 하고, 생성해야 하는 기기가 추가된다면 팩토리 객체를 생성하는 부분이 더 길어지게 된다.
이렇듯 팩토리 패턴은 하나의 상속구조를 가진 경우에는 유용하지만, 여러개의 서로 다른 상속 구조를 가진 객체들과 상호작용 할때는 코드가 난잡해 지는 문제가 있다. 이 문제는 추상 팩토리 패턴을 사용하면 해결할 수 있다.
각 부품을 기준으로 만들었던 팩토리를 제조사 기준으로 만들어 보자.
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
|
public class FactoryOfComputerFactory {
private ComputerFactory selectComputerBrand(String type) {
return switch (type) {
case "LG" -> new LGComputerFactory();
case "Samsung" -> new SamsungComputerFactory();
default -> null;
};
}
public void createComputer(String type) {
ComputerFactory computerFactory = selectComputerBrand(type);
computerFactory.createKeyboard();
computerFactory.createMouse();
}
}
public interface ComputerFactory {
Keyboard createKeyboard();
Mouse createMouse();
}
public class SamsungComputerFactory implements ComputerFactory {
@Override
public Keyboard createKeyboard() {
return new SamsungKeyboard();
}
@Override
public Mouse createMouse() {
return new SamsungMouse();
}
}
public class LGComputerFactory implements ComputerFactory {
@Override
public Keyboard createKeyboard() {
return new LGKeyboard();
}
@Override
public Mouse createMouse() {
return new LGMouse();
}
}
|
cs |
따라서 팩토리 패턴은 구성품 마다 팩토리를 만드는 것이고 추상 팩토리 패턴은 관련 객체를 한번에 캡슐화해 일관된 객체를 만드는 것이다.