Design pattern

[Design Pattern] Template Method Pattern

s00ng 2022. 8. 14. 20:43
Template Method Pattern 이란?

전체적인 알고리즘은 상위 클래스에서 구현하면 서 다른 부분은 하위 클래스에서 구현할 수 있도록 하는 디자인 패턴
전체적으로는 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화할 때 유용

템플릿 메서드 패턴 컬레보레이션

 

  • AbstractClass : 템플릿 메서드를 정의하는 클래스. 하위 클래스에 공통 알고리즘을 정의하고 하위 클래스에서 구현될 기능을 primitive 메서드 또는 hook 메서드로 정의하는 클래스
  • ConcreteClass : 물려받은 primitive 메서드나 hook 메서드를 구현하는 클래스. 상위 클래스에 구현된 템플릿 메서드의 일반적인 알고리즘에서 하위 클래스에 적합하게 primitive 메서드나 hook 메서드를 오버라이드 하는 클래스)
* 템플릿 메서드 : 동일한 기능을 구현하면서 각 하위 클래스에서 구체적으로 정의할 필요가 있는 부분
* primitive 메서드(= hook 메서드) : 템플릿 메서드에서 호출되면서 하위 클래스에서 오버라이드될 필요가 있는 부분

 

Templete 메서드 구현

교재 예시 : 엘레베이터 제어 시스템의 모터 구동 기능
  • HyundaiMotor 클래스는 move 메서드 실행 시 door클래스 상태 확인(열려있으면 닫기)
  • 엘리베이터가 이미 이동중일 경우 모터 구동 필요 없음
  • move method(구동 메서드) : 모터 상태 조회 → 동작중이면 종료, 아니라면 문 상태 조회 → 모터 구동

현대 모터를 구동시키는 HyndaiMotor 클래스 설계

 

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 {
	
    pirvate Door door;
    private MotorStatus motorStatus;
    
    public HyundaiMotor(Door door){
    	this.door = doorl
        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();

        moveHyndaiMotor(direction); // 모터를 주어진 방향으로 이동
        setMotorStatus(MotorStatus.MOVING); // 모터 상태를 이동중으로 변경
    }
}
public class Client {
	public static void main(String args[]){
    
    	Door door = new Door();
        HyndaiMotor hyndaiMotor = new HyndaiMotor(door);
        hyundaiMotor.move(Direction.UP);
    }
}

 

문제점

문제 상황 : 다른 회사의 모터를 제어해야 하는 경우
  • 모터 구동에 있어 다른 회사의 제품이기 때문에 완전히 동일하지는 않지만, 많은 부분 코드의 중복이 발생하는 문제
  • 상속을 통해 해결(중복되는 부분을 상위클래스로 옮김 + 다르게 동작하는 부분은 각각의 클래스가 구현하게 함) → 각각의 클래스가 구현한 함수(move)에 대해서도 여전히 중복되는 부분이 많음

 

문제 해결

중복되는 부분만 메소드로 상위 클래스에 두고, 다르게 동작하는 부분은 추상 메소드로 두어 구현하게 함
public abstract class Motor {
	private Door door;
    private MotorStatus motorStatus;
    
    public Motor(Door door){
    	this.door = door;
        motorStatus = MotorStatus.STOPPED;
    }
	
    public MotorStatus getMotorStatus(){
    	return motorStatus;
    }
	
    private void setMotorStatus(MotorStatus motorStatus){
    	this.motorStatus = motorStatus;
    }
    
    // LGMotor와 HyundaiMotor의 move 메서드에서 공통되는 부분만 가짐
    public void move(Direction direction){
    	MotorStatus motorStatus = getMotorStatus();
        if (motorStatus == MotorStatus.MOVING)
        	return;
            
       DoorStatus doorStatus = door.getDoorStatus();
       if (doorStatus == DoorStatus.OPENED)
       	door.close();
        
		moveMotor(direction);
        setMotorStatus(MotorStatus.MOVING);
    }
    protected abstract void moveMotor(Direction direction);
}
public class HyndaiMotor extends Motor {
	public HyundaiMotor(Door door){
    	super(door);
    }
	
    protected void moveMotor(Direction direction){
    	//HyundaiMotor 구동시킴
    }
}

public class LGMotor extends Motor {
	public LGMotor(Door door){
    	super(door);
    }
    
    protected void moveMotor(Direction direction){
    	//LGMotor 구동시킴
    }
}