팩토리 메서드 패턴(Factory Method Pattern)
- 객체의 생성 코드를 별도의 클래스/메소드로 분리함으로써 객체 생성의 변화를 대비하는 데 유용
- 팩토리 메소드 패턴은 객체의 생성 코드를 별도의 클래스/메소드로 분리함으로써 객체 생성 방식의 변화를 대비하는 데 유용하다.
여러 방식의 엘리베이터 스케줄링 방법 지원하기 Example
- ElevatorManager 클래스: 여러 엘리베이터 중에서 스케줄링에 따라서 하나의 엘리베이터를 선택하고 이동시킴
- ElevatorController 클래스: 하나의 엘리베이터 이동을 제어하는 클래스
- ThroughputScheduler 클래스: 처리량을 기준으로 스케줄링하는 클래스
- 소스 코드
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 ElevatorManager {
private List<ElevatorController> controllers;
private ThroughputScheduler scheduler;
public ElevatorManager(int controllerCount) {
controllers = new ArrayList<ElevatorController>(controllerCount);
for ( int i = 0; i < controllerCount; i ++ ) {
ElevatorController controller = new ElevatorController(i+1);
controllers.add(controller);
}
scheduler = new ThroughputScheduler();
}
void requestElevator(int destination, Direction direction) {
// ThroughputScheduler를 이용해서 엘리베이터를 선택함
int selectedElevator = scheduler.selectElevator(this, destination, direction);
controllers.get(selectedElevator).gotoFloor(destination); // 선택된 엘리베이터를 이동 시킴
}
}
public class ThroughputScheduler {
public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
return 0; // 임의로 선택한다.
}
}
public class ElevatorController {
private int id;
private int curFloor;
public ElevatorController(int id) {
this.id = id;
curFloor = 1;
}
public void gotoFloor(int destination) {
System.out.print("Elevator [" + id + "] Floor: " + curFloor); // 현재 층 갱신, 즉 주어진 목적지 층으로 엘리베이터가 이동함
curFloor = destination;
System.out.println(" ==> " + curFloor);
}
}
문제점
- 현재 ElevatorManager는 ThroughputScheduler를 이용하고 있다. 즉 엘리베이터의 처리량을 최대화시키는 전략을 사용하고 있다. 만약 다른 스케쥴링 전략을 사용해야 한다면? 예를 들어 사용자의 대기시간을 최소화시키는 엘리베이터를 선택하는 전략을 사용해야 한다면?
- 게다가 스케쥴링 전략이 프로그램 실행 중에 변경을 해야 한다면 즉 동적 스케쥴링을 지원해야 한다면? 예를 들어 오전에는 대기시간 최소화 전략을 사용하고 오후에는 처리량 최대화 전략을 사용해야 한다면?
스트래티지 패턴을 활용한 스케줄링 전략 설계
해결책
- 스케줄링 전략에 맞는 객체를 생성하는 코드를 별도로 정의
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
64
65
66
public enum SchedulingStrategyID { RESPONSE_TIME, THROUGHPUT, DYNAMIC }
public class SchedulerFactory {
public static ElevatorScheduler getScheduler(SchedulingStrategyID strategyID) {
ElevatorScheduler scheduler = null;
switch ( strategyID ) {
case RESPONSE_TIME : scheduler = new ResponseTimeScheduler();
break;
case THROUGHPUT : scheduler = new ThroughputScheduler(); break;
case DYNAMIC : { // 오전에는 대기 시간 최소화 전략, 오후에는 처리량 최대화 전략
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if ( hour < 12 ) // 오전
scheduler = new ResponseTimeScheduler();
else // 오후
scheduler = new ThroughputScheduler();
break;
}
}
return scheduler;
}
}
public class ElevatorManager {
private List<ElevatorController> controllers;
private SchedulingStrategyID strategyID;
public ElevatorManager(int controllerCount, SchedulingStrategyID strategyID) {
controllers = new ArrayList<ElevatorController>(controllerCount);
for ( int i = 0; i < controllerCount; i ++ ) {
ElevatorController controller = new ElevatorController(i+1);
controllers.add(controller);
}
this.strategyID = strategyID; // 스케줄링 전략을 설정함
}
public void setStrategyID(SchedulingStrategyID strategyID) {
this.strategyID = strategyID;
}
void requestElevator(int destination, Direction direction) {
// 주어진 전략 ID에 해당되는 ElevatorScheduler를 사용함
ElevatorScheduler scheduler = SchedulerFactory.getScheduler(strategyID);
System.out.println(scheduler);
int selectedElevator = scheduler.selectElevator(this, destination, direction);
controllers.get(selectedElevator).gotoFloor(destination);
}
}
public class Client {
public static void main(String[] args) {
ElevatorManager emWithResponseTimerScheduler =
new ElevatorManager(2, SchedulingStrategyID.RESPONSE_TIME);
emWithResponseTimerScheduler.requestElevator(10, Direction.UP);
ElevatorManager emWithThroughputScheduler =
new ElevatorManager(2, SchedulingStrategyID.THROUGHPUT);
emWithThroughputScheduler.requestElevator(10, Direction.UP);
ElevatorManager emWithDynamicScheduler =
new ElevatorManager(2, SchedulingStrategyID.DYNAMIC);
emWithDynamicScheduler.requestElevator(10, 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class SchedulerFactory {
public static ElevatorScheduler getScheduler(SchedulingStrategyID strategyID) {
ElevatorScheduler scheduler = null;
switch ( strategyID ) {
case RESPONSE_TIME :
scheduler = ResponseTimeScheduler.getInstance(); break;
case THROUGHPUT :
scheduler = ThroughputScheduler.getInstance(); break;
case DYNAMIC : {
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if ( hour < 12 ) // 오전
scheduler = ResponseTimeScheduler.getInstance();
else // 오후
scheduler = ThroughputScheduler.getInstance();
break;
}
}
return scheduler;
}
}
public class ThroughputScheduler implements ElevatorScheduler {
private static ElevatorScheduler scheduler;
private ThroughputScheduler() {}
public static ElevatorScheduler getInstance() {
if ( scheduler == null ) scheduler = new ThroughputScheduler();
return scheduler;
}
public int selectElevator(ElevatorManager manager,
int destination, Direction direction) {
return 0;
}
}
public class ResponseTimeScheduler implements ElevatorScheduler {
private static ElevatorScheduler scheduler;
private ResponseTimeScheduler() {}
public static ElevatorScheduler getInstance() {
if ( scheduler == null ) scheduler = new ResponseTimeScheduler();
return scheduler;
}
public int selectElevator(ElevatorManager manager,
int destination, Direction direction) {
return 1;
}
}
public class Client {
public static void main(String[] args) {
ElevatorManager emWithResponseTimerScheduler =
new ElevatorManager(2, SchedulingStrategyID.RESPONSE_TIME);
emWithResponseTimerScheduler.requestElevator(10, Direction.UP);
ElevatorManager emWithThroughputScheduler =
new ElevatorManager(2, SchedulingStrategyID.THROUGHPUT);
emWithThroughputScheduler.requestElevator(10, Direction.UP);
ElevatorManager emWithDynamicScheduler =
new ElevatorManager(2, SchedulingStrategyID.DYNAMIC);
emWithDynamicScheduler.requestElevator(10, 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public abstract class ElevatorManager {
private List<ElevatorController> controllers;
public ElevatorManager(int controllerCount) {
controllers = new ArrayList<ElevatorController>(controllerCount);
for ( int i = 0; i < controllerCount; i ++ ) {
ElevatorController controller = new ElevatorController(i+1);
controllers.add(controller);
}
}
protected abstract ElevatorScheduler getScheduler(); // primitive 메서드
void requestElevator(int destination, Direction direction) { // 템플릿 메서드
// 하위클래스에서 오버라이드한 getScheduler 메서드를 호출
ElevatorScheduler scheduler = getScheduler();
int selectedElevator = scheduler.selectElevator(this, destination, direction);
controllers.get(selectedElevator).gotoFloor(destination);
}
}
public class ElevatorManagerWithThroughputScheduling extends ElevatorManager {
public ElevatorManagerWithThroughputScheduling(int controllerCount) {
super(controllerCount);
}
protected ElevatorScheduler getScheduler() { // 처리량 최대화 전략을 사용함
ElevatorScheduler scheduler = ThroughputScheduler.getInstance();
return scheduler;
}
}
public class ElevatorManagerWithResponseTimeScheduling extends ElevatorManager {
public ElevatorManagerWithResponseTimeScheduling(int controllerCount) {
super(controllerCount);
}
protected ElevatorScheduler getScheduler() { // 대기 시간 최소화 전략을 사용함
ElevatorScheduler scheduler = ResponseTimeScheduler.getInstance();
return scheduler;
}
}
public class ElevatorManagerWithDynamicScheduling extends ElevatorManager {
public ElevatorManagerWithDynamicScheduling(int controllerCount) {
super(controllerCount);
}
protected ElevatorScheduler getScheduler() { // 동적 스케줄링을 사용함
ElevatorScheduler scheduler = null;
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if ( hour < 12 ) // 오전
scheduler = ResponseTimeScheduler.getInstance();
else // 오후
scheduler = ThroughputScheduler.getInstance();
return scheduler;
}
}
팩토리 메서드 패턴의 일반적인 형태
팩토리 메서드 패턴을 위에 적용한 클래스 다이어그램
출처
- Java객체지향 디자인패턴(한빛미디어)