팩토리 메서드 패턴 (Factory method)
JAVA/Design Pattern

팩토리 메서드 패턴 (Factory method)

반응형

팩토리 메서드 패턴은 객체의 생성 코드를 별도의 클래스/메서드로 분리하는 패턴이다.

이로써 

특정 기능의 구현은 개별 클래스를 통해서 제공을 하도록 설계를 하면서

호출하는 메서드 코드의 중복된 코드 발생과 기능 변경으로 인해 자주 변경되지 않도록 하기 위해서 
사용하는데 효과적이다.


다음 예를 살펴보자.

여러 엘레베이터 조작을위해 다음과 같이 구성이 되어있다고 가정해보자.

엘레베이터의 층수를 관리하는 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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import java.util.ArrayList;
import java.util.List;
 
public class ElevatorManager {
    private List<ElevatorController> controllers;
    private ThroughputScheduler scheduler;
    
    public ElevatorManager(int controllerCount) {
        controllers = new ArrayList<ElevatorController>(controllerCount);
        for (int i = 0; i < controllers.size(); i++) {
            ElevatorController controller = new ElevatorController(i);
            controllers.add(controller);
        }
        scheduler = new ThroughputScheduler();
    }
    
    public void requestElevator(int destination, Direction direction) {
        Scheduler scheduler;
        if (rushHour) {
          scheduler = new RushHourScheduler();
        } else {
          scheduler = new ThroughputScheduler();
        }
        int selectedElevator = scheduler.selectElevator(this, destination, direction);
        controllers.get(selectedElevator).gotoFloor(destination);
    }
 
}
 
 
public class ThroughputScheduler implements Scheduler {
    public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
        return 0;
    }
}
 
public class RushHourScheduler implements Scheduler {
    public int selectElevator(ElevatorManager manager, int destinataion, Direction direction) {
         return 1;
    }
}
 
 
public class ElevatorController {
    private int id;
    private int curFloor;
    
    public ElevatorController(int id) {
        this.id = id;
        this.curFloor = 1;
    }
    
    public void gotoFloor(int destination) {
        System.out.println("Elevator ]" + id + "] Floor " + curFloor);
        curFloor = destination;
        System.out.println(" ==> " + curFloor);
    }
}
package factory;
 
public enum Direction {
    UP,
    DOWN
}
cs




위의 코드에서 보면
requestElevator에서 scheduler는 현재 시간에 따라 스케줄을 다르게 동작시킨다.

Scheduler라는 인터페이스를 사용하여 각 상황에 맞는 스케줄러를 받는 스트래티지 패턴을 사용하였다.

하지만 ElevatorManager 클래스의 requestElevator() 메서드는 엘레베이터 스케줄링 전략이 변경될 때 마다 수정되어야 하는 단점이 있다.

팩토리 메서드 패턴을 이용한 해결방법

먼저

스케줄링 절략에 일치하는 클래스를 생성하는 코드를 requestElevator 메스드에서 분리해 별도의 클래스/메서드를 정의한다.

생성되는 스케줄러 클래스의 경우 계속해서 새로운 객체를 만들필요가 없으므로 싱글톤으로 사용되도록 변경한다.



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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package factory;
 
import java.util.ArrayList;
import java.util.List;
 
public class ElevatorManager {
    private List<ElevatorController> controllers;
    private SchedulingID id;
    
    public ElevatorManager(int controllerCount, SchedulingID id) {
        controllers = new ArrayList<ElevatorController>(controllerCount);
        for (int i = 0; i < controllers.size(); i++) {
            ElevatorController controller = new ElevatorController(i);
            controllers.add(controller);
        }
        this.id = id;
    }
    
    public void requestElevator(int destination, Direction direction) {
        int selectedElevator = SchedulerFactory.getScheduler(id).selectElevator(this, destination, direction); 
        controllers.get(selectedElevator).gotoFloor(destination);
    }
 
}
 
package factory;
 
public class RushHourScheduler implements
        Scheduler {
    private static Scheduler scheduler;
    
    public static synchronized Scheduler getInstance() {
        if (scheduler == null) {
            scheduler = new RushHourScheduler();
        }
        
        return scheduler;
    }
    
    public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
        return 1;
    }
 
}
 
package factory;
 
public class ThroughputScheduler implements Scheduler {
    private static Scheduler scheduler;
    
    public static synchronized Scheduler getInstance() {
        if (scheduler == null) {
            scheduler = new ThroughputScheduler();
        }
        
        return scheduler;
    }
    
    public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
        return 0;
    }
 
}
 
package factory;
 
public class SchedulerFactory {
    public static Scheduler getScheduler(SchedulingID id) {
        Scheduler scheduler = null;
        switch (id) {
        case RUSH_HOUR:
            scheduler = RushHourScheduler.getInstance();
            break;
            
        case NORMAL_TIME:
            scheduler = ThroughputScheduler.getInstance();
            break;
        }
        
        return scheduler;
    }
}
package factory;
 
public enum SchedulingID {
    RUSH_HOUR,
    NORMAL_TIME;
}
cs





또다른 팩토리 메스트 패턴의 형태로써 

ElevatorManager 를 추상클래스로 정의하고 
getScheduler를 추상메서드로 정의한다.

그리고 각 Schuler 클래스들이 ElevatorManager를 상속받아 getScheduler를 구현하는 방식으로
설계할 수도 있다.



이런 구조에서 ElevatorManager 추상클래스에 requestElevator 메서드는 
하위의 Scheduler 클래스들의 공통메서드 이므로 템플릿 메스드에 해당된다.




반응형