본문 바로가기

Dev.../패턴자료

The Template Pattern

The Template Pattern

    하나 또는 그 이상의 메소드들을 파생된 클래스에 의해 구현하도록 남기는 부모 클래스를 작성할 때 마다  기본적으로 Template 패턴을 사용한다. Templeate 패턴은 클래스에서 알고리즘을 정의하는 것에 대한 생각을 형식화하지만 서브클래스에서 남겨진 것들에 대해 자세한 구현이 이루어 진다. 바꿔서 말하면 디자인 패턴에서 종종 발생하는 것처럼 base 클래스가 추상 클래스이면 Template 패턴의 간단한 형태를 이용한다.

Motivation

    Template들은 기초적인 것이어서 아마 그것에 관한 생각조차 없이 수 십 번도 더 사용했을 것이다. Template 패턴 이면의 생각은 알고리즘의 몇몇 부분이 잘 정의되고 base 클래스에서 구현될 수 있고 반면에 다른 부분은 파생된 클래스들에 남겨져 구현될 수 있다는 것이다. 또 다른 생각은 여러개의 서브클래스에서 반복되지 않도록 base 클래스에서 만들 수 있는 부분이 있다는 것에 대한 인식이다.

    예를 들어 Strategy 패턴의 예에서 사용했던 PlotPanel 클래스들의 개발에서 우리는 선 그래프나 막대 그래프 모두 데이터를 스케일하는 것과 x 축과 y축을 계산하는 부분이 발견되었다. 
public class PlotPanel extends JPanel {	float xfactor, yfactor;	int xpmin, ypmin, xpmax, ypmax;	float minX, maxX, minY, maxY;	float x[], y[];	Color color;		//--------------------------------------------	public void setBounds(float minx, float miny, float maxx, float maxy) {		minX=minx;		maxX= maxx;		minY=miny;		maxY = maxy;	}                                           		//--------------------------------------------	public void plot(float[] xp, float[] yp, Color c) {		x = xp;      //copy in the arrays		y = yp;		color = c;   //and color				//compute bounds and sclaing factors		int w = getWidth() - getInsets().left - getInsets().right;		int h = getHeight() - getInsets().top - getInsets().bottom;		  		xfactor = (0.9f * w) / (maxX - minX);		yfactor = (0.9f * h)/ (maxY - minY);				xpmin = (int)(0.05f * w);		ypmin = (int)(0.05f * h);		xpmax = w - xpmin;		ypmax = h - ypmin;		repaint();      //this causes the actual plot	}
그러므로 이러한 메소들은 실제적인 plotting을 할 수 있는 능력 없이 모두 PlotPanel 크래스에 속해있는다. plot 메소드가 상수들을 스케일링하고 repaint() 메소드를 호출하는 걸 주목해 보자. 실제 paint 메소드는 파생된 클래스 까지 연기되어 있다. Jpanel 클래스가 항상 paint 메소드를 가지고 있기 때문에 우리는 base 클래스에서 추상 메소드로 선언하는 걸 원하지 않지만, 파생된 클래스에서 재정의할 필요가 있다.

Kinds of Methods in a Template Class

Template는 파생된 클래스에서 이용할 수 있는 네 가지의 메소드를 가지고 있다 :
  1. 서브클래스들이 사용하고자 하는 모든 기본적인 기능을 수행하는 메소드를 완성한다. 이런 것들은 Concrete methods 라 부른다.
     
  2. 전혀 채워 지지 않은 메소들은 파생 클래스에서 구현해야만 하나. 자바에서는 abstract 메소드로 선언한다.
     
  3. 몇몇 연산에 대하여 기본적인 구현을 포함하는 메소드는 파생 클래스에서 재정의 될 수 있다. 이런 것들을 Hook 메소드라 부른다. 물론 이것은 임의적으로 만들 수 있다.
     
  4. 마지막으로 Template 클래스는 어떤 추상이나 hook나 구체적인 메소드의 조합으로 그들 자체를 부를 수 있는 메소드를 포함할 수 있다.

Sample Code

    스크린 상에 삼각형들을 그리는 프로그램을 생각해 보자. 우리는 추상 Triangle 클래스를 가지고 시작할 것이고 그다음 몇몇 특별한 삼각형을 파생시킬 것이다.

   

우리의 추상 Triangle 클래스는 Template 패턴으로 설명된다:
public abstract class Triangle {	Point p1, p2, p3; 		//---------------------------------------	public Triangle(Point a, Point b, Point c) {		//save		p1 = a; p2 = b; p3 = c;	}		//---------------------------------------	public void draw(Graphics g) {		//This routine draws a general triangle		drawLine(g, p1, p2);		Point current = draw2ndLine(g, p2, p3);		closeTriangle(g, current);	}		//---------------------------------------	public void drawLine(Graphics g, Point a, Point b) {		g.drawLine(a.x, a.y, b.x, b.y);	}	//---------------------------------------	//this routine is the "Hook" that has to be implemented	//for each triangle type.	abstract public Point draw2ndLine(Graphics g, Point a, Point b);	//---------------------------------------	public void closeTriangle(Graphics g, Point c) {		//draw back to first point		g.drawLine(c.x, c.y, p1.x, p1.y);	}}
이 Triangle 클래스는 3개의 선의 좌표를 저장하지만, draw 루틴은 단지 첫째와 마지막 선만 그린다. draw2ndLine 메소드는 추상 메소드로 남겨진다. 파생된 클래스는 그리고자 하는 사각형의 종류를 생성하는 세 번째 점으로 이동할 수 있도록 한다.

    이것은 Template 패턴을 이용하는 클래스의 일반적인 예이다. draw 메소드는 두 개의 구체적인 base 클래스 메소드를 호출하고 하나의 추상 메소드를 Triangle로부터 상속 받은 구체적인 클래스에서 구체적으로 재정의 해야 한다.

Drawing a Standard Triangle

    모양에 제한 없이 일반적인 삼각형을 그리기 위해 우리는 간단히 draw2ndLine 메소드를 파생된 stdTrinalgle 클래스에서 구현하였다.   
public class stdTriangle extends Triangle {	public stdTriangle(Point a, Point b, Point c) {		super(a, b, c);	}		public Point draw2ndLine(Graphics g, Point a, Point b) {		g.drawLine(a.x, a.y, b.x, b.y);		return  b;	}}
Drawing an Isoceles Triangle  

    이 클래스는 세 번째 데이터 포인트를 두 변의 길이가 같도록 새로 계산하고 새로운 점을 저장한다.
public class IsocelesTriangle extends Triangle {	Point newc;	int newcx, newcy;	int incr;		public IsocelesTriangle(Point a, Point b, Point c)    {		super(a, b, c);		double dx1 = b.x - a.x;		double dy1 = b.y - a.y;		double dx2 = c.x - b.x;		double dy2 = c.y - b.y;				double side1 = calcSide(dx1, dy1);		double side2 = calcSide(dx2, dy2);				if (side2 < side1) 		incr = -1;		else		incr = 1;				double slope = dy2 / dx2;		double intercept = c.y - slope* c.x;				//move point c so that this is an isoceles triangle		newcx = c.x; newcy = c.y;				while(Math.abs(side1 - side2) > 1) {			newcx += incr;    //iterate a pixel at a time until close			newcy = (int)(slope* newcx + intercept);			dx2 = newcx - b.x;			dy2 = newcy - b.y;			side2 = calcSide(dx2, dy2);		}		newc = new Point(newcx, newcy);	}		//--------------------------------------	//calculate length of side	private double calcSide(double dx, double dy) {		return Math.sqrt(dx*dx + dy*dy);	}
Triangle 클래스가 draw 클래스를 호출했을 때 새로운 버젼의 draw2ndLine 메소드를 부르고 새로운 세 번째 점에 선을 그린다. 게다가 삼각형이 정확하게 연결될 수 있도록 새로운 점을 draw 메소드에 반환한다.
	//draws 2nd line using saved new point	public Point draw2ndLine(Graphics g, Point b, Point c)    {		g.drawLine(b.x, b.y, newc.x, newc.y);		return newc;	}

The Triangle Drawing Program

    메인 프로그램은 간단하게 그리고자하는 삼각형의 인스턴스를 생성한다. 그리고 나서, TPanel의 벡터에 그 인스턴스들을 추가한다.
public class TriangleDrawing extends JxFrame {	stdTriangle t, t1;	IsocelesTriangle it;		public TriangleDrawing() {		super("Draw triangles");		TPanel tp = new TPanel();		t = new stdTriangle(new Point(10,10), new Point(150,50), new Point(100, 75));		it = new IsocelesTriangle(new Point(150,100), new Point(240,40), new Point(175, 150));		t1 = new stdTriangle(new Point(150,100), new Point(240,40), new Point(175, 150));		tp.addTriangle(t);		tp.addTriangle(it);		 		getContentPane().add(tp);		setSize(300, 200);		setBackground(Color.white);		setVisible(true);	}		//=======================================	public void paint(Graphics g) {		for (int i = 0; i < triangles.size(); i++) {			Triangle tngl = (Triangle)triangles.elementAt(i);			tngl.draw(g);		}	}					
아래 그림에서 왼쪽은 표준적인 삼각형들을 보여주고 오른쪽은 isoceles 삼각형을 보여주고 있다.

            

Summary and Consequences

    Template 패턴들은 객체지향 소프트웨어에서 상상 발생하고 복잡하거나 분명하지 않는 것을 의도하지 않는다. 이 패턴들은 객체지향 프로그램의 정규적인 부분이고 그것들이 실제적으로 하는 것 보다 더 추상적으로 만들려 할 필요가 없다.

    가장 의미 있는 점은 base 클래스가 메소드들 중 사용될 것은 정의하고 파생 클래스에서 구현할 것은 남겨 둔 다는 것이다. 두 번째는 메소드들의 시퀀스를 부를 수 있는 메소드가 base 클래스에 있을 수 있다는 것이다. Template 메소드는 base 클래스에서 자세한 구현이 완벽하게 이루어지지 않았더라도 일반적인 알고리즘을 정의한다.

    Template 클래스들은 종종 몇 개의 추상 메소드를 가질 수 있고 이 메소드들은 파생된 클래스에서 재정의해야만 한다.

'Dev... > 패턴자료' 카테고리의 다른 글

The Interpreter Pattern  (0) 2005.03.01
The Strategy Pattern  (0) 2005.03.01
The Mediator Pattern  (0) 2005.03.01
The Chain of Responsibility Pattern  (0) 2005.03.01
The Observer Pattern  (0) 2005.03.01