Observer 패턴

Observer 패턴은 정말 많이 사용하는 디자인 패턴입니다. 자바 프레임워크에서도 쉽게 찾아 볼 수 있는데 흔히 Java C#에서 사용되는 Event Listener 들이 이 종류에 속합니다.

 

이번 장에서 만들 Observer pattern 예제에는 아래의 요소가 필요합니다.

 

observer_t : 관찰자 구조체

calculate_temperature : 온도 측정 실행함수

display_degree_handler : 화면에 측정된 온도를 표시하는 함수

file_write_degree_handler : 파일에 측정된 온도를 기록하는 함수

 

우선 헤더를 보겠습니다.

/* header.h */
typedef void (*observer_handler_t)(uint32_t degree, uint32_t param1);

typedef struct
{
	observer_handler_t update;
}observer_t;

extern bool register_observer(observer_handler_t *observer);
extern bool unregister_observer(observer_handler_t *observer);
extern void calculate_temperature(void);
 

observer_handler_t 가 바로 관찰자 Call back함수 입니다. 어떠한 계산이 완료된 후 그 계산값을 등록된 핸들러를 호출하여 전달할 예정입니다. 이러한 핸들러들을 등록, 삭제하는 함수들이 register_observer, unregister_observer 들입니다.

 

다음은 실제로 observer를 등록하고 삭제하는 코드 temperature.c 보도록 하겠습니다. 핸들러 등록, 삭제하는 코드들이 들어 있습니다.

/* temperature.c */
#define MAX_OBSERVER_COUNT	(10)
static observer_t *g_observer[MAX_OBSERVER_COUNT];
static uint8_t g_observer_len;

bool register_observer(observer_handler_t *observer)
{
	if(g_observer_len >= MAX_OBSERVER_COUNT)
		return false;

	g_observer[g_observer_len++] = observer;

	return true;
}

bool unregister_observer(observer_handler_t *observer)
{
	for(int i=0; i<g_observer_len; i++)
	{
		if(g_observer[i] == observer)
		{
			for(int j=i; j<g_observer_len; j++)
			{
				if((j+1) == g_observer_len)
					break;

				g_observer[j] = g_observer[j+1];
			}

			g_observer_len--;
			return true;
		}
	}

	return true;
}
 

추가적인 temperature.c 코드를 보겠습니다. 계산된 값을 update_observer에 전달하면 등록된 핸들러들을 호출하여 온도 값을 다시 일괄적으로 전달해 주게 됩니다. 여기서 파라메터가 두개인 별다른 이유는 없으며 추후 확장성을 위해 이렇게 추가될 수도 있음을 이해하시면 됩니다.

static void update_observer(uint32_t temperature)
{
	for(int i=0; i<g_observer_len; i++)
	{
		g_observer[i]->update(temperature, 0);
	}
}

void calculate_temperature(void)
{
	uint32_t degree = 0;

	// need to implement

	update_observer(degree);
}
 

마지막으로 main.c에서 observer 핸들러를 등록하고 사용하는 방법을 보도록 하겠습니다. 먼저 핸들러(Call back) 함수들을 만들고 observer 등록을 시켰습니다. 각각의 핸들러들은, 1초 마다 calculate_temperature()를 호출하여 계산된 결과를 받아볼 것입니다.

/* main.c */
void main(void)
{
	observer_t display_degree;
	observer_t file_write_degree;

	display_degree.update = display_degree_handler;
	file_write_degree.update = file_write_degree_handler;

	register_observer(display_degree);		//옵저버등록
	register_observer(file_write_degree);	//옵저버등록

	while(1)
	{
		calculate_temperature();			//1초에 한번씩 온도 체크
		DelayMS(1000);
	}
}

void display_degree_handler(uint32_t degree, uint32_t param1)
{
	//화면에 디스플레이 구현
}

void file_write_degree_handler(uint32_t degree, uint32_t param1)
{
	//파일에 온도 기록
}
 

위의 코드에서는 main 무한루프에서 1초에 한번씩 calculate_temperature를 호출하였으나, interrupt 라든지, timer 라든지 아니면 버튼을 누를 경우에만 호출하든지 다방면에서 알맞게 사용하면 됩니다.

 

Observer 패턴을 사용하면 온도가 업데이트 되는 즉시 등록된 핸들러에 업데이트가 일어납니다. 하지만 Observer 패턴을 사용하지 않는다면 계속해서 온도가 업데이트되었는지 확인하는 코드가 추가되어야 하며 온도를 필요로하는 코드가 많아지만 프로그램의 복잡성이 많이 높아집니다. 이런점이 Obsever 패턴을 사용해야하는 이유입니다.

 

이상으로 Observer 패턴을 알아봤습니다. 명칭만 다르지 Java에서 listener, C++ callback등 정말 많이 사용되고 있습니다.

 

감사합니다.

 

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

Iterator 패턴 - Queue  (0) 2023.12.16
Singleton 패턴  (0) 2023.12.16
Strategy 패턴  (0) 2023.12.12
Message 기반 프로세스  (0) 2023.12.10
RTOS 없이 구현한 스케줄러  (0) 2023.12.09