틀이나 견본을 의미하는 템플릿을 붙인 템플린 메서드는 추상 메서드나 구현된 메서드를 활용하여 전체의 흐름(시나리오)를 정의 해놓은 메서드를 뜻합니다. final로 선언하여 하위 클래스에서 재정의 될 수 없게 하고 그렇기에 전체의 흐름이 정의되있다고 얘기할 수 있는 것입니다.
템플릿 메서드는 Java에서만 사용되는 개념이아닌 디자인 패턴의 일종으로 프레임워크에서 많이 사용되는 페턴입니다. 추상 클래스로 선언된 상위클래스에서 추상메서드를 이용하여 전체적인 흐름을 정의하고 하위 클래스에서 각 메서드에 대한 구체적인 구현을 하도록 위임하는 방식입니다.
왼쪽 클래스 관계도에서 볼수 있듯이 Car라는 상위 클래스를 추상클래스로 정의하여 차의 공동적인 기능들인 startCar(), tunOff(), run()을 이미 구현해 놓았고 그외에 하위 클래스인 AICar와 ManualCar에서 개별적으로 구현해야 할 drive(), stop() 메서드를 추상메서드로 두었습니다.
메서들 중에 run()메서드는 전체적으로 차의 움직임에 흐름을 조직하는 기능을 하는 메서드로 각 메서드들의 활용하는 순서가 정의되어 있고 이런 기능을 하는 메서드를 템플릿 메서드라고 합니다.
startCar();나 turnOff()같이 상위클래스에서 구현된 메서드들도 하위클래스에서 재정의 될 수 있습니다.
public abstract class Car {
//1번
public abstract void drive();
public abstract void stop();
//2번
public void startCar() {
System.out.println("시동을 켭니다.");
}
public void turnoff() {
System.out.println("시동을 끕니다.");
}
//3번
final public void run() {
startCar();
drive();
stop();
turnoff();
} //템플릿메서드
}
1번 : Car클래스를 생성하고 추상메서드인 drive()와 stop()을 작성하고 abstract를 각메서드와 클래스의 선언해줍니다.
2번 : 상위 클래스에서 구현되어야할 stratCar()와 turnOff() 클래스를 작성합니다.
3번 : 메서드들의 전체적인 흐름을 조직하는 run() 메서드를 작성하고 각 메서드들을 순서에 맞게 입력합니다. 이렇게 전체적인 흐름을 구성하는 메서드들 템플릿 메서드라고 하고 하위 클래스에서 재정의 할 수 없게 앞에 final을 붙여 줍니다.
public class AICar extends Car {
@Override
public void drive() {
System.out.println("자율 주행합니다");
System.out.println("자동차가 스스로 방향을 바꿉니다.");
}
@Override
public void stop() {
System.out.println("스스로 멈춥니다");
}
}
AICar 클래스를 만들고 하위클래스에서 구체화 되어야 할 drive()와 stop()을 작성합니다.
public class MannualCar extends Car {
@Override
public void drive() {
System.out.println("사람이 운전합니다.");
System.out.println("사람이 핸들을 조작합니다.");
}
@Override
public void stop() {
System.out.println("브레이크를 밟아서 정지합니다.");
}
}
MannualCar 클래스를 작성하고 drive()와 stop()을 작성합니다.
public class CarTest {
public static void main(String[] args) {
Car aiCar = new AICar();
aiCar.run();
System.out.println("=======================");
Car mannualCar = new MannualCar();
mannualCar .run();
}
}
///결과
시동을 켭니다.
자율 주행합니다
자동차가 스스로 방향을 바꿉니다.
스스로 멈춥니다
시동을 끕니다.
=======================
시동을 켭니다.
사람이 운전합니다.
사람이 핸들을 조작합니다.
브레이크를 밟아서 정지합니다.
시동을 끕니다.
CarTest클래스를 만들고 AICar와 MannalCar 인스턴스를 상위클래스인 Car를 타입으로 하여 생성하고 run()메서드를 각각 사용하면 아래와 같은 결과물이 출력됩니다.
이처럼 전체적인 흐름을 정해져 있지만 각 하위 클래스에 따라 달라져야 할 구체적인 내용을 추상메서드로 작성하여 구성하는 것을 템플릿 메서드라고 합니다.
//1번
public void washCar() {};
//2번
final public void run() {
startCar();
drive();
stop();
turnoff();
washCar();
}
1번의 washCar()와 같이 구현부가 있지만 내용이 비어있는 메서드를 훅 메서드라고 합니다. 추상 메서드와는 다르게 에러가 나지 않고 하위 클래스에서 재정의 하지 않더라도 에러가 나지 않습니다.
이런 훅메서드는 하위클래스에서 선택적인 오버라이딩을 할때 사용됩니다. 2번과 같이 washCar();을 run에 포함시키더라도 CarTest에 실행 결과에는 변한이 없습니다.
//AICar 클래스
@Override
public void washCar() {
System.out.println("자동 세차 합니다.");
}
시동을 켭니다.
자율 주행합니다
자동차가 스스로 방향을 바꿉니다.
스스로 멈춥니다
시동을 끕니다.
자동 세차 합니다.
AICar 클래스에 washCar 메서드를 재정의하고 CarTest 클래스에서 출력을 해보면 자동세차합니다가 추가된것을 알 수 있습니다.
이렇듯 훅메서드는 추상메서드와 같이 상위 클래스에서 정의되고 하위클래스에서 구현하는 역할을 하지만 훅메서드는 재정의를 하지 않아도 오류가 발생하거나 abstract를 선언해줄 필요가 없습니다.
프로젝트 구현시 시 여러 파일에서 공유해야 하는 상수 값은 하나의 클래스에 선언하여 사용하면 편리합니다.
위의 예시 처럼 Define 이란 클래스에 public static final 을 선언하고 상수값을 입력하면 다른 클래스들에서 생성자를 생하지 않고 클래스명.상수명(Define.MIN,MAX..등)을 통해서 언제든지 상수를 불러와서 사용할 수 있게됩니다.