템플릿 메소드 패턴 (template method pattern)

2020. 3. 13. 14:39자바 Java/자바 공부 java study

반응형

 

템플릿 메서드 패턴이란
어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴
즉, 전체적으로는 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화 할 때 유용하다.

(동작 상의 알고리즘의 프로그램 뼈대를 정의하는 행위 디자인 패턴으로써, 알고리즘의 구조를 변경하지 않고 알고리즘의 특정단계를 다시 정의할 수 있게 해주는 패턴방식. 공통되는 부분은 추상클래스로 정의된 상위 클래스에서 구현을 하고, 재정의가 필요한 부분은 추상메소드로 선언한다.) 
다른 관점에서 보면 동일한 기능을 상위 클래스에서 정의하면서 확장/변화가 필요한 부분만 서브 클래스에서 구현할 수 있도록 한다.
예를 들어, 전체적인 알고리즘은 상위 클래스에서 구현하면서 다른 부분은 하위 클래스에서 구현할 수 있도록 함으로써 전체적인 알고리즘 코드를 재사용하는 데 유용하도록 한다.
‘행위(Behavioral) 패턴’의 하나 

 

 

 

 

예로 살펴보자

 

 

package d01;

public class Piano {
	public void learn() {
		System.out.println("말린님이 입장하였습니다.");
		System.out.println("피아노 공부를 시작합니다. ");
		System.out.println("연습을 하고 있습니다.");
		System.out.println("열심히 합니다!!!!!!");
		System.out.println("말린님이 퇴장하였습니다.");
	}
}
​
package d01;

public class Computer {
	
	public void learn() {
		System.out.println("니모님이 입장하였습니다.");
		System.out.println("자바 공부를 시작합니다. ");
		System.out.println("실습을 하고 있습니다.");
		System.out.println("열심히 합니다!!!!!!");
		System.out.println("니모님이 퇴장하였습니다.");
	}
}
package d01;

public class English {

	public void learn() {
		System.out.println("도리님이 입장하였습니다.");
		System.out.println("영어 공부를 시작합니다. ");
		System.out.println("자습을 하고 있습니다.");
		System.out.println("열심히 합니다!!!!!!");
		System.out.println("도리님이 퇴장하였습니다.");
	}
}

 

 

package d01;

public class Test {

	public static void main(String[] args) {

		Computer computer = new Computer();
		English english = new English();
		Piano piano = new Piano();

		System.out.println("====================== ");
		computer.learn();
		System.out.println("====================== ");
		english.learn();
		System.out.println("====================== ");
		piano.learn();
		System.out.println("====================== ");

	}

}

 

 

====================== 

니모님이 입장하였습니다.

자바 공부를 시작합니다. 

실습을 하고 있습니다.

열심히 합니다!!!!!!

니모님이 퇴장하였습니다.

====================== 

도리님이 입장하였습니다.

영어 공부를 시작합니다. 

자습을 하고 있습니다.

열심히 합니다!!!!!!

도리님이 퇴장하였습니다.

====================== 

말린님이 입장하였습니다.

피아노 공부를 시작합니다. 

연습을 하고 있습니다.

열심히 합니다!!!!!!

말린님이 퇴장하였습니다.

====================== 

 

라는 결과가 도출 된다. 공통되는 부분이 보인다 각 클래스의 두번째 줄은 앞의 공부 종류만 다를 뿐 '00 공부를 시작합니다' 라는 같은 뼈대를 가지고 있고 세번째 줄은 ''00을 하고 있습니다' 라는 뼈대가 같다. 

 

코드를 템플릿 메서드 패턴을 사용 하여 간결하고 기능적으로 효율적으로 만들수 있다 

 

 

인터페이스를 이용하여 같은 동작을 하는 메소드를 묶어준다. 

각 메소드 들도 다시 클래스로 정의해준다. 

 

package d03;

public interface StudySubject {
	public void Study();
}
package d03;

public class PianoStudy implements StudySubject {
	public void Study() {
		System.out.println("피아노 공부를 시작합니다. ");
	}
}
package d03;

public class JavaStudy implements StudySubject {
	public void Study() {
		System.out.println("자바 공부를 시작합니다. ");
	}
}
package d03;

public class EngStudy implements StudySubject {
	public void Study() {
		System.out.println("영어 공부를 시작합니다. ");
	}
}

 

 

 

 

 

 

 

 

 

package d03;

public interface StudyWhat {
	public void What();
}
package d03;

public class Practice implements StudyWhat {
	public void What() {
		System.out.println("연습을 하고 있습니다.");
	}
}
package d03;

public class Self implements StudyWhat {
	public void What() {
		System.out.println("자습을 하고 있습니다.");
	}
}
package d03;

public class Using implements StudyWhat {
	public void What() {
		System.out.println("실습을 하고 있습니다.");
	}
}

 

 

 Academy 객체를 만들고 상속을 이용하여 공통의 멤버 변수와 메소드 들을 정리해준다. 

 

package d03;

public class Academy {

	protected String name;
	protected StudyWhat what;
	protected StudySubject subject;

	public void Study() {
		subject.Study();
	}

	public void what() {
		what.What();
	}

	public void how() {}

	public void learn() {
		System.out.println(name + "이 입장하였습니다.");
		Study();
		what();
		how();
		System.out.println(name + "이 퇴장하였습니다.");
	}

}

 

이렇게 반복되는 것을 인터페이스와 상속을 통해 정리 하면 

 

각 자식클래스들은 다음과 같이 간결해질 수 있다 

 

package d03;

public class Computer extends Academy {

	public Computer() {
		name = "백지영";
		subject = new JavaStudy();
		what = new Using();
	}

	public void how() {
		System.out.println("열심히 합니다!!!!!!");
	}

}
package d03;

public class English extends Academy {

	public English() {
		name = "서현석";
		subject = new EngStudy();
		what = new Self();
	}

	public void how() {
		System.out.println("열심히 합니다!!!!!!");
	}

}
package d03;

public class Piano extends Academy {

	public Piano() {
		name = "내사랑";
		subject = new PianoStudy();
		what = new Practice();
	}

	public void how() {
		System.out.println("열심히 합니다!!!!!!");
	}

}

 

 

 

 

  연결 관계를 그려보면 다음과 같다. 

 

 

 

 

 

 

 

핵심정리

  • 템플릿 메소드에서는 알고리즘의 단계들을 정의하는데 일부 단계는 서브클래스에서 구현하도록 할 수 있습니다.
  • 템플릿 메소드 패던은 코드 재사용에 크게 도움이 됩니다.
  • 템플릿 메소드가 들어 있는 추상클래스에서는 구상메소드, 추상메소드, 후크를 정의할 수 있습니다.
  • 추상메소드는 서브클래스에서 구현합니다.
  • 후크(hook)는 추상 클래스에 들어 있는 아무일도 하지 않거나 기본 행동을 정의하는 메소드로 서브클래스에서 오버라이드할  수 있습니다.
  • 서브클래스에서 템플릿 메소드에 들어 있는 알고리즘을 함수보로 바꾸지 못하게 하고 싶다면 final 로 선언하면 됩니다.
  • 헐리우드 원칙에 의하면 저수준 모듈은 언제 어떻게 호출할지는 고수준 모듈에서 결정하는 것이 좋습니다.
  • 템플릿 메소드 패턴은 실전에서도 꽤 자주 쓰이지만 반드시 교과서적인 방식으로 적용되지는 않습니다.
  • 스트래티지 패턴과 템플릿 메소드 패던은 모두 알고리즘을 캡슐화 하는 패턴이지만 전자에서는 상속을 후자에서는 구성을 이용합니다.
  • 팩토리 메소드 패턴은 특화된 템플릿 메소드 패턴입니다.

 

디자인 원칙 

할리우드 원칙(Hollywood Principle) : 먼저 연락하지 마세요. 저희가 연락드리겠습니다.

 

할리우드 원칙을 활용하면 의존성 부패(dependency rot)를 방지 할 수 있습니다. 어떤 고수준 구성요소가 저수준 구성요소에 의존하고, 그 저수준의 구성요소는 다시 고수준의 구성요소에 의존하는 것과 같은 식으로 의존성이 복잡하게 꼬여 있는 것을 의존성 부패라고 부릅니다.

이렇게 의존성이 부패되면 시스템이 어떤식으로 디자인 된 것인지 알아 볼 수 없습니다.

할리 우드 원칙을 사용하면 저수준 구성요소에서 시스템에 접속 할 수는 있지만 언제 어떤식으로 그 구성요소들을 사용할 지는 고수준의 구성요소에서 결정하게 됩니다.

즉, 고수준의 구성요소에서 "먼저 연락하지 마세요. 저희가 연락드리겠습니다" 라고 이야기 하는 것과 같습니다.

 

 

반응형