추상 팩토리 메서드 패턴(Abstract Factory Method Pattern)
- 관련성이 있는 여러 종류의 객체를 일관된 방식으로 생성하는 경우에 유용
엘리베이터 부품 업체의 변경하기 Example
LG와 현대 업체의 모터와 문을 지원하는 클래스
템플릿 메서드의 적용
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
public abstract class Door {
private DoorStatus doorStatus ;
public Door() {
doorStatus = DoorStatus.CLOSED ;
}
public DoorStatus getDoorStatus() {
return doorStatus ;
}
public void close() { // 템플릿 메서드
if ( doorStatus == DoorStatus.CLOSED ) return ;
doClose() ;
doorStatus = DoorStatus.CLOSED ;
}
protected abstract void doClose() ;
public void open() { // 템플릿 메서드
if ( doorStatus == DoorStatus.OPENED ) return ;
doOpen() ;
doorStatus = DoorStatus.OPENED ;
}
protected abstract void doOpen() ;
}
public class LGDoor extends Door {
protected void doClose() {
System.out.println("close LG Door") ;
}
protected void doOpen() {
System.out.println("open LG Door") ;
}
}
public class HyundaiDoor extends Door {
protected void doClose() {
System.out.println("close Hyundai Door") ;
}
protected void doOpen() {
System.out.println("open Hyundai Door") ;
}
}
- 팩토리 메서드 패턴의 적용: MotorFactory, DoorFactory
1
2
3
4
5
6
7
8
9
10
11
public enum VendorID { LG, HYUNDAI }
public class MotorFactory {
public static Motor createMotor(VendorID vendorID) {
Motor motor = null ;
switch ( vendorID ) {
case LG : motor = new LGMotor() ; break ;
case HYUNDAI : motor = new HyundaiMotor() ; break ;
}
return motor ;
}
}
1
2
3
4
5
6
7
8
9
10
public class DoorFactory {
public static Door createDoor(VendorID vendorID) {
Door door = null ;
switch ( vendorID ) {
case LG : door = new LGDoor() ; break ;
case HYUNDAI : door = new HyundaiDoor() ; break ;
}
return door ;
}
}
- 클라이언트 코드
1
2
3
4
5
6
7
8
9
10
public class Client {
public static void main(String[] args) {
Door lgDoor = DoorFactory.createDoor(VendorID.LG) ;
Motor lgMotor = MotorFactory.createMotor(VendorID.LG) ;
lgMotor.setDoor(lgDoor) ;
lgDoor.open() ;
lgMotor.move(Direction.UP) ;
}
}
- 실행 결과
문제점
- 현재 프로그램은 LG의 부품(LGMotor와 LGDoor)를 사용하고 있다. 만약 다른 제조업체의 부품을 사용해야 한다면? 예를 들어 LG 부품 대신에 현대의 부품(HyundaiMotor와 HyundaiDoor)를 사용해야 한다면?
- 팩토리 메서드 패턴을 이용한 현대 부품 사용
1 2 3 4 5 6 7 8 9 10
public class Client { public static void main(String[] args) { Door hyundaiDoor = DoorFactory.createDoor(VendorID.HYUNDAI) ; Motor hyundaiMotor = MotorFactory.createMotor(VendorID.HYUNDAI) ; hyundaiMotor.setDoor(hyundaiDoor) ; hyundaiDoor.open() ; hyundaiMotor.move(Direction.UP) ; } }
- 부품 별로 팩토리를 구현해야 함
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
public class Client { public static void main(String[] args) { Door hyundaiDoor = DoorFactory.createDoor(VendorID.HYUNDAI) ; Motor hyundaiMotor = MotorFactory.createMotor(VendorID.HYUNDAI) ; hyundaiMotor.setDoor(hyundaiDoor) ; ArrivalSensor hyundaiArrivalSensor = ArrivalSensorFactory.createArrivalSensor (VendorID.HYUNDAI) ; WeightSensor hyundaiWeightSensor = WeightSensorFactory.createWeightSensor (VendorID.HYUNDAI) ; ElevatorLamp hyundaiElevatorLamp = ElevatorLampFactory.createElevatorLamp (VendorID.HYUNDAI) ; FloorLamp hyundaiFloorLamp = FloorLampFactory.createFloorLamp (VendorID.HYUNDAI) ; DirectionLamp hyundaiDirectionLamp = DirectionLampFactory.createDirectionLamp (VendorID.HYUNDAI) ; Speaker hyundaiSpeaker = SpeakerFactory.createSpeaker (VendorID.HYUNDAI) ; ElevatorButton hyundaiElevatorButton = ElevatorButtonFactory.createElevatorButton (VendorID.HYUNDAI) ; FloorButton hyundaiFloorButton = FloorButtonFactory.createElevatorFloorButton (VendorID.HYUNDAI) ; hyundaiDoor.open() ; hyundaiMotor.move(Direction.UP) ; } }
- 코드량이 너무 방대해지고 하나씩 일일이 부품을 추가해줘야하는 문제점 발생
- 게다가 새로운 제조업체의 부품을 사용해야 한다면? 예를 들어 삼성에서 엘리베이터 부품을 생산하기 시작해서 SamsungMotor와 SamsungDoor 클래스를 사용해야 한다면?
- 각 팩토리에서 새로운 제조 업체 부품을 생성하도록 수정이 필요함
- 각 팩토리에서 새로운 제조 업체 부품을 생성하도록 수정이 필요함
해결책
부품이 아니라 제조업체 별로 팩토리를 정의함
제조업체의 팩토리 클래스의 공통 추상 팩토리 클래스를 정의
소스 코드
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
public abstract class ElevatorFactory {
public abstract Motor createMotor() ;
public abstract Door createDoor() ;
}
public class LGElevatorFactory extends ElevatorFactory {
public Motor createMotor() {
return new LGMotor() ;
}
public Door createDoor() {
return new LGDoor() ;
}
}
public class HyundaiElevatorFactory extends ElevatorFactory {
public Motor createMotor() {
return new HyundaiMotor() ;
}
public Door createDoor() {
return new HyundaiDoor() ;
}
}
public class Client {
public static void main(String[] args) {
ElevatorFactory factory = null ;
String vendorName = args[0] ;
if ( vendorName.equalsIgnoreCase("LG") )
factory = new LGElevatorFactory() ;
else
factory = new HyundaiElevatorFactory() ;
Door door = factory.createDoor() ;
Motor motor = factory.createMotor() ;
motor.setDoor(door) ;
door.open() ;
motor.move(Direction.UP) ;
}
}
실행 결과
새로운 제조 업체가 새로 추가되면? (삼성 부품의 지원)
소스 코드
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
public class SamsungElevatorFactory extends ElevatorFactory {
public Motor createMotor() {
return new SamsungMotor() ;
}
public Door createDoor() {
return new SamsungDoor() ;
}
}
public class SamsungDoor extends Door {
protected void doClose() {
System.out.println("close Samsung Door") ;
}
protected void doOpen() {
System.out.println("open Samsung Door") ;
}
}
public class SamsungMotor extends Motor {
protected void moveMotor(Direction direction) {
System.out.println("move Samsung Motor") ;
}
}
public class Client {
public static void main(String[] args) {
ElevatorFactory factory = null ;
String vendorName = args[0] ;
if ( vendorName.equalsIgnoreCase("LG") )
factory = new LGElevatorFactory() ;
else if ( vendorName.equalsIgnoreCase("Samsung") )
factory = new SamsungElevatorFactory() ;
else
factory = new HyundaiElevatorFactory() ;
Door door = factory.createDoor() ;
Motor motor = factory.createMotor() ;
motor.setDoor(door) ;
door.open() ;
motor.move(Direction.UP) ;
}
}
실행 결과
패턴의 추가적인 적용
- 제조업체 별로 Factory 클래스를 생성하는 부분을 팩토리 메소드 패턴을 적용하여 설계
- 제조업체 별 팩토리는 1개만 필요하다면 싱글턴 패턴을 적용
1 2 3 4 5 6
if ( vendorName.equalsIgnoreCase("LG") ) factory = new LGElevatorFactory() ; else if ( vendorName.equalsIgnoreCase("Samsung") ) factory = new SamsungElevatorFactory() ; else factory = new HyundaiElevatorFactory() ;
- 소스 코드
1 2 3 4 5 6 7 8 9 10 11
public class ElevatorFactoryFactory { public static ElevatorFactory getFactory(VendorID vendorID) { // 팩토리 메서드 ElevatorFactory factory = null ; switch ( vendorID ) { case LG: factory = LGElevatorFactory.getInstance() ; break ; case HYUNDAI : factory = HyundaiElevatorFactory.getInstance() ; break ; case SAMSUNG : factory = SamsungElevatorFactory.getInstance() ; break ; } return factory ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
public class LGElevatorFactory extends ElevatorFactory { // 싱글턴을 적용한 LG 팩토리 private static ElevatorFactory factory ; private LGElevatorFactory() {} public static ElevatorFactory getInstance() { if ( factory == null ) factory = new LGElevatorFactory(); return factory ; } public Motor createMotor() { return new LGMotor() ; } public Door createDoor() { return new LGDoor() ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
public class Client { public static void main(String[] args) { String vendorName = args[0] ; VendorID vendorID ; if ( vendorName.equalsIgnoreCase("LG")) vendorID = VendorID.LG ; else if ( vendorName.equalsIgnoreCase("Samsung")) vendorID = VendorID.SAMSUNG ; else vendorID = VendorID.HYUNDAI ; ElevatorFactory factory = ElevatorFactoryFactory.getFactory(vendorID) ; // 제조사별 팩토리 생성하는 부분 Door door = factory.createDoor() ; Motor motor = factory.createMotor() ; motor.setDoor(door) ; door.open() ; motor.move(Direction.UP) ; } }
추상 팩토리 패턴의 일반적인 형태
추상 팩토리 패턴의 순차 다이어그램
추상 팩토리 패턴을 위에 적용한 클래스 다이어그램
출처
- Java객체지향 디자인패턴(한빛미디어)