Posts [개발자 블로그] 템플릿 메서드 패턴(Template Method Pattern)
Post
Cancel

[개발자 블로그] 템플릿 메서드 패턴(Template Method Pattern)

템플릿메서드패턴(Template Method Pattern)

  • 전체적으로 동일하면서 부분적으로 상이한 문장을 가지는 메소드의 코드 중복을 최소화할 때 유용
  • 템플릿 메소드 패턴은 전체적인 알고리즘을 구현하면서 상이한 부분은 하위 클래스에서구현할 수 있도록 해 주는 디자인 패턴으로서 전체적인 알고리즘의 코드를 재사용하는 데 유용하다.
  • 전체적인 틀은 상위클래스에게 상속받고 변경되는 부분만 하위클래스에서 오버라이딩 하는 패턴
  • 변화되는 부분만 추상 메소드로 만들어준다

엘리베이터 제어 프로그램 Example

  • 엘리베이터 제어 시스템에서 모터를 구동시키는 기능
    • HyundaiMotor 클래스: 모터를 제어하여 엘리베이터를 이동시키는 클래스
    • Door 클래스: 문을 열거나 닫는 기능을 제공하는 클래스
      image
      image
  • 소스 코드
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);
	}
}

image

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);
	}
}

스크린샷 2021-03-16 오후 12 55 30

  • HyundaiMotor클래스와 LGMotor클래스의 move()메서드에서 코드 중복 발생!!!

해결책

  • move 메서드에서 공통적인 부분을 상위 클래스 Motor로 이동
    image
    스크린샷 2021-03-16 오후 12 59 43
    스크린샷 2021-03-16 오후 1 00 54

템플릿 메서드의 개념

스크린샷 2021-03-16 오후 1 04 23

템플릿 메서드 패턴의 일반적인 형태

image

템플릿 메서드 패턴의 순차 다이어그램

image

템플릿 메서드 패턴을 위에 적용한 클래스 다이어그램

image

출처

  • Java객체지향 디자인패턴(한빛미디어)
This post is licensed under CC BY 4.0 by the author.

[Backend] 동기&비동기&block&non-block

[개발자 블로그] 커맨드 패턴(Command Pattern)