Facade pattern에 대해 알아보자. Facade의 목적은 다음과 같다.
1. 서브 시스템을 합성하는 다수의 객체들의 인터페이스 집합에대해 일관된 하나의 인터페이스를 제공한다.
2. 서브 시스템을 사용하기 쉽게 하기 위한 포괄적 개념의 인터페이스를 제공한다.
C 스타일 TCP 서버의 예
다음의 C스타일의 소스코드를 객체지향으로 변경해가면서 Facade를 적용해보자. 이 코드는 간단한 TCP 서버이다. 이러한 TCP서버를 만드는 일반적인 절차는 다음과 같다.
1. 1. N/W 라이브러리 초기화(Windows OS)
2. socket 생성(socket)
3. socket에 주소 지정(bind)
4. socket을 대기 상태로 변경(listen)
5. client의 접속을 대기(accept)
6. 접속된 클라이언트와 통신(recv, send)
7. socket 닫기(close, closesocket)
8. N/W 라이브러리 clean up(Windows OS)
#include <iostream>
#include <WinSock2.h>
using namespace std;
int main()
{
// 1. 네트워크 라이브러리 초기화
WSADATA w;
WSAStartup(0x202, &w);
// 2. Socket 생성
int sock = socket(PF_INET, SOCK_STREAM, 0); // TCP 소켓
// 3. 소켓에 주소 작성
struct sockaddr_in addr = { 0 };
addr.sin_family = AF_INET;
addr.sin_port = htons(4000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
bind(sock, (SOCKADDR*)&addr, sizeof(addr));
// 4. 소켓을 대기 상태로 변경
listen(sock, 5);
// 5. 클라이언트가 접속할때 까지 대기
struct sockaddr_in addr2 = { 0 };
int sz = sizeof(addr2);
accept(sock, (SOCKADDR*)&addr2, &sz);
// 6. socket 라이브러리 cleanup
WSACleanup();
}
이 예제를 객체지향으로 바꿔보자. 이에 대한 방법은 크게 두가지로 구분할 수 있을것이다.
1. 모든 절차를 하나의 클래스에 모든 기능을 넣는다.
2. 기능별로 분리해서 각각의 클래스로 설계한다.
-> (2) 작은 클래스가 유지보수가 편리하고 활용도가 높다!
객체 지향 프로그래밍으로 변경
#include <iostream>
#include <WinSock2.h>
using namespace std;
// network 라이브러리의 초기화와 cleanup을 담당.
class NetworkInit
{
public:
NetworkInit()
{
WSADATA w;
WSAStartup(0x202, &w);
}
~NetworkInit()
{
WSACleanup();
}
};
// IP 주소 관리.
class IPAddress
{
struct sockaddr_in addr;
public:
IPAddress(const char* ip, short port)
{
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);
}
struct sockaddr* getRawAddress() { return (struct sockaddr*)&addr; }
};
// socket 프로그래밍의 일반적인 절차.
class Socket
{
int sock;
public:
Socket(int type) { sock = socket(PF_INET, type, 0); }
void Bind(IPAddress* ip)
{
::bind(sock, ip->getRawAddress(), sizeof(struct sockaddr_in));
}
void Listen() { ::listen(sock, 5); }
void Accept()
{
struct sockaddr_in addr2 = { 0 };
int sz = sizeof(addr2);
accept(sock, (struct sockaddr*)&addr2, &sz);
}
};
int main()
{
NetworkInit init;
IPAddress ip("127.0.0.1", 4000);
Socket sock(SOCK_STREAM); // TCP
sock.Bind(&ip);
sock.Listen();
sock.Accept();
}
각 기능 별로 분리해서 클래스가 구현되었다.
1. N/W 라이브러리 초기화 - NetworkInit
2. IP 주소 관리 - IPAddress
3. socket 프로그래밍의 일반적인 함수 - Socket
이러한 변화를 통해 세부적인 코드가 클래스 내부에서 처리되므로 함수 기반의 코드보다 간결하고 사용이 조금더 편리해졋다. 그런데 좀더 사용하기 쉽고 간결한 형태로 제공 될 수 없을까?
조금더 사용이 편리하고 간결하게!
#include <iostream>
#include <WinSock2.h>
#include <string>
using namespace std;
// network 라이브러리의 초기화와 cleanup을 담당.
class NetworkInit
{
public:
NetworkInit()
{
WSADATA w;
WSAStartup(0x202, &w);
}
~NetworkInit()
{
WSACleanup();
}
};
// IP 주소 관리.
class IPAddress
{
struct sockaddr_in addr;
public:
IPAddress(const char* ip, short port)
{
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);
}
struct sockaddr* getRawAddress() { return (struct sockaddr*)&addr; }
};
// socket 프로그래밍의 일반적인 절차.
class Socket
{
int sock;
public:
Socket(int type) { sock = socket(PF_INET, type, 0); }
void Bind(IPAddress* ip)
{
::bind(sock, ip->getRawAddress(), sizeof(struct sockaddr_in));
}
void Listen() { ::listen(sock, 5); }
void Accept()
{
struct sockaddr_in addr2 = { 0 };
int sz = sizeof(addr2);
accept(sock, (struct sockaddr*)&addr2, &sz);
}
};
class TCPServer
{
NetworkInit init;
Socket sock;
public:
TCPServer() : sock(SOCK_STREAM) {}
void Start(const char* sip, short port)
{
IPAddress ip(sip, port);
sock.Bind(&ip);
sock.Listen();
sock.Accept();
}
};
int main()
{
TCPServer server;
server.Start("127.0.0.1", 4000);
}
네트워크 프로그래밍을 경험해본 사람은 이러한 절차에 대해 잘 알고 있겠지만, 네트워크 프로그래밍을 해보지 않은 사람은 그 절차에 대해 잘 모를것이고 이에 대한 학습이 필요할 것이다. 그리고 이러한 절차 코드들은 반복해서 만들때마다 반복된다. 그러므로 위와 같이 단 두줄 만으로 서버 생성과 초기화를 할 수 있게 되었다. 실재 C#에서 이러한 방식으로 두줄정도로 TCP 서버를 생성할 수 있다.
보통 OS는 C기반으로 되어있다. 이들을 분야/기능별 C함수로 제공한다. (1차)
이러한 C함수를 그대로 사용하면 복잡해지므로 이를 분야별/기능별 클래스로 제공한다.(2차)
그런데 이러한 코드들이 반복해서 사용되므로 일반적인 API들을 맵핑하는 (3차) API를 제공한다.
3차 API TCPServer, UDPServer, TCPCllient, UDPClient -> Facade 계층
2차 API IPAddress, Timer, ScoketError -> 분야/기능별 클래스로 제공
1차 API 분야/기능별 C함수로 제공
OS layer Windows, Linux, Mac
정리
Facade는 "표면"이라는 사전적 의미와 같이 복잡한 절차와 계층을 단순화 한 인터페이스를 제공하여 사용 편의성을 높이는데 목적이 있다.
'프로그래밍 이야기 > C++ 기초' 카테고리의 다른 글
[Design Pattern] PIMPL (Pointer to Implementation) (0) | 2021.02.03 |
---|---|
[Design Pattern] Bridge (0) | 2021.02.02 |
[C++]Design Pattern - STL과 Adapter (0) | 2020.12.30 |
[C++]Design Pattern - Adapter (0) | 2020.12.30 |
[게임이론] 벡터-내적의 활용 (0) | 2020.12.26 |