AI나 컴파일러 개발자가 아니라면 익숙한 개념이 아닐 수 있다.

 

간단한 횡스크롤 플랫포머를 만들다고 할 때 버튼을 눌러서 점프하는 것을 만들 때, 점프를 했을 때, 또 한 번 더 누를 수가 있다. 이럴 때가 있을 수 있기 때문에 점프 상태가 아닐 때만 실행할 수 있도록 해야 한다.

또한, 땅에 있을 때 아래 버튼을 누르면 엎드리고, 버튼을 떼면 다시 일어서는 기능을 추가할 때, 점프 중 일 때에도 땅에 서 있는 모습으로 보일 수도 있다. 그렇기 때문에 점프 중인지 아닌지와 버튼을 누르고 있는지를 체크해야 한다.

이런 것을 보면 코드가 얼마 없는데도 조금만 건드리면 망가진다. 심지어 걷기 구현조차 시작하지도 않았다.

 

유한 상태 기계(FSM)

컴퓨터 과학 분야 중의 하나인 오토마타 이론에서 나왔다.

- 가질 수 있는 '상태'가 한정된다.

- 한 번에 '한 가지' 상태만 될 수 있다.

- '입력'이나 '이벤트'가 기계에 전달된다.

- 각 상태에는 입력에 따라 다음 상태로 바뀌는 '전이'가 있다.

 

예를 들어, 서 있는 동안 아래 버튼을 누르면 엎드리기 상태로 전이, 점프하는 동안 아래 버튼을 누르면 내려찍기 상태로 전이, 현재 상태에서 들어온 입력에 대한 전이가 없을 경우 입력을 무시

순수하게 형식만 놓고 보면 상태, 입력, 전이가 FSM의 전부, 컴파일러가 플로차트를 이해하지 못하니 구현해야 한다.

 

열거형과 다중 선택문

여러 플래그 변수 중에서 하나만 참일 때가 많다면 열거형(enum)이 필요하다는 신호

플래그 변수 여러 개 대신 상태 필드 하나만 만들면 된다.

분기 순서도 바뀐다. 이전에는 입력에 따라 먼저 분기한 뒤에 상태에 따라 분기

하나의 버튼 입력에 대한 코드는 모아둘 수 있었으나 하나의 버튼 입력에 대한 코드는 모아둘 수 있었으나

하나의 상태에 대한 코드는 흩어져 있었다. 상태 관련 코드를 한 곳에 모아두기 위해 상태에 따라 분기한다.

열거식으로 코드를 나눈다면 코드가 나눠졌다. 분기문을 다 없애지는 못했지만 업데이트해야 할 상태 변수를 하나로 줄였고, 하나의 상태를 관리하는 코드는 깔끔하게 한 곳에 모을 수 있다. 열거형은 상태 기계를 구현하는 가장 간단한 방벙, 이 정도만으로 충분할 때도 있다.

 

상태 패턴

 

상태 인터페이스

상태에 의존하는 모든 코드, 즉 다중 선택문에 있던 동작을 인터페이스의 가상 메서드로 만든다.

 

상태별 클래스

상태별로 인터페이스를 구현하는 클래스도 정의

메서드에는 정해진 상태가 되었을 때 캐릭터가 어떤 행동을 할지를 정의

다른 선택문에 있던 case별로 클래스를 만들어 코드를 옮기면 된다.

 

동작을 상태에 위임

객체의 현재 상태 객체 포인터를 추가해, 거대한 다중 선택문을 제거하고 상태 객체에 위임

 

상태 객체는 어디에 두어야 하나

상태를 바꾸려면 상태에 새로운 상태 객체를 할당해야 한다.

열거형은 숫자처럼 기본 자료형이기 때문에 신경 쓸 게 없지만

상태 패턴은 클래스를 쓰기 때문에 포인터에 저장할 실제 인스턴스가 필요

 

정적 객체

상태 객체에 필드가 따로 없다면 가상 메서드 호출에 필요한 가상 테이블 포인터만 있게 된다.

모든 인스턴스가 같기 때문에 인스턴스는 하나만 있으면 된다.

여러 FSM이 동시에 돌더라도 상태 기계는 다 같으므로 인스턴스 하나를 같이 사용하면 된다.

 

상태 객체 만들기

정적 객체만으로 부족할 떄도 있다. 엎드리기 상태에서는 시간 필드가 있는데 이 값이 객체마다 다르다 보니 정적 객체로 만들 수 없다. 객체가 하나라면 어떻게든 되겠지만, 여러 캐릭터의 객체들을 한 화면에 보여야 한다면 문제가 된다.

이럴 때는 전이할 때마다 상태 객체를 만들어야 한다. FSM이 상태별로 인스턴스를 갖게 된다. 새로 상태를 할당했기 때문에 이전 상태를 해제해야 한다. 상태를 바꾸는 코드가 현재 상태 메서드에 있기 때문에 삭제할 때 this를 스스로 지우지 않도록 주의해야 한다.

상태가 바뀔 때에만 새로운 상태를 반환하고, 밖에서는 반환 값에 따라 예전 상태를 삭제하고 새로운 상태를 저장

새로운 상태를 반환하지 않는다면 현재 상태를 삭제하지 않는다. 서 있기 상태에서 엎드리기 상태로 전이하려면 새로운 인스턴스를 생성해 반환한다.

 

입장과 퇴장

상태 패턴의 목표는 같은 상태에 대한 모든 동작과 데이터를 클래스 하나에 캡슐화하는 것이다.

객체는 상태를 변경하면서 객체의 스프라이트도 같이 바꾼다. 지금까지는 이전 상태에서 스프라이트를 변경했다.

이렇게 하는 것보다는 상태에서 그래픽까지 제어하는 게 바람직, 입장 기능을 추가

새로운 상태에 들어 있는 입장 기능을 호출하도록 상태 변경 코드를 수정

서기 상태로 변경하기만 하면 서기 상태가 알아서 그래픽까지 챙긴다. 그래야 상태가 제대로 캡슐화되었다고 할 수 있다.

그 전 상태와는 상관없이 항상 같은 입장 코드가 실행된다는 것도 장점, 실제 게임 상태 그래프라면 점프 후 착지 혹은 내려찍기 후 착지하는 식으로 같은 상태에 여러 전이가 들어올 수 있다. 그냥 두면 전이가 일어나는 모든 곳에 중복 코드를 넣었겠지만 이제는 입장 기능 한 곳에 코드를 모아둘 수 있다.

상태가 새로운 상태로 교체되기 직전에 호출되는 퇴장 코드도 이런 식으로 활용할 수 있다.

 

단점은?

상태 기계는 엄격하게 제한된 구조를 강제함으로써 복잡하게 얽힌 코드를 정리할 수 있게 해 준다.

FSM에는 미리 정해놓은 여러 상태와 현재 상태 하나, 하드 코딩되어 있는 전이만이 존재

상태 기계를 인공지능같이 더 복잡한 곳에 적용하다 보면 한계에 부딪히게 된다.

 

병행 상태 기계

총을 장착한 후에도 이전에 있었던 달리기, 점프, 엎드리기 같은 동작을 모두 할 수 있어야 한다.

그러면서 동시에 총도 쏠 수 있어야 한다.

FSM 방식을 고수하겠다면 모든 상태를 서기, 무장한 채로 서기, 점프, 무장한 채로 점프 같은 식으로 무장,

비무장에 맞춰 두 개씩 만들어야 한다.

무기를 추가할수록 상태 조합이 폭발적으로 늘어난다.

상태가 많아지는 것도 문제지만,

무장 상태와 비무장 상태는 총 쏘기 코드 약간 외에는 거의 같아서 중복이 많아진다는 점이 더 문제

두 종류의 상태, 무엇을 하는가와 무엇을 들고 있는가를 한 상태 기계에 욱여넣다 보니 생긴 문제

모든 가능한 조합에 대해 모델링하려다 보니 모든 쌍에 대해 상태를 만들어야 한다.

해결법은 상태 기계를 둘로 나누면 된다.

무엇을 하는가에 대한 상태 기계는 그대로 두고, 무엇을 들고 있는가에 대한 상태 기계를 따로 정의

각각의 상태 기계는 입력에 따라 동작을 실행하고 독립적으로 상태를 변경할 수 있다.

두 상태 기계가 서로 전혀 연관이 없다면 이 방법이 잘 들어맞는다.

현실적으로는 점프 도중에는 총을 못 쏜다든가, 무장한 상태에서는 내려찍기를 못한다든가 하는 식으로 복수의 상태 기계가 상호작용해야 할 수도 있다. 이를 위해 어떤 상태 코드에서 다른 상태 기계의 상태가 무엇인지를 검사하는 코드를 만들 일이 생길 수도 있다. 좋지는 않지만, 문제를 해결할 수는 있다.

 

계층형 상태 기계

단순한 상태 기계 구현에서는 이런 코드를 모든 상태마다 중복해 넣어야 한다.

그보다는 한 번만 구현하고 다른 상태에서 재사용하는 게 낫다.

상태 기계가 아니라 객체지향 코드라고 생각해보면, 상속으로 여러 상태가 코드를 공유할 수 있다.

점프와 엎드리기는 '땅 위에 있는' 상태 클래스를 정의해 처리,

서기, 걷기, 달리기, 미끄러지기는 '땅 위에 있는' 상태 클래스를 상속받아 고유 동작을 추가하면 된다.

이런 구조를 계층형 상태 기계라고 한다. 어떤 상태는 상위 상태를 가질 수 있고,

그 경우 그 상태 자신은 하위 상태가 된다.

이벤트가 들어올 떄 하위 상태에서 처리하지 않으면 상위 상태로 넘어간다.

상속받은 메서드를 오버라이드 하는 것과 같다.

계층형을 꼭 이렇게 구현해야 하는 건 아니다. 

주 클래스에 상태를 하나만 두지 않고 상태 스택을 만들어 명시적으로 현재 상태의 상위 상태 연쇄를 모델링할 수도 있다.

현재 상태가 스택 최상위에 있고 밑에는 바로 위 상위 상태가 있으며, 그 상위 상태 밑에는 그 상위 상태의 상위 상태가 있는 식, 상태 관련 동작이 들어오면 어느 상태든 동작을 처리할 때까지 스택 위에서부터 밑으로 전달 ( 아무도 처리하지 않는다면 무시하면 된다 )

 

푸시다운 오토마타

상태 스택을 활용하여 FSM을 확장하는 다른 방법도 있다.

계층형 FSM에서 봤던 스택과는 상태를 담는 방식도 다르고 해결하려는 문제도 다르다.

FSM에는 이력 개념이 없다는 문제가 있다. 현재 상태는 알 수 있지만 직전 상태가 무엇인지를 따로 저장하지 않기 때문에 이전 상태로 쉽게 돌아갈 수 없다.

일반적인 FSM에서는 이전 상태를 알 수 없다. 이전 상태를 알려면 서 있는 상태에서 총 쏘기, 달려가면서 총 쏘기, 점프하면서 총 쏘기 같은 식으로 상태마다 새로운 상태를 하나씩 더 만들어 총 쏘기가 끝났을 때 되돌아갈 상태를 하드 코딩해야 한다.

이것보다는 총 쏘기 전 상태를 저장해놨다가 나중에 불러와 써먹는 게 훨씬 낫다.

이럴 때 써먹을 만한 것으로 푸시다운 오토마타가 있다.

FSM이 한 개의 상태를 포인터로 관리했다면 푸시다운 오토마타에서는 상태를 스택으로 관리

FSM은 이전 상태를 덮어쓰고 새로운 상태로 전이하는 방식

푸시다운 오토마타에서는 이외에도 부가적인 명령이 두 가지 더 있다.

- 새로운 상태를 스택에 넣는다. 스택의 최상위 상태가 '현재' 상태이기 때문에, 새로 추가된 상태가 현재 상태가 된다.

다만, 이전 상태는 버리지 않고 방금 들어온 최신 상태 밑에 있게 된다.

- 최상위 상태를 스택에서 뺀다. 빠진 상태는 제거되고, 바로 밑에 있던 상태가 새롭게 '현재' 상태가 된다.

 

총 쏘기 상태를 구현할 때 딱 좋다. 총 쏘기 상태를 만들고, 발사 버튼을 누르면 총 쏘기 상태를 스택에 넣고 총 쏘기 애니메이션이 끝날 때 총쏘기 상태를 스택에서 빼면, 푸시다운 오토마타가 알아서 이전 상태로 보내준다.

 

얼마나 유용한가

FSM에는 몇 가지 확장판이 나와 있지만 FSM만으로는 한계가 있다.

요즘 게임 AI는 행동 트리나 계획 시스템을 더 많이 쓰는 추세다.

FSM이나 푸시다운 오토마타, 그 외 간단한 시스템들이 쓸모없다는 얘기는 아니다.

이것만으로도 특정 문제 해결을 위한 모델링으로선 충분

FSM은 다음 경우에 사용하면 좋다.

- 내부 상태에 따라 객체 동작이 바뀔 때

- 이런 상태가 그다지 많지 않은 선택지로 분명하게 구분될 수 있을 때

- 객체가 입력이나 이벤트에 따라 반응할 때

 

게임에서는 FSM이 AI에서 사용되는 걸로 가장 잘 알려져 있지만, 입력 처리나 메뉴 화면 전환, 문자 해석, 네트워크 프로토콜, 비동기 동작을 구현하는 데에도 많이 사용되고 있다.

'프로그래밍 패턴' 카테고리의 다른 글

싱글턴  (0) 2022.02.12
프로토타입  (0) 2022.02.06
관찰자 패턴  (0) 2022.02.01
경량 패턴  (0) 2022.01.27
명령 패턴  (0) 2022.01.22

싱글턴 패턴

 

1. 오직 한 개의 클래스 인스턴스만 갖도록 보장

인스턴스가 여러 개면 제대로 작동하지 않는 상황이 종종 있다. 외부 시스템과 상호작용하면서 전역 상태를 관리하는 클래스 같은 게 그렇다.

파일 시스템 API를 래핑하는 클래스가 있다고 할 때, 파일 작업은 완료하는 데 시간이 좀 걸리기 때문에 이 클래스는 비동기로 동작하게 만들어야 한다. 즉 여러 작업이 동시에 진행될 수 있으므로 작업들을 서로 조율, 한쪽에서는 파일을 생성하고 다른 한쪽에서는 방금 생성한 파일을 삭제하려고 한다면, 래퍼 클래스가 두 작업을 다 파악해서 서로 간섭하지 못하게 해야 한다.

이를 위해서는 파일 시스템 클래스로 들어온 호출이 이전 작업 전체에 대해서 접근할 수 있어야 한다.

아무 데서나 파일 시스템 클래스 인스턴스를 만들 수 있다면 다른 인스턴스에서 어떤 작업을 진행 중인지를 알 수 없다.

이를 싱글턴으로 만들면 클래스가 인스턴스를 하나만 가지도록 컴파일 단계에서 강제할 수 있다.

 

2. 전역 접근점을 제공

여러 내부 시스템에서 파일 시스템 래퍼 클래스를 사용할 것이다.

이들 시스템에서 파일 시스템 클래스 인스턴스를 따로 생성할 수 없다.

싱글턴 패턴은 여기에 대해 방법이 있다. 하나의 인스턴스만 생성하는 것에 더해서, 싱글턴은 그 인스턴스를 전역에서 접근할 수 있는 메서드를 제공, 이를 통해서, 누구든지 어디서든지 우리가 만든 인스턴스에 접근할 수 있다.

생성자를 private로 만들고 public으로 instance로 생성해서 클래스 내에서 new로 생성 후 있다면 retun 없다면 new로 생성해준다. 혹은 static으로 해서도 만들 수 있다.

class FileSystem
{
public:
    static FileSystem& instance()
    {
        static FileSystem* instance = new FileSystem();
        return *instance;
    }
 
private:
    FileSystem() {};
};

C++에서는 정적 지역 변수 초기화 코드가 멀티스레드 환경에서도 딱 한 번 실행되어야 한다.

즉 최신 C++ 컴파일러로 컴파일한다면 이 코드는 스레드 안전하다

(스레드 안전하게 초기화하는 것까지만 보장할 뿐)

 

3. 싱글턴을 왜 사용하는가?

한 번도 사용하지 않는다면 아예 인스턴스를 생성하지 않는다

메모리와 CPU 사용량을 줄이는 것은 항상 좋은 일이다. 싱글턴은 처음 사용될 때 초기화되므로, 게임 내에서 전혀 사용되지 않는다면 아예 초기화되지 않는다.

 

4. 런타임에 초기화된다.

보통 싱글턴 대안으로 정적 멤버 변수를 많이 사용, 정적 멤버 변수는 자동 초기화되는 문제가 있다.

즉, 컴파일러는 main 함수를 호출하기 전에 정적 변수를 초기화하기 때문에 프로그램이 실행된 다음에야 알 수 있는

(파일로 읽어 들인 설정 값 같은) 정보를 활용할 수 없다. 정적 변수 초기화 순서도 컴파일러에서 보장해주지 않기 때문에 한 정적 변수가 다른 정적 변수에 안전하게 의존할 수도 없다.

싱글턴은 최대한 늦게 초기화되기 때문에, 그때쯤에는 클래스가 필요로 하는 정보가 준비, 순환 의존만 없다면, 초기화할 때 다른 싱글턴을 참조해도 괜찮다.

 

5. 싱글턴을 상속할 수 있다.

파일 시스템 래퍼가 스로스 플랫폼을 지원해야 한다면 추상 인터페이스를 만든 뒤, 플랫폼마다 구체 클래스를 만들면 된다.

 

싱글턴이 왜 문제일까?

1. 알고 보니 전역 변수

게임이 점차 커지고 복잡해짐에 따라 설계와 유지보수성이 병목이 되기 시작했다. 하드웨어 한계보다는 생산성 한계 때문에 게임 출시가 늦어지는 시대가 되었다. 그러다 보니 전역 변수는 나쁘다는 것을 알게 되었다.

- 전역 변수는 코드를 이해하기 어렵게 한다.

남이 만든 함수에서 버그를 찾아야 할 때, 함수가 전역 상태를 건드리지 않는다면 함수 코드와 매개 변수만 보면 된다.

- 전역 변수는 커플링을 조장한다.

유지 보수하기 좋도록 코드를 느슨하게 결합해놓은 아키텍처를 아직 제대로 파악하지 못한 신입 개발자에게 첫 일감을 줬을 경우 기존 작업자들은 물리 코드와 사운드 코드 사이에 커플링이 생기는 걸 피하겠지만, 신입에게는 주어진 작업을 끝내고 싶은 마음뿐이기 때문에 AudioPlayer 인스턴스에 전역적으로 접근할 수 있다 보니, 신입이 #include 한 줄만 추가해도 신중하게 만들어놓은 아키텍처를 이상하게 만들 수 있습니다.

쉽게 할 수 없다는 것은 두 모듈이 서로 몰라야 하고 다른 방법으로 문제를 해결해야 한다는 것을 분명하게 알려주는 신호, 인스턴스에 대한 접근을 통제함으로써 커플링을 통제

- 전역 변수는 멀티스레딩 같은 동시성 프로그래밍에 알맞지 않다.

멀티스레딩을 최대한 활용하지는 못하더라도, 최소한 멀티스레딩 방식에 맞게 코드를 만들어야 한다. 무엇인가를 전역으로 만들면 모든 스레드가 보고 수정할 수 있는 메모리 영역이 생기는 셈, 다른 스레드가 전역 데이터에 무슨 작업을 하는지 모를 때도 있다. 이러다 보면 교착상태, 경쟁 상태 등 정말 찾기 어려운 스레드 동기화 버그가 생기기 쉽다.

싱글턴 패턴은 클래스로 캡슐화된 전역 상태

 

2. 싱글턴은 문제가 하나뿐일 때도 두 가지 문제를 풀려 든다.

'한 개의 인스턴스'와 '전역 접근' 중에서 보통은 '전역 접근'이 싱글턴 패턴을 선택하는 이유

로그 클래스를 생각할 때, 게임 내 모듈에서 진단 정보를 로그로 남길 수 있다면 편할 것

모든 함수 인수에 로그 클래스 인스턴스를 추가하면 메서드 시그니처가 번잡해지고 코드 의도를 알아보기 어려워진다.

가장 간단한 해결책은 로그 클래스를 싱글턴으로 만드는 것, 모든 함수에서 직접 로그 클래스에 접근해 인스턴스를 얻을 수 있다. 의도치 않게 로그 객체를 하나만 만들 수 있다는 제한.

처음에는 별로 문제가 되지 않지만 로그를 파일 하나에 다 쓴다면 인스턴스도 하나만 있으면 된다.

하지만 개발이 진행되면서 개발팀 모두가 각자 필요한 정보를 로그로 남기다 보니 로그 파일이 뒤죽박죽 되게 된다.

원하는 정보 하나를 찾기 위해서는 수십 페이지가 넘는 텍스트 파일을 뒤져야 한다.

로그를 여러 파일에 나눠 쓸 수 있다면 좋을 것, 분야별로 로그를 만들 수 있어야 하는데 싱글턴이다 보니 인스턴스를 여러 개 만들 수 없다. 이런 설계 제약이 로그 클래스를 사용하는 모든 코드에 영향을 미치게 된다.

 

3. 게으른 초기화는 제어할 수가 없다.

가상 메모리도 사용할 수 있고 성능 요구도 심하지 않은 데스크톱 PC에서는 게으른 초기화가 괜찮은 기법이지만 게임은 다르다. 시스템을 초기화할 때 메모리 할당, 리소스 로딩 등 할 일이 많다 보니 시간이 꽤 걸릴 수 있다.

오디오 시스템 초기화에 몇백 밀리세컨드 이상 걸린다면 초기화 시점을 제어해야 한다. 처음 소리를 재생할 때 게으른 초기화를 하게 만들면 전투 도중에 초기화가 시작되는 바람에 화면 프레임이 떨어지고 게임이 버벅댈 수 있다.

게임에서는 메모리 단편화를 막기 위해 힙에 메모리를 할당하는 방식을 세밀하게 제어하는 게 보통, 오디오 시스템이 초기화될 때 상당한 메모리를 힙에 할당한다면, 힙 어디에 메모리를 할당할지를 제어할 수 있도록 적절한 초기화 시점을 찾아야 한다.

 

두 가지 문제 때문에 여러 가지 게임에서는 게으른 초기화를 사용하지 않는다.

정적 함수로 싱글턴을 선언하면 게으른 초기화 문제를 해결할 수 있지만, 싱글턴이 그냥 전역 변수보다 나은 점을 몇 개 포기해야 한다. 정적 인스턴스를 사용하면 다형성을 사용할 수 없다. 클래스는 정적 객체 초기화 시점에 생성, 인스턴스가 필요 없어도 메모리를 해제할 수 없다.

싱글턴 대신 단순한 정적 클래스를 하나 만든 셈, 나쁘진 않지만, 정적 클래스만으로 다 해결할 수 있다면 정적 함수를 대신 사용하는 게 낫다. 정적 메모리에 접근한다는 걸 더 분명하게 보여주기 때문이다.

 

대안

 

1. 클래스가 꼭 필요한가?

싱글턴 클래스 중에는 애매하게 다른 객체 관리용으로만 존재하는 '관리자'가 많았다. 

관리 클래스가 필요할 때도 있지만 OOP를 제대로 이해하지 못해 만드는 경우도 많았다.

관리자 클래스를 없애고 났을 때 문제가 없어졌을 때도 있었다. 서툴게 만든 싱글턴은 다른 클래스에 기능을 더해주는 도우미인 경우가 많다. 가능하다면 도우미 클래스에 있던 작동 코드를 원래 클래스로 옮기자. 객체가 스스로를 챙기게 하는 게 바로 OOP이다.

 

2. 오직 한 개의 클래스 인스턴스만 갖도록 보장

특정 코드에서만 접근할 수 있게 만들거나 아예 클래스의 private 멤버 변수로 만들고 싶을 수 있을 때, 전역에서 누구나 접근할 수 있게 만들면 구조가 취약.

변수는 작업 가능한 선에서 최대한 적은 범위로 노출하는 게 일반적으로 좋다. 변수가 노출된 범위가 적을수록 코드를 볼 때 머릿속에 담아둬야 할 범위가 줄어든다.

-넘겨주기

객체를 필요로 하는 함수에 인수로 넘겨주는 게 가장 쉬우면서도 최선인 경우가 많다.

객체를 렌더링 하는 함수를 생각해보고 렌더링 하려면 렌더링 상태를 담고 있는 그래픽 디바이스 대표 객체에 접근할 수 있어야 한다. 이럴 때는 일반적으로 모든 렌더링 함수에서 context 같은 이름의 매개변수를 받는다.

반면, 어떤 객체는 메서드 시그니처에 포함되지 않는다.

 

- 상위 클래스로부터 얻기

많은 게임에서 클래스를 대부분 한 단계만 상속할 정도로 상속 구조를 얕고 넓게 가져간다.

몬스터나 다른 게임 내 객체가 상속받는 GameObject라는 상위 클래스가 있을 때, 이런 구조에서는 게임 코드의 많은 부분이 잎 혹은 단말에 해당하는 하위 클래스에 있다. 즉, 많은 클래스에서 같은 객체, 즉 GameObject 상위 클래스에 접근할 수 있다. 이 점을 활용하면 GameObject를 상속받은 코드에서만 로그 객체에 접근할 수 있게 만들 수 있다.

 

- 이미 전역인 객체로부터 얻기

전역 상태를 모두 제거하기란 너무 이상적이다. 결국에는 Game이나 World같이 전체 게임 상태를 관리하는 전역 객체와 커플링 되어 있기 마련이다. 기본 전역 객체에 빌붙으면 전역 클래스 개수를 줄일 수 있다. 

'프로그래밍 패턴' 카테고리의 다른 글

상태  (0) 2022.02.18
프로토타입  (0) 2022.02.06
관찰자 패턴  (0) 2022.02.01
경량 패턴  (0) 2022.01.27
명령 패턴  (0) 2022.01.22

+ Recent posts