템플릿메서드패턴(Template Method Pattern)
- 전체적으로 동일하면서 부분적으로 상이한 문장을 가지는 메소드의 코드 중복을 최소화할 때 유용
- 템플릿 메소드 패턴은 전체적인 알고리즘을 구현하면서 상이한 부분은 하위 클래스에서구현할 수 있도록 해 주는 디자인 패턴으로서 전체적인 알고리즘의 코드를 재사용하는 데 유용하다.
- 전체적인 틀은 상위클래스에게 상속받고 변경되는 부분만 하위클래스에서 오버라이딩 하는 패턴
- 변화되는 부분만 추상 메소드로 만들어준다
엘리베이터 제어 프로그램 Example
- 엘리베이터 제어 시스템에서 모터를 구동시키는 기능
- HyundaiMotor 클래스: 모터를 제어하여 엘리베이터를 이동시키는 클래스
- Door 클래스: 문을 열거나 닫는 기능을 제공하는 클래스
- 소스 코드
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
public enum DoorStatus { CLOSED, OPENED }
public enum MotorStatus { MOVING, STOPPED}
public class Door {
private DoorStatus doorStatus;
public Door() {
doorStatus = DoorStatus.CLOSED;
}
public DoorStatus getDoorStatus() {
return doorStatus;
}
public void close() {
doorStatus = DoorStatus.CLOSED;
}
public void open() {
doorStatus = DoorStatus.OPENED;
}
}
public class HyundaiMotor {
private Door door;
private MotorStatus motorStatus;
public HyundaiMotor(Door door) {
this.door = door;
motorStatus = MotorStatus.STOPPED; // 초기에는 멈춘 상태
}
private void moveHyundaiMotor(Direction direction) {
// Hyundai Motor를 구동시킨다.
}
public MotorStatus getMotorStatus() { return motorStatus; }
private void setMotorStatus(MotorStatus motorStatus) { this.motorStatus = motorStatus; }
public void move(Direction direction) {
MotorStatus motorStatus = getMotorStatus();
if ( motorStatus == MotorStatus.MOVING ) return; // 이미 이동 중이면 아무 작업을 하지 않음
DoorStatus doorStatus = door.getDoorStatus();
if ( doorStatus == DoorStatus.OPENED ) door.close(); // 만약 문이 열려 있으면 먼저 문을 닫음
moveHyundaiMotor(direction); // 모터를 주어진 방향으로 이동
setMotorStatus(MotorStatus.MOVING); // 모터 상태를 이동 중으로 변경함
}
}
public class Client {
public static void main(String[] args) {
Door door = new Door();
HyundaiMotor hyundaiMotor = new HyundaiMotor(door);
hyundaiMotor.move(Direction.UP);
}
}
문제점
- HyundaiMotor 클래스는 현대모터를 구동시킨다. 만약 다른 회사의 모터를 제어해야 한다면? 예를 들어 LG모터를 구동시키기 위해서는 어떻게 해야 할까?
- => 내부소스코드 수정 (OCP위반)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LGMotor {
private Door door;
private MotorStatus motorStatus;
public LGMotor(Door door) {
this.door = door; motorStatus = MotorStatus.STOPPED;
}
private void moveLGMotor(Direction direction) {
// LG Motor를 구동시킴
}
public MotorStatus getMotorStatus() { return motorStatus; }
private void setMotorStatus(MotorStatus motorStatus) {
this.motorStatus = motorStatus;
}
public void move(Direction direction) {
MotorStatus motorStatus = getMotorStatus();
if ( motorStatus == MotorStatus.MOVING ) return;
DoorStatus doorStatus = door.getDoorStatus();
if ( doorStatus == DoorStatus.OPENED ) door.close();
moveLGMotor(direction); // move 메서드는 이 문장을 제외하면 HyundaiMotor와 동일함
setMotorStatus(MotorStatus.MOVING);
}
}
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
public abstract class Motor { // HyundaiMotor와 LGMotor에 공통적인 기능을 구현하는 클래스
protected Door door;
private MotorStatus motorStatus;
public Motor(Door door) {
this.door = door;
motorStatus = MotorStatus.STOPPED;
}
public MotorStatus getMotorStatus() {
return motorStatus;
}
protected void setMotorStatus(MotorStatus motorStatus) {
this.motorStatus = motorStatus;
}
}
public class HyundaiMotor extends Motor { // Motor를 상속받아서 HyundaiMotor를 구현함
public HyundaiMotor(Door door) {
super(door);
}
private void moveHyundaiMotor(Direction direction) {
// Hyundai Motor를 구동시킨다.
}
public void move(Direction direction) {
MotorStatus motorStatus = getMotorStatus();
if ( motorStatus == MotorStatus.MOVING ) return;
DoorStatus doorStatus = door.getDoorStatus();
if ( doorStatus == DoorStatus.OPENED )
door.close();
moveHyundaiMotor(direction); // move 메서드는 이 구문을 제외하면 LGMotor와 동일함
setMotorStatus(MotorStatus.MOVING);
}
}
public class LGMotor extends Motor {
public LGMotor(Door door) {
super(door);
}
private void moveLGMotor(Direction direction) {
// LG Motor를 구동시킨다.
}
public void move(Direction direction) {
MotorStatus motorStatus = getMotorStatus();
if ( motorStatus == MotorStatus.MOVING ) return;
DoorStatus doorStatus = door.getDoorStatus();
if ( doorStatus == DoorStatus.OPENED )
door.close();
moveLGMotor(direction); // move 메서드는 이 구문을 제외하면 HyundaiMotor와 동일함
setMotorStatus(MotorStatus.MOVING);
}
}
- HyundaiMotor클래스와 LGMotor클래스의 move()메서드에서 코드 중복 발생!!!
해결책
- move 메서드에서 공통적인 부분을 상위 클래스 Motor로 이동
템플릿 메서드의 개념
템플릿 메서드 패턴의 일반적인 형태
템플릿 메서드 패턴의 순차 다이어그램
템플릿 메서드 패턴을 위에 적용한 클래스 다이어그램
출처
- Java객체지향 디자인패턴(한빛미디어)