디자인패턴_Template Pattern of Behavioral Pattern

2021. 1. 15. 11:202021/JOB DA STUDY

템플릿 메서드 패턴 이란,

어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴이다.

 

  • 전체적으로 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화할 때 유용
  • 동일한 기능은 상위 클래스에서 정의하면서, 확장/변화가 필요한 부분만 서브 클래스에서 구현할 수 있도록 한다.

  • AbstractClass

-템플릿 메서드를 정의하는 클래스

-하위 클래스에 공통 알고리즘을 정의하고, 하위 클래스에서 구현될 기능을 primitiveMethod 또는 hookMethod로 정의   하는 클래스

 

  • ConcreteClass

-물려받은 Primitive 메서드 또는 hook 메서드를 구현하는 클래스

-상위 클래스에서 구현된 템플릿 메서드의 일반적인 알고리즘을 하위 클래스에서 적합하게 primitive 메서드나 hook메서드를 오버라이드하는 클래스

 

 

여러 회사의 모터 지원하기

엘리베이터 제어 시스템에서 모터 구동시키는 기능

 

HyundaiMotor Class <------- move()메서드를 실행시 안전을 위해 Door가 닫혀 있는지 확인 -------> Door Class

 

Enumeration Interface

  • MotorStatus: Moving, Stopped
  • DoorStatus: Opened, Closed
  • MotorStatus: UP, DOWN

 

public enum DoorStatus { CLOSED, OPENED }
public enum MotorStatus { MOVING, STOPPED }
public class Door{
    private DoorStatus doorStatus;

    //문의 Default Value는 CLOSED
    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(){
        this.door = door;
        motorStatus = motorStatus.STOPED;
    }

    //엘리베이터 Motor 구동
    private void moveHyundaiMotor(Direction direction){
}

    //엘리베이터 Motor의 상태 get
    public MotorStatus getMotorStatus() { return motorStatus; }

    //엘리베이터 Motor의 상태 set
    private void setMotorStatus() { this.motorStatus = motorStatus; }

    public void move(Direction direction){
        //Motor가 이동중이면 아무 작업 하지 않음
        MotorStatus motorStatus = getMotorStatus();
        if(motorStatus == MotorStatus.MOVING) return;
        //Dooe가 열려있으면 닫음
        DoorStatus doorStatus = door.getDoorStatus();
        if(doorStatus == DoorStatus.OPENED) door.close();

        //Hyundai 모터를 주어진 방향으로 이동시킴
        moveHyundaiMotor(direction);
        //Motor의 상태를 Moving으로 변경
        setMotorStatus(MotorStatus.MOVING);
        }
    }
  • HynudaiMotor 클래스의 move 메서드는 getMotorStatus 메서드를 호출해 모터의 상태를 조회                          (Motor동작 중인 경우 -> move 메서드 실행 종료)
  • Door 클래스의 getDoorStatus 메서드를 호출해 문의 상태 조회                                                                   1. DoorStatus.OPENED -> DoorStatus.CLOSED 호출해 문 닫기                                                                   2. moveHyundaiMotor 메서드 호출 -> MotorStatus.MOVING                                                                     3. setMotorStatus를 호출해 모터의 상태를 MOVING으로 기록                                                                 
public class Client{
    public static void main(String[] args){
        Door door = new Door();
        HyundaiMotor hyundaiMotor = new HyundaiMotor(door);
        hyundaiMotor.move(Direction.UP);
    }
}

 

문제점

*** 다른 회사(LG)의 모터를 제어해야 하는 경우 ***

public class LGMotor{
    private Door door;
    private MotorStatus motorStatus;

    // STOPPED
    public LGMotor() {
        this.door = door;
        motorStatus = MotorStatus.STOPPED;
    }

    //MOVING
    private void moveLGMotor(Direction direction){
}

    public MotorStatus getMotorStatus() { return motorStatus; }
    private void setMotorStatus() { 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);
        setMotorStatus(MotorStatus.MOVING);
        }
    }
  • LGMotor 와 HyundaiMotor는 많은 중복 코드를 가진다.
  • 중복코드 -------------> 유지보수성을 악화

해결책

  • 2개 이상의 클래스가 유사한 기능을 제공하면서, 중복된 코드가 있는 경우 상속을 이용해 코드 중복의 문제 피할 수 있다.

*** HyundaiMotor 와 LGMotor의 공통적인 기능을 구현하는 클래스 ***
public abstract class Motor {
    //Door 객체는 자식 클래스에 따라 변경
    protected Door door;
    private MotorStatus motorStatus;
    
    public Motor(Door door){
        this.door = door;
        motorStatus = MotorStatus.STOPPED;
    }

    public MotorStatus getMotorStatus() {return MotorStatus; }
    //protected = setMotorStatus는 자식 클래스에 따라 변경
    protected void setMotorStatus(MotorStatus motorStatus) { this.motorStatus = motorStatus; }
}

*** Motor 클래스를 상속받아 HyundaiMotor 클래스 구현 ***
public class HyundaiMotor extends Motor{
    //Motor(Door door) 상속: STOPPED
    public HyundaiMotor(Door door) { super(door); }
    //MOVING
    private void moveHyundaiMotor(Direction direction){
    }

    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);
        }
}
*** Motor 클래스를 상속받아 LGMotor 클래스 구현 ***
public class LGMotor extends Motor{
    //Motor(Door door) 상속: STOPPED
    public LGMotor(Door door) { super(door); }
    //MOVING
    private void moveLGMotor(Direction direction){
    }
    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);
        setMotorStatus(MotorStatus.MOVING);
        }

}
  • Motor 클래스를 상위 클래스로 정의함으로써 중복코드 제거 가능
  • move 메서드에는 코드 중복 문제가 존재

 

  • move 메서드와 같이 부분적으로 중복되는 경우, 상속을 활용해 코드 중복을 피할 수 있다.

move Method에서 moveHyundaiMotor 메서드와 moveLGMotor 메서드를 호출하는 부분만 다르다. 
moveHyundaiMotor 메서드와 moveLGMotor 메서드는 기능면에서는 동일하다. 

                 ↓

1. move 메서드를 상위 Motor 클래스로 이동
2. moveHyundaiMotor 메서드와 moveLGMotor 메서드의 호출 부분을 하위 클래스에서 Override
public abstract class Motor {
    ********* 동일 *********

    public void move(Direction direction){
        MotorStatus motorStatus = getMotorStatus();
        if(motorStatus motorStatus == MotorStatus.MOVING) return;

        DoorStatus doorStatus = door.getDoorStatus();
        if(doorStatus == DoorStatus.OPENING) door.close();

        //HyundaiMotor 와 LGMotor에서 오버라이드
        moveMotor(direction);
        setMotorStatus(MotorStatus.MOVING);
    }
}
public class HyundaiMotor extends Motor{
    public HyundaiMotor(Door door) { super(door); }
    //@Override
    protected void moveMotor(Direction direction){
    }
}
public class LGMotor extends Motor{
    public LGMotor(Door door) { super(door); }
    
    //@Override
    protected void moveMotor(Direction direction){
    }
}
  • Motor클래스의 move메서드는 HyundaiMotor 와 LGMotor에서 동일한 기능을 구현하면서, 각 하위 클래스에서 구체적으로 정의할 필요가 있는 부분(moveMotor 메서드)만 각 하위 클래스에서 Override

 

'2021 > JOB DA STUDY' 카테고리의 다른 글

디자인패턴_Decorator Pattern of Structural Pattern  (0) 2021.01.19
디자인패턴_Observer Pattern of Behavioral Pattern  (0) 2021.01.15
Super  (0) 2021.01.15
디자인패턴_Command Pattern of Behavioral Pattern  (0) 2021.01.14
Interface  (0) 2021.01.14