Strategy 패턴

Strategy 패턴은 아주 단순하며 디자인 패턴 중에 가장 유용한 패턴입니다. 사용법은 아주 간단하며 interface를 만들고 원하는 곳에서 Concrete 코드를 치환해서 사용하면 됩니다. 적절한 예제일지는 모르지만, 특정한 상황에서 uart를 spi로 변경하는 드라이버 예제를 보도록 하겠습니다.

먼저 interface를 구현해보도록 하겠습니다.

/* driver.h */
typedef struct
{
	bool (*open)(void);
	bool (*close)(void);
	bool (*read)(uint32_t addr, uint8_t *value);
	bool (*write)(uint32_t addr, uint8_t *value, uint32_t len);
}driver_t;
 

open, close, read, write 네가지를 만들었습니다. 필요하면 더 할 수도 있습니다. read, write에는 addr 파라메터가 있는데, word 단위의 파라메터는 메모리 주소(예:자료형의 포인터)를 전달할 수 있기 때문에 미리 정의해 두면 쓰일 일이 많습니다. 물론 필요에 따라서 사용하지 않아도 됩니다.

 

다음 Concrete 코드를 보겠습니다. uart, spi 두가지 입니다. 실제로는 두개의 c 파일로 구분되어야 합니다.

먼저 uart 입니다. 실제로 uart open, close, read, write 의 구체적인 구현부분은 여러분께서 추가하여야 합니다.

 

setup_uart_driver() 함수가 instance를 반환하는 함수라고 생각하시면 됩니다.

/* uart.c */
bool open(void)
{
	// init and setting uart

	return true;
}

bool close(void)
{
	// de-init and stop uart

	return true;
}

bool read(uint32_t addr, uint8_t *value)
{
	//read from uart data register

	return true;
}

bool write(uint32_t addr, uint8_t *value, uint32_t len)
{
	//write to uart data register

	return true;
}

driver_t *setup_uart_driver(void)
{
	static driver_t this;

	this.open = open;
	this.close = close;
	this.read = read;
	this.write = write;

	return &this;
}
 

다음은 spi 콘크리트 코드 입니다. 거의 uart와 동일합니다. 마찬가지로 실제 구동을 위한 코드는 직접 구현하여야 합니다.

/* spi.c */
bool open(void)
{
	// init and setting spi

	return true;
}

bool close(void)
{
	// de-init and stop spi

	return true;
}

bool read(uint32_t addr, uint8_t *value)
{
	//read from spi data register

	return true;
}

bool write(uint32_t addr, uint8_t *value, uint32_t len)
{
	//write to spi data register

	return true;
}

driver_t *setup_spi_driver(void)
{
	static driver_t this;

	this.open = open;
	this.close = close;
	this.read = read;
	this.write = write;

	return &this;
}
 

특히 uart나 spi의 데이터 수신을 interrupt로 사용한다면 IRQ 핸들러에서 위의 드라이버 read() 부분을 호출하면 됩니다. 실제 Strategy 디자인 패턴의 코드를 살펴보도록 하겠습니다. 지난번에 만든 스케쥴러와 같이 작성해 보았습니다.

/* main.c */
static driver_t *char_driver;

void main(void)
{
	register_schedule(message_process);

	while(1)
	{
		execute_schedule(0, 0);
	}
}

void message_process(uint32_t param1, uint32_t param2)
{
	msg_t msg;
	uint32_t msg_param1, msg_param2;

	if(get_message(&msg, &msg_param1, &msg_param2) == true)
	{
		switch(msg)
		{
		case MSG_TEST_1:
			char_driver = setup_uart_driver();
			process_test1(msg_param1, msg_param2);
			break;

		case MSG_TEST_2:
			char_driver = setup_spi_driver();
			process_test2(msg_param1, msg_param2);
			break;

		case MSG_NONE:
			break;

		default:
			break;
		}
	}
}

void process_test1(uint32_t param1, uint32_t param2)
{
	...
}

void process_test2(uint32_t param1, uint32_t param2)
{
	...
}

 

처음 char_driver = setup_uart_driver(); 부분에서 uart를 사용하고 메시지 MSG_TEST_2가 들어오면 spi로 드라이버가 교체되는 것을 볼 수 있습니다.

 

이와 같이 런타임시에 전략(Strategy)을 수정할 수 있는것이 Strategy 패턴입니다. 다양한 메소드들을 하나의 interface로 묶을 수 있어 현업에서 driver 개발에 많이 사용됩니다.

 

읽어주셔서 감사합니다.

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

Singleton 패턴  (0) 2023.12.16
Observer 패턴  (0) 2023.12.14
Message 기반 프로세스  (0) 2023.12.10
RTOS 없이 구현한 스케줄러  (0) 2023.12.09
State 패턴  (1) 2023.12.08