[250312 TIL] 팩토리 패턴_디자인 패턴 공부로 포인터 다시 공부
1️⃣ C++ 코딩 테스트 공부
🔹방문길이
#include <string>
#include <set>
#include <algorithm>
#include <cmath>
using namespace std;
pair<int, int> moveVirtex(char dir, const pair<int, int>& position)
{
pair<int, int> newP = {0, 0};
switch(dir)
{
case 'U':
newP.first = position.first;
newP.second = position.second + 1;
break;
case 'D':
newP.first = position.first;
newP.second = position.second - 1;
break;
case 'L':
newP.first = position.first - 1;
newP.second = position.second;
break;
case 'R':
newP.first = position.first + 1;
newP.second = position.second;
break;
default:
break;
}
return newP;
}
int solution(string dirs) {
set<set<pair<int, int>>> lines;
pair<int, int> currentPosition = {0, 0};
for(char dir : dirs)
{
pair<int, int> newPosition = moveVirtex(dir, currentPosition);
if(abs(newPosition.first) > 5 || abs(newPosition.second) > 5)
{
continue;
}
set<pair<int, int>> line;
line.insert(currentPosition);
line.insert(newPosition);
lines.insert(line);
currentPosition = newPosition;
}
int length = lines.size();
return length;
}
💭회고
처음에는 방문한 좌표들을 Set에 넣고 Set에 존재하는 좌표의 개수를 세서 길이를 구하려고했는데 좌표의 처음과 끝이 맞닿지 않는 직선이라면 가능하지만 닿을 경우 면 안쪽에 선을 카운팅하기가 어려워졌다.
그래서 Set에 현재 좌표와 다음 좌표를 넣어 line을 만들고 이 라인을 저장하는 Set lines를 만들어서 카운팅 해보자는 생각에 한번 넣어봤는데 바로 풀려서 당황하긴 했다.
역시 자료구조를 잘 사용하면 문제 풀이가 쉬워지는 매직…
하지만 이 자료구조의 틀을 생각해내는게 어려운 것 같다..🥲
책에서는 아예 bool 배열로 문제를 풀었다. 문제 풀이를 생각하면서 좌표별로 bool 값을 해볼까도 생각해봤는데 4방향을 어떻게 담아야할지 모르겠어서 넘겼었는데 그냥 3차원 배열로 하면 되는구나..!
2️⃣ C++ 디자인 패턴 공부
🔹팩토리 메서드가 필요한가?
팩토리 메서드 예시를 봤을 때 스폰 종류를 데이터 테이블로 빼서 사용할 수 있는데
굳이 사용해야 하나 싶은 느낌이 들었다.
이에 대한 AI의 답
몬스터 생성에 복잡한 로직이 필요하거나, 다양한 스폰 조건이 있는 경우 팩토리 메서드 패턴을 사용하는 것이 유리
예를 들어, 일부 몬스터는 아이템을 가지고 태어나고, 일부는 다른 조건에서 태어나는 경우
팩토리 메서드로 각 몬스터를 어떻게 스폰할지 정의 가능
🔹추상 팩토리
C# to C++
#include <iostream>
#include <memory>
using namespace std;
class IButton
{
public:
virtual void Render() = 0;
};
class ICheckbox
{
public:
virtual void Render() = 0;
};
class WindowsButton : public IButton
{
public:
virtual void Render() override
{
cout << "Windows 버튼"<<endl;
}
};
class MacButton : public IButton
{
public:
virtual void Render() override
{
cout << "Mac 버튼"<<endl;
}
};
class WindowsCheckbox : public ICheckbox
{
public:
virtual void Render() override
{
cout << "Windows 체크박스"<<endl;
}
};
class MacCheckbox : public ICheckbox
{
public:
virtual void Render() override
{
cout << "Mac 체크박스"<<endl;
}
};
class IGUIFactory
{
public:
virtual unique_ptr<IButton> CreateButton() = 0;
virtual unique_ptr<ICheckbox> CreateCheckbox() = 0;
};
class WindowsFactory : public IGUIFactory
{
public:
unique_ptr<IButton> CreateButton() override
{
return make_unique<WindowsButton>();
}
unique_ptr<ICheckbox> CreateCheckbox() override
{
return make_unique<WindowsCheckbox>();
}
};
class MacFactory : public IGUIFactory
{
public:
unique_ptr<IButton> CreateButton() override
{
return make_unique<MacButton>();
}
unique_ptr<ICheckbox> CreateCheckbox() override
{
return make_unique<MacCheckbox>();
}
};
class AbstractFactory
{
private:
unique_ptr<IButton> _Button;
unique_ptr<ICheckbox> _Checkbox;
public:
AbstractFactory(IGUIFactory& Factory)
{
_Button = Factory.CreateButton();
_Checkbox = Factory.CreateCheckbox();
}
void RenderUI()
{
_Button->Render();
_Checkbox->Render();
}
};
int main()
{
WindowsFactory WF;
AbstractFactory WinApp = AbstractFactory(WF);
WinApp.RenderUI();
MacFactory MF;
AbstractFactory MacApp = AbstractFactory(MF);
MacApp.RenderUI();
return 0;
}
C++로 코드를 변환하며 포인터 사용에 대한 정리를 할 수 있었다. (지금에서야..?)
C++에서 자식 클래스의 인스턴스를 부모 클래스 변수에 저장하려면 *을 붙여줘야 함
객체 슬라이싱 때문에 포인터 또는 참조를 사용하여 업캐스팅 해줘야함
❓객체 슬라이싱
자식 클래스 고유의 데이터/함수가 잘려나가는 현상
부모 클래스 타입으로 자식 클래스의 인스턴스를 저장할 때는 포인터 저장이므로 new로 동적 할당해줘야 함
기본(스택) 생성자로 만들면 포인터로 가리킬 공간이 없기 때문에 동적으로 힙에 올려서 주소를 받아야 함
AI가 말해준 개선 사항
- 가상 함수가 있는 클래스(인터페이스)는 반드시 가상 소멸자 써줘야 함
- unique_ptr 은 복사 불가라서 클래스도 복사 불가로 만들어야 일관성있기 때문에 (클래스를 복사하면 unique_ptr도 복사되니까) 해당 멤버가 있으면 반드시 복사 생성/대입을 삭제(delete) 해주는 게 안전하고 자연스러움.
AbstractFactory(const AbstractFactory&) = delete; // 복사 생성자 삭제
AbstractFactory& operator=(const AbstractFactory&) = delete; // 복사 대입 연산자 삭제
AbstractFactory(AbstractFactory&&) = default; // 이동 생성자
AbstractFactory& operator=(AbstractFactory&&) = default; // 이동 대입 연산자
🔹심플 팩토리 메서드
위의 추상 팩토리 코드에서 Factory 클래스를 추상-구체로 나누지 않고
하나의 Factory 클래스에서 매개변수에 따라 조건문을 활용해 생성 해줌(간단하거나 제품군이 적을 때 적합)
3️⃣ 언리얼 멀티플레이
Unreal5 리슨 서버와 데디케이티드 서버
1️⃣ 개요언리얼이 제공하는 서버인 데디케이트 서버와 리슨 서버에 대해 알아보고자 한다.먼저 데디케이트 서버와 리슨 서버는한국의 온라인 게임이 사용하는 Persistent 서버(항상 운영되는 상
yoosorang.tistory.com
언리얼의 리슨 서버와 데디케이트 서버를 공부해보았다. 아직 복잡한 상황에서 서버를 사용해본 것이 아니라
확실하게 알았다고 보기 어려운 것 같다.
과제를 해보면서 이것저것 실험해보아야 할 것 같다.
💭 회고
디자인 패턴을 공부하면서 내가 계속 헷갈려했던
매개변수 *, &와 스마트 포인터, 동적할당에 대한 개념의 감을 찾아가고 있다.
이미 배웠던 내용이지만 뭔가 개념을 받아들이기만 하고 여기에 사용하는 게 맞나 싶으면서도
사용해봤더니 코드가 돌아가서 그냥 넘겼었기에 아직까지도 헷갈렸던 것 같다.
아직 완전히 이해했다고는 하기 어렵지만 그래도 더듬더듬 이건가본데..?라는 느낌으로 가고 있어서
앞으로 배울 디자인 패턴도 C++로 변환해가면서 포인터와 동적할당, 다형성에 대해 숙지할 예정이다.