Template method 패턴

Template method(템플릿 메소드) 디자인 패턴도 매우 유용한 패턴입니다. 기능은 같으나 구현방법이 다른 메소드들의 구조를 제시하고 기능과 구현방법이 같은 메소드는 대표로 하나만 제시되는 방법입니다.

 

예를 들어 도형을 그리는 것을 생각해보도록 하겠습니다. 도형에는 삼각형, 사각형, 원 등이 있습니다. 넓이와 높이(원의 경우 지름)을 통해 도형을 설정하는 것은 모두 똑같습니다. 그러나 구체적으로 모양은 다 다릅니다. 도형을 그릴 때 메모리라는 사상위에 비트를 역상시켜 해당 도형을 그리는 방법을 사용하는데 이는 어느 도형이나 이미지를 그리든 동일합니다.

 

따라서 도형의 모양을 만드는 기능은 개별 구현이 가능하도록 추상 메소드로 하고 그리는 부분은 모두 같으므로 하나만 구현하면 됩니다.

 

설명이 약간 어려울 수 있으니 코드를 보며 이해하도록 하겠습니다.

 

아래 코드는 shape 헤더입니다. 우선 도형을 만들기 위한 set_area, 와 그리기를 위한 get_gui_data를 추상 메소드로 잡았습니다. 그리고 shape_draw는 모든 도형에서 동일하므로 Template method로 만들었습니다.

/* shape.h */
typedef struct
{
	void (*set_area)(uint32_t width, uint32_t height);
	uint8_t *(*get_gui_data)(uint32_t *width, uint32_t *height);
}shape_t;

extern void shape_draw(uint32_t x, uint32_t y, shape_t *shape);
 

shape_draw 함수 구현부를 보도록 하겠습니다. 파라메터로 넘어온 shape의 get_gui_data를 이용해 그림을 그리는 코드 입니다. x, y는 그림이 그려질 첫 좌표입니다.

/* shape.c */
void shape_draw(uint32_t x, uint32_t y, shape_t *shape)
{
	uint32_t width, height;
	uint8_t *data = shape->get_gui_data(&width, &height);

	//need to implement drawing
}
 

그럼 하위 shape 인 사각형 먼저 볼까요? setup_shape_rectangle은 외부에서 호출 할 수 있는 extern 함수입니다. 아마 이 예제에선 main에서 호출되겠지요? set_area를 통해 메모리에 해당 도형을 그리고, get_gui_data를 통해 그려질 메모리를 포인터로 넘겨주게 될 것입니다.

/* shape_retangle.c */
static shape_t this = null;

shape_t *setup_shape_retangle(void)
{
	this.set_area = set_area;
	this.get_gui_data = get_gui_data;

	return &this;
}

static void set_area(uint32_t width, uint32_t height)
{
	//need to implement
}

static uint8_t *get_gui_data(uint32_t *width, uint32_t *height)
{
	//need to implement
}
 

다음은 원(circle) shape도 살펴보도록 하겠습니다. 위의 사각형과 거의 차이가 없지요? 그러나 원을 그리려면 구체적인 구현이 달라지겠지요? 이부분은 여러분들이 구현하면 되겠습니다.

/* shape_circle.c */
static shape_t this = null;

shape_t *setup_shape_circle(void)
{
	this.set_area = set_area;
	this.get_gui_data = get_gui_data;

	return &this;
}

static void set_area(uint32_t width, uint32_t height)
{
	//need to implement
}

static uint8_t *get_gui_data(uint32_t *width, uint32_t *height)
{
	//need to implement
}
 

각각 하나의 도형만을 그리니 아주 간단하게 구현했지만 실제로는 더 복잡해질 수도 있을 겁니다. 그럼 main에서 어떻게 호출되는지 보도록 하겠습니다.

/* main.c */
void main(void)
{
	shape_t *retangle = setup_shape_retangle();
	shape_t *circle = setup_shape_circle();

	retangle->set_area(10, 10);
	circle->set_area(35, 0);

	shape_draw(0, 0, retangle);
	shape_draw(0, 0, circle);
}
 

main 에서는 각각의 도형을 호출하고, 특성을 정하고 shape_draw에 넘겨서 그림을 그립니다. shape_draw에서는 해당 도형의 메모리를 호출하여 그림을 그리게 됩니다.

 

C언어에서 특히 firmware 수준의 소프트웨어서는 동적 메모리 할당(malloc) 사용을 하지 않는게 좋지만 꼭 사용해야 한다면 해제에 신경 써줘야 합니다. 그리고 단편화로 인한 성능저하도 어느정도 감안해야 합니다. 요즘은 다양한 malloc 함수를 구하 수 있으니 적당한 것을 골라 사용하는 것도 좋은 방법이 됩니다.

 

이전 포스트에서 언급했듯, 객체지향 언어에서 지원하는 new 키워드를 C언어에서 비슷하게 따라하려면 미리 개수를 한정 혹은 예상하여 선언 및 할당을 해줘야 합니다. C언어에서 동적이라는 위험할 수 있으니 static하게 선언하여 사용하면 되며, 경험했던 많은 프로젝트에서 아무런 문제가 없었습니다.

 

즐거운 C 생활되세요.

'▶ C Application > 디자인 패턴' 카테고리의 다른 글

Factory method 패턴  (0) 2023.12.23
소프트웨어 Timer 만들기  (0) 2023.12.20
Iterator 패턴 - Queue  (1) 2023.12.16
Singleton 패턴  (0) 2023.12.16
Observer 패턴  (0) 2023.12.14