JAVA/Design Pattern

템플릿 메서드 패턴 (Template method)

반응형

템플릿 메서드 패턴에 대해 알아보기 위해
먼저 문제의 소지가 있는 상황을 알아보자.

[예시]
만약 특정 제품의 모터를 동작시키고자 할 때 
현재 모터가 동작중인지, 작업장에 문이 열려 있는지 확인하고 동작하도록 하는
로직을 다음과 같이 만들어보자



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
89
90
91
92
93
package template;
 
public class Door {
    private DoorStatus doorStatus;
 
    public DoorStatus getDoorStatus() {
        return doorStatus;
    }
 
    public void setDoorStatus(DoorStatus doorStatus) {
        this.doorStatus = doorStatus;
    }
    
    public void open() {
        this.doorStatus = DoorStatus.OPENED;
    }
    
    public void close() {
        this.doorStatus = DoorStatus.CLOSED;
    }
}
 
package template;
 
public enum DoorStatus {
    CLOSED,
    OPENED
}
 
package template;
 
public enum MotorStatus {
    MOVING,
    STOPPED
}
 
package template;
 
public enum Direction {
    UP,
    DOWN
}
 
package template;
 
public class TrailMotor {
    private Door door;
    private MotorStatus mortorStatus;
    
    public TrailMotor(Door door, MotorStatus status) {
        this.door = door;
        this.mortorStatus = status; 
    }
 
    public Door getDoor() {
        return door;
    }
 
    public void setDoor(Door door) {
        this.door = door;
    }
 
    public MotorStatus getMortorStatus() {
        return mortorStatus;
    }
 
    public void setMortorStatus(MotorStatus mortorStatus) {
        this.mortorStatus = mortorStatus;
    }
    
    public void Move(Direction direction) {
        // 이미 모터가 동작중인 경우 움직이지 않는다.
        if (mortorStatus == MotorStatus.MOVING) {
            return;
        }
        
        // 문이 열려있는 경우 닫는다,
        if (door.getDoorStatus() == DoorStatus.OPENED) {
            door.close();
        }
 
        readyTrail();
        
        // 동작 시킨 후 모터상태 변경
        startMotor(direction);
        setMortorStatus(MotorStatus.MOVING);
    }
    
    public void startMotor(Direction direction) {
        
    }
    
}
cs



위와 같은 코드에는 어떤 문제가 있을까?

[문제]
만약 TrailMortor가 아니 자동차 모터를 동작해야 한다고 가정했을 때,
자동차 모터는 트레일 모터와 다른 부분이 존재하기 때문에 트레일 모터와 동일한 클래스에서 
일부가 추가/수정된 클래스를 추가로 또 만들어야 할 것이다.

만약 이 중복되는 코드들을 없애기 위해서
상속을 사용한다면 어떨까?





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
89
90
91
92
93
94
95
96
97
98
99
100
package template;
 
public abstract class Motor {
    
    protected Door door;
    protected MotorStatus mortorStatus;
    
    public Motor(Door door) {
        this.door = door;
        this.mortorStatus = MotorStatus.STOPPED; 
    }
 
    public Door getDoor() {
        return door;
    }
 
    public void setDoor(Door door) {
        this.door = door;
    }
 
    public MotorStatus getMortorStatus() {
        return mortorStatus;
    }
 
    public void setMortorStatus(MotorStatus mortorStatus) {
        this.mortorStatus = mortorStatus;
    }
    
    public abstract void Move(Direction direction);
 
    public abstract void startMotor(Direction direction);
 
}
 
package template;
 
public class TrailMotor extends Motor {
    
    public TrailMotor(Door door) {
        super(door);
    }
    
    public void Move(Direction direction) {
        // 이미 모터가 동작중인 경우 움직이지 않는다.
        if (mortorStatus == MotorStatus.MOVING) {
            return;
        }
        
        // 문이 열려있는 경우 닫는다,
        if (door.getDoorStatus() == DoorStatus.OPENED) {
            door.close();
        }
 
        // 트레일 운전 시 특정 메서드 호출
        readyTrail();
        
        // 동작 시킨 후 모터상태 변경
        startMotor(direction);
        setMortorStatus(MotorStatus.MOVING);
    }
    
    public void startMotor(Direction direction) {
        
    }
 
    public void readyTrail() {}
    
}
 
package template;
 
public class CarMotor extends Motor {
    public CarMotor(Door door) {
        super(door);
    }
    
    public void Move(Direction direction) {
        // 이미 모터가 동작중인 경우 움직이지 않는다.
        if (mortorStatus == MotorStatus.MOVING) {
            return;
        }
        
        // 문이 열려있는 경우 닫는다,
        if (door.getDoorStatus() == DoorStatus.OPENED) {
            door.close();
        }
 
        // 트레일 운전 시 특정 메서드 호출
        readyCar();
        
        // 도
        startMotor(direction);
        setMortorStatus(MotorStatus.MOVING);
    }
    
    public void startMotor(Direction direction) {
        
    }
    
}
cs




위와같이 하면 어느정도 중복되는 코드를 제거할 수는 있으나,
Move, startMotor 메서드 부분에서 상당한 부분이 중복되는 것은 여전하다.

그래서 더욱 줄이기 위해서 템플릿 메서드 패턴을 적용해 보자.

템플릿 메서드 패턴

템플릿 메서드 패턴은 전체적으로 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화할 때 유용하다.

위의 코드를 템플릿 메서드 패턴을 적용할 경우

move 메소드를 공통 부분을 모두 모아놓아서 템플릿 메서드로 사용하고
트레일, 카 모터 등등에서 개별적인 움직이이 필요한 부분인 startMotor에서는 오버라이드하여 사용한다.

startMotor 메서드를 primitive 또는 hook 메서드라고 한다.



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
89
90
91
92
93
94
95
96
97
98
99
100
101
package template;
 
public abstract class Motor {
    
    protected Door door;
    protected MotorStatus mortorStatus;
    
    public Motor(Door door) {
        this.door = door;
        this.mortorStatus = MotorStatus.STOPPED; 
    }
 
    public Door getDoor() {
        return door;
    }
 
    public void setDoor(Door door) {
        this.door = door;
    }
 
    public MotorStatus getMortorStatus() {
        return mortorStatus;
    }
 
    public void setMortorStatus(MotorStatus mortorStatus) {
        this.mortorStatus = mortorStatus;
    }
    
    public abstract void Move(Direction direction);
 
    public abstract void startMotor(Direction direction);
 
}
 
package template;
 
public class TrailMotor extends Motor {
    
    public TrailMotor(Door door) {
        super(door);
    }
    
    public void Move(Direction direction) {
        // 이미 모터가 동작중인 경우 움직이지 않는다.
        if (mortorStatus == MotorStatus.MOVING) {
            return;
        }
        
        // 문이 열려있는 경우 닫는다,
        if (door.getDoorStatus() == DoorStatus.OPENED) {
            door.close();
        }
 
        // 동작 시킨 후 모터상태 변경
        setMortorStatus(MotorStatus.MOVING);
 
        // hook 메서드
        startMotor(direction);
    }
    
    public void startMotor(Direction direction) {
        // 트레일 운전 시 특정 메서드 호출
        readyTrail();
        
    }
 
    public void readyTrail() {}
    
}
 
package template;
 
public class CarMotor extends Motor {
    public CarMotor(Door door) {
        super(door);
    }
    
    public void Move(Direction direction) {
        // 이미 모터가 동작중인 경우 움직이지 않는다.
        if (mortorStatus == MotorStatus.MOVING) {
            return;
        }
        
        // 문이 열려있는 경우 닫는다,
        if (door.getDoorStatus() == DoorStatus.OPENED) {
            door.close();
        }
 
        setMortorStatus(MotorStatus.MOVING);
        //hook 메서드
        startMotor(direction);
    }
    
    public void startMotor(Direction direction) {
        // 트레일 운전 시 특정 메서드 호출
        readyCar();
        
    }
    
}
 
cs


반응형