추상 팩토리 메서드 패턴(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객체지향 디자인패턴(한빛미디어)
 
 



