[디자인 패턴] #14_서비스 중개자 패턴, Service Locator
게임 디자인 패턴 중 "디커플링 패턴"에 대해 알아보겠습니다.
"게임 프로그래밍 패턴"의 16 항목, "서비스 중개자"에 해당하는 내용입니다.
Overview
- 개요
- 활용
- 예제
- 널 객체 디자인 패턴
- 데코레이터 패턴
#1. 개요
1. 정의
- 서비스 중개자 패턴은 디자인 패턴 중 하나로, 다수의 서비스나 컴포넌트가 서로 통신할 수 있도록 '중개자'를 도입하는 방법입니다. 따라서, '서비스 중개자' 패턴은 객체 간의 커플링을 낮추고, 유연성을 향상하며, 유지보수성을 증가시키는데 도움이 됩니다.
2. 왜 필요한가?
// 정적 서비스 클래스
AudioSystem::playSound(GUN_SHOT_SOUND);
// 싱글턴 디자인 패턴 활용
AudioSystem::getInstance()->playSound(GUN_SHOT_SOUND);
Details
- 서비스 클래스는 코드 전역에서 필요로 하기 때문에, 보통은 "정적 클래스"로 선언하거나, "싱글턴"을 활용합니다.
- 하지만, 이러한 방법들은 "강한 커플링"을 동반하며, 문제 발생시 수정이 용이하지 않겠죠!
- 따라서, 요청 하는 사람과 요청받는 사람이 모두 숨겨지며, 한 곳에서 편리하게 관리할 수 있도록 "서비스 중개자"를 활용합니다!
#2. 활용
1. 기본 구조
- "서비스" 추상 인터페이스 클래스
- "서비스" 인터페이스 클래스를 상속하는 "서비스 제공" 구체 클래스
- "서비스" 구체 클래스의 등록과 제공 과정에서 해당 클래스의 정확한 자료형을 숨겨주는 "서비스 중개자" 클래스
2. 사용 시점
- 전역 메커니즘 해결을 위해 객체를 인수로 넘겨주는 방식을 보통 채택합니다.
- 하지만, 이러한 방식은 코드 가독성을 떨어뜨리고, 복잡성을 증가시킵니다.
- "서비스 중개자" 패턴은 절제하는 것 또한 필요하지만, 유연한 싱글턴 패턴의 필요 요건을 충분히 만족시킵니다!
3. 주의 사항
- 서비스 클래스를 별도로 "서비스 중개자"에 등록해야 합니다.
- 서비스 요청자의 정보가 노출되지 않기 때문에, 접근성에 대한 고찰이 필요합니다.
#3. 예제
1. 서비스 인터페이스 클래스, AudioSystem
class AudioSystem
{
public:
virtual ~AudioSystem();
virtual void PlaySound(int SoundID) = 0;
virtual void StopSound(int SoundID) = 0;
};
2. 서비스 구체 클래스, ConsoleAudioSystem
class ConsoleAudioSystem : public AudioSystem
{
public:
virtual void PlaySound(int SoundID)
{
// 정의
}
virtual void StopSound(int SoundID)
{
// 정의
}
//...
};
3. 서비스 중개자, Locator
class Locator
{
public:
// 서비스 등록 메서드
static void registerAuioSystem(AudioSystem* _service) { service = _service; }
// 서비스 제공 메서드
static AudioSystem* getAudioSystem() { return service; }
private:
static AudioSystem* service;
};
Details
- 멤버 메서드와 데이터 멤버 모두 "static"으로 선언한 것이 주요합니다!
4. 서비스 등록 예제 코드
ConsoleAudioSystem* consoleAudio = new ConsoleAudioSystem();
Locator::registerAudioSystem(consoleAudio);
5. 서비스 요청 반환 예제 코드
AudioSystem* audio = Locator::getAudioSystem();
audio->PlaySound(GUN_SHOT_SOUND);
Details
- AudioSystem 클래스는 인터페이스 클래스로, 서비스 제공 구체 클래스의 자료형이 은닉됩니다.
- 따라서, 요청 받는 곳과 서비스 구체 클래스 간 디커플링이 활성화됩니다.
#4. 널 객체 디자인 패턴
1. 단점
- 요청 받은 서비스 객체가 아직 "서비스 중개자"에 미등록된 상태라면 NULL 객체가 반환되는 문제가 발생합니다.
- 이때, 우리는 "널 객체 디자인 패턴"을 활용합니다.
2. NULL 객체 디자인 패턴
// 1. 널 객체
class NullAudioSystem : public AudioSystem
{
public:
virtual void PlaySound(int SoundID)
{
// 아무런 기능을 제공하지 않습니다.
}
virtual void StopSound(int SoundID)
{
// 아무런 기능을 제공하지 않습니다.
}
};
// 2. 서비스 중개자 클래스
class Locator
{
public:
static void Init()
{
service = &nullService;
}
static void getAudioSystem() { return *service; }
static void registerAudioSystem(AudioSystem* service_)
{
if (service_ == NULL)
{
service = &nullService;
}
else
{
service = service_;
}
}
private:
static AudioSystem* service;
static NullAudioSystem* nullService;
};
Details
- "NullAudioSystem" 클래스는 AudioSystem 인터페이스 클래스를 상속받지만, 아무 기능도 제공하지 않습니다.
- Locator 클래스는 이러한 "NullAudioSystem" 객체 포인터를 멤버로 두어, 상세 기능을 제공하는 서비스 구체 클래스의 객체가 미등록 상태에선 항상 NullAudioSystem 객체 포인터를 반환하도록 합니다.
- 이로써, 서비스 중개자 클래스는 항상 유효한 객체를 반환합니다.
#5. 데코레이터 패턴
1. 개념
- Decorator란, 어떠한 동작의 생명 주기 동안, 백그라운드에서 함께 수행되는 동작입니다.
- 기생하는 특정 동작의 수행이 모두 끝나면, 함께 끝납니다.
2. LoggedAudio, 데코레이션 클래스
class LoggedAudio : public AudioSystem
{
public:
LoggedAudio(AudioSystem& _wrappedByDecorator) : wrappedByDecorator(_wrappedByDecorator){}
virtual void PlaySound(int SoundID)
{
// 데코레이터 클래스의 로깅 메서드 호출
log("사운드 재생");
// 감싼 서비스 클래스의 멤버 메서드를 호출
wrappedByDecorator.PlaySound(SoundID);
}
virtual void StopSound(int SoundID)
{
// 데코레이터 클래스의 로깅 메서드 호출
log("사운드 정지");
// 감싼 서비스 클래스의 멤버 메서드를 호출
wrappedByDecorator.StopSound(SoundID);
}
private:
// 로깅 메서드
void log(string str);
AudioSystem& wrappedByDecorator;
};
4. EnableLogging(), 데코레이터 클래스 생성
void EnableLogging()
{
// 서비스 중개자에 등록된 기존의 서비스 클래스를 LoggedAudio 데코레이터로 감싸줍니다.
AudioSystem* service = new LoggedAudio(Locator::getAudioSystem());
// 기존 서비스 + 데코레이터 객체로 서비스 중개자에 재등록
Locator::registerAudioSystem(service);
}
'언어 > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴]#16_공간 분할 패턴, Spatial Partition (0) | 2022.11.10 |
---|---|
[디자인 패턴]#15_객체 풀, Object Pooling (0) | 2022.11.02 |
[디자인 패턴]#13_이벤트 큐, Event Queue (0) | 2022.10.02 |
[디자인 패턴]#12_Component Pattern, 컴포넌트 패턴 (0) | 2022.09.25 |
[디자인 패턴]#11_타입 객체, Type Object (0) | 2022.09.19 |