728x90

방문자 패턴은 연산(행동)을 적용할 클래스를 변경하지 않고도 새로운 연산을 정의할 수 있게 하는 패턴이다.

 

방문자 패턴은 단일 클래스로만 구성 되어 있을때도 유용하지만, 이미 수많은 클래스가 "군"(많은 결합)

이루고 있어서, 새로운 기능 추가시 많은 비용( 많은 수정 )이 들어갈때 매우 유용하게 사용할 수 있다.

(수정하는 양이 많아지면, qa 대상도 곱으로 늘어난다)

 

예를 들어 현재 라이브 서비스중인 게임회사에 새로 입사했다.

프로그래머가 1명뿐이던 영세 회사인데, 이전 프로그래머가 그만두면서 들어와서 legacy 코드를 물어볼 사람도 없다.

(실제 상황이라면, 뤈)

 

현재 웹서버 기반(HTTP)으로 게임을 만들었으나, 답답한 지연성 때문에 좀더 compact하게

깡 TCP로 만들기로 하여, 일단 간단히 TCP Socket으로 송수신 가능한 기능을 만들었다.

class TCPClient
{
public:
	//@brief : socket 초기화 및 서버 연결등의 처리
	void init(void) noexcept;

	//@brief : 별도의 스레드에서 로그아웃, 종료, 서버 응답 없음 등의 이유로 게임이 종료되어야 할때까지 실행됨
	void run(void) noexcept;

	//@brief : 송/수신 관련 인터페이스. 
	//{@
	void send(void) noexcept;
	void receive(void) noexcept;
	//@}
};

이제 기존에 HTTP로 요청하던 부분들을 다음과 같이 변경할 것이다.

class Player
{
public:
	void func(void) noexcept
	{
		// 플레이어 관련 동기화 데이터 전송
		//httpClient->requect();
		tcpClient->send();
	}
};

 

근데 얼마 안가서 또 이번에는 TCP의 "연결지향성"이 여전히 느리다 판단하고, UDP로 변경하자고 한다....

이럴때마다, 코드를 다 엎는 거는 힘들고(심지어 라이브 서비스중) 지친다. 이럴때 "방문자 패턴"을 적용해보자.

class NetworkingVisitor
{
public:
	virtual void visit(Player* player) noexcept = 0;
};

class TCPVisitor : public NetworkingVisitor
{
public:
	virtual void visit(Player* player) noexcept
	{
		// tcp용 패킷 만들기
		_tcpClient->send();
	}
};

class HTTPVisitor : public NetworkingVisitor
{
public:
	virtual void visit(Player* player) noexcept
	{
		// http용 패킷 만들기
		_httpClient->request();
	}
};

networking 기능을 확장시킬 NetworkingVisitor를 interface로 만들고, 프로토콜별로 기능을 수행할 TCPVisitor와

HTTPVisitor를 추가하였다. 

 

이제 Player class는 visitor를 방문하기만 하면 해당 기능을 사용할 수 있다!

#define Networking_Method new TCPVisitor()
//#define Networking_Method new HTTPVisitor();

class Player : GameObject
{
public:
	void func(void) noexcept
	{
		// 플레이어 관련 동기화 데이터 전송
		//httpClient->requect();
		//tcpClient->send();

		NetworkingVisitor* visitor = Networking_Method;
		visitor->visit(this);
	}
};

이렇게 Player코드에서 아예 네트워킹 관련 코드는 빠지게 되고, Player class와

TCPClient class(혹은 HTTPClient)와의 결합도 사라졌다. (c++이니까 define으로 switch 쉽게 해둔 것은 덤.)


서두에서 말했듯 수많은 클래스에 걸쳐서 비슷한 연산(기능)을 추가할때 기존 구조를 확장할 수 있어서

유용하게 사용할 수 있다.

 

또한, 위 예제 처럼 visitor만 교체하면 새로운 기능을 할 수 있으므로, 일종의 "전략 패턴"으로도 볼 수 있다.

728x90

'프로그래밍 > C++' 카테고리의 다른 글

Observer Pattern  (0) 2021.08.13
Bridge Pattern  (0) 2021.07.15
Prototype Pattern  (0) 2021.07.13
Adaptor Pattern  (0) 2021.06.30
Factory Pattern - Simple Factory  (1) 2021.02.21

+ Recent posts