프로그래밍/JAVA

디자인 패턴 - 템플릿 메소드 패턴(Template Method Pattern)

코딩딩 2016. 4. 3. 10:07

디자인패턴이란?

소프트웨어 설계에서 얻은 세세한 경험들에서 얻어낸 패턴을 정의해 놓은 것, 다시말해 시스템을 만들기 위해서 전체 중 일부 의미 있는 클래스들을 묶은 각각의 집합을 말한다. 이러한 반복되는 의미있는 집합을 정의하고 이름을 지정해서, 누가 이야기하더라도 동일한 의미의 패턴이 되도록 만들어 놓은 것이다.

Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides 라는  [Design Patterns : Elements of Reusable Object-Oriented Software]라는 책에서 최초로 객체 지향에 맞는 디자인 패턴을 정리해 놓았다. 4명의 아저씨가 만들어서 Gof (Gangs of  Four)로 부른다.

 

템플릿 메소드 패턴을 이해하기 위한 예제!

우리가 샌드위치 프로그램을 만들려고 하는데 메뉴는 참치 샌드위치 와 햄 샌드위치 두가지 이고, 만드는 방법은 아래 그림과 같다고 해보자.

이러한 프로그램을 작성하려고 할때 가장 단순한 방법은 각각의 클래스를 만들어서 각자 메소드를 실행해주는것이다.

 

참치 샌드위치 클래스

public class Tuna {
	
	public void cook(){
		System.out.println("white bread 한장");
		System.out.println("양상추를 깐다.");
		System.out.println("참치를 넣는다.");
		System.out.println("토마토를 넣는다.");
		System.out.println("white bread 한장");
	}
}
 
햄 샌드위치 클래스
public class Ham {
	
	public void cook(){
		System.out.println("brown bread 한장");
		System.out.println("양상추를 깐다.");
		System.out.println("햄을 넣는다.");
		System.out.println("토마토를 넣는다.");
		System.out.println("brown bread 한장");
	}
}
 

 

실행 함수
public class Test {
	public static void main(String[] args) {
		
		Tuna tuna = new Tuna();
		Ham ham = new Ham();
		
		tuna.cook();
		System.out.println("--------------------------------");
		ham.cook();
	}
}

 


실행결과

white bread 한장

양상추를 깐다.

참치를 넣는다.

토마토를 넣는다.

white bread 한장

--------------------------------

brown bread 한장

양상추를 깐다.

햄을 넣는다.

토마토를 넣는다.

brown bread 한장

 

그런데, Tuna 클래스나 Ham클래스나 동일하게 cook메소드를 가지고 있다.

그래서 Sandwich 라는 부모클래스를 만들어서 공통된 기능을 만들어 놓으면 Tuna클래스나 Ham클래스에서 물려받아(상속) 사용하면 편할 것이다.

그런데 자세히 살펴보니 Tuna클래스와 Ham클래스의 cook 메소드가 완전히 같지 않다.

tuna 샌드위치든 ham 샌드위치든 어쨌든 요리, 즉 cook 메소드를 실행해야 하지만 그 세부 내용이 약간씩 다르다는 것이다.

이럴때 우리는 추상 클래스 abstract class를 사용할 수 있다. 부모클래스(super class)에서 구조만 잡아주고 자식 클래스에서 세부사항을 결정하여 사용하는 방법이다.

그러므로 부모클래스인 sandwich 클래스를 추상 클래스로 만들어주고 cook 메소드를 추상 메소드로 만들어 준 다음 Tuna, Ham 클래스에서 각각 메소드재정의(메소드 오버라이딩)을 해주면된다.

 

public abstract class Sandwich {
	public abstract void cook();

}

public class Ham extends Sandwich {
	
	public void cook(){
		System.out.println("brown bread 한장");
		System.out.println("양상추를 깐다.");
		System.out.println("햄을 넣는다.");
		System.out.println("토마토를 넣는다.");
		System.out.println("brown bread 한장");
	}
}

public class Tuna extends Sandwich {

	public void cook(){
			System.out.println("white bread 한장");
			System.out.println("양상추를 깐다.");
			System.out.println("참치를 넣는다.");
			System.out.println("토마토를 넣는다.");
			System.out.println("white bread 한장");
	}
}

참치 샌드위치 만들기 함수를 보면 "White bread 한장" 명령이 두번 반복적으로 실행된다.

이 예제에서는 한줄의 문장이지만 실제로는 길고 복잡한 코딩이라고 가정한다면, 길고 복잡한 같은 코딩을 두번 반복해야한다.
이럴때는 함수로 정의해서 호출만 하면 더욱 편할것이다.

또한, "참치를 넣는다", "햄을 넣는다"는 언젠가 다시 사용될 가능성이 높다고 ( 새로운 메뉴추가시 ) 생각 되므로 메소드로 정의해 놓는다.위의 과정을 코딩하면 다음과 같다.

// 부모클래스
public abstract class Sandwich {
	
	public abstract void cook();

}

//자식클래스 : 햄

public class Ham extends Sandwich {
	
	
	public void cook(){
		bread();
		System.out.println("양상추를 깐다.");
		topping();
		System.out.println("토마토를 넣는다.");
		bread();
	}
	
	public void bread(){
		System.out.println("brown bread 한장");		
	}
	
	public void topping(){		
		System.out.println("햄을 넣는다.");
	}
}

//자식클래스 : 참치

public class Tuna extends Sandwich {
	
	public void cook(){
		bread();
		System.out.println("양상추를 깐다.");
		topping();
		System.out.println("토마토를 넣는다.");
		bread();
	}
	
	public void bread(){		
		System.out.println("white bread 한장");
	}		
	
	public void topping(){
		System.out.println("참치를 넣는다.");		
	}
}

 

다시 위의 코딩을 잘 살펴보면  Tuna 클래스와 Ham 클래스의 cook() 메소드가 똑같다는것을 확인 할수 있다.

즉, 부모클래스인 Sandwich 클래스에서 정의한 후 자식 클래스에서는 호출해서 사용할 수 있다는 뜻이다.

이렇게 하면, 동일한 기능을 하는  코드를 두번 작성할 필요가 없어진다. 

게다가 Ham 클래스와 Tuna 클래스의 bread 메소드와 topping 메소드는 내용이 완전히 같지는 않지만 비슷하므로 부모클래스에서 추상메소드로 정의해 규격을 정해주고 자식클래스에서 메소드 재정의를 해서 사용할 수 있다.

그 결과 코딩은 이렇게 될 것이다. 

/부모클래스

public abstract class Sandwich {
	
	public void cook(){
			bread();
			System.out.println("양상추를 깐다.");
			topping();
			System.out.println("토마토를 넣는다.");
			bread();
	}
	
	public abstract void bread();
	public abstract void topping();

}

//자식클래스 : 햄

public class Ham extends Sandwich {
	
	public void bread(){
		System.out.println("brown bread 한장");		
	}
	
	public void topping(){		
		System.out.println("햄을 넣는다.");
	}
}

//자식클래스 : 참치

public class Tuna extends Sandwich {	
	public void bread(){		
		System.out.println("white bread 한장");
	}		
	
	public void topping(){
		System.out.println("참치를 넣는다.");		
	}
}

 

 

이렇게 부모클래스에서 템플릿에 해당하는 메소드가 정의되어있고 메소드 내용중 세부적 기능( bread, topping)의 대한 정의는 추상메소드로 남겨놓은채 자식 클래스에서 세부 기능을 구현해서 템플릿을 완성하는 방법을  템플릿 패턴 메소드(Template Method Pattern)라고 한다.