TIL

[250312 TIL] 팩토리 패턴_디자인 패턴 공부로 포인터 다시 공부

yoosorang 2025. 3. 12. 20:52

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가 말해준 개선 사항
  1. 가상 함수가 있는 클래스(인터페이스)는 반드시 가상 소멸자 써줘야 함
  2. 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++로 변환해가면서 포인터와 동적할당, 다형성에 대해 숙지할 예정이다.