728x90

1. 서비스 중개자 패턴 정의

  • 서비스를 구현한 구체 클래스는 숨긴 채로 어디에서나 서비스에 접근할 수 있게 하는 패턴

 

2. 서비스 중개자 패턴 구현

// 오디오 서비스가 제공할 인터페이스.
class Audio
{
public:
	virtual ~Audio() {}
	virtual void PlaySound( int InSoundId ) = 0;
	virtual void StopSound( int InSoundId ) = 0;
	virtual void StopAllSounds() = 0;
};

// 오디오 서비스
class ConsoleAudio : public Audio
{
	virtual void PlaySound( int InSoundId )
	{
		// 콘솔의 오디오 API를 이용해 사운드를 출력한다.
	}

	virtual void StopSound( int InSoundId )
	{
		// 콘솔의 오디오 API를 이용해 사운드를 중지한다.
	}

	virtual void StopAllSounds()
	{
		// 콘솔의 오디오 API를 이용해 모든 사운드를 중지한다.
	}
};

// 서비스 중개자
class Locator
{
private:
	static Audio* Service;

public:
	// 중개 역할을 하는 GetAudio() 함수
	static Audio* GetAudio() { return Service; }

	static void Provide( Audio* InService ) { Service = InService; }
};

int main()
{
	// Locator가 오디오 서비스를 등록하는 방법
	ConsoleAudio* consoleAudio = new ConsoleAudio();
	Locator::Provide( consoleAudio );
	
	// 중개자를 통해 Audio 인스턴스를 반환받아 사용
	Audio* audio = Locator::GetAudio();
	audio->PlaySound( SOUND_1 );
}
  • 정적 함수인 GetAudio()가 중개 역할을 한다.
  • 어디에서나 부를 수 있는 GetAudio() 함수는 Audio 인스턴스를 반환해 사용할 수 있도록 한다.
  • PlaySound()를 호출하는 쪽에서는 Audio라는 추상 인터페이스만 알 뿐, ConsoleAudio라는 구체 클래스에 대해서는 전혀 모른다는 게 핵심이다.
  • Locator 클래스와 서비스 제공자의 구체 클래스와는 커플링 되지 않는다.
  • 어떤 구체 클래스가 실제로 사용되는지는 서비스를 제공하는 초기화 코드에서만 알 수 있다.

 

3. 널 객체 패턴 구현

// 널 객체 디자인 패턴
class NullAudio : public Audio
{
public:
	virtual void PlaySound( int InSoundId ) { /*아무것도 하지 않는다.*/ }

	virtual void StopSound( int InSoundId ) { /*아무것도 하지 않는다.*/ }

	virtual void StopAllSounds() { /*아무것도 하지 않는다.*/ }
};

// 중개자
class Locator
{
private:
	static Audio* Service;
	static NullAudio NullService;

public:
	// 널 서비스로 초기화
	static void Initialize()
	{
		Service = &NullService;
	}
	// 중개 역할을 하는 GetAudio() 함수
	static Audio* GetAudio() { return Service; }

	static void Provide( Audio* InService ) 
	{
		if ( Service == nullptr )
		{
			// 널 서비스로 돌려놓는다.
			Service = &NullService;
		}
		else
		{
			Service = InService;
		}
		Service = InService; 
	}
};
  • 서비스 제공자가 서비스를 등록하기 전에 서비스를 사용하려고 하면, NULL을 반환해 크래시 위험이 있다.
  • 객체를 찾지 못하거나 만들지 못해 NULL을 반환해야 할 때, 대신 같은 인터페이스를 구현한 특수한 객체를 반환한다.
  • 아무런 구현이 되어 있지 않지만, 객체를 받는 쪽에서는 '진짜' 객체를 받은 것처럼 안전하게 작업을 할 수 있다.

 

4. 데커레이터 패턴 구현

// 데커레이터 패턴
class LoggedAudio : public Audio
{
private:
    Audio& Wrapped;

private:
    void Log( cosnt std::string& InMessage ) 
    {
        // 로그
    }

public:
    LoggedAudio( AUdio& InWrapped ) : Wrapped( InWrapped ) {}

    virtual void PlaySound( int InSoundId )
    {
        log ( "사운드 출력" );
        Wrapped.PlaySound( InSoundId );
    }

	virtual void StopSound( int InSoundId )
    {
        log ( "사운드 중지" );
        Wrapped.StopSound( InSoundId );
    }

	virtual void StopAllSounds()
    {
        log ( "모든 사운드 중지" );
        Wrapped.StopAllSounds();
    }
};

int main()
{   
    // 기존 서비스를 데커레이트한다.
    Audio* service = new LoggedAudio( Locator::GetAudio() );

    // 이 값으로 바꿔치기한다.
    Locator::Provide( service );
}
  • 조건적으로 로그를 남기고 싶은 시스템이 서비스로 노출되어 있다면 데커레이터 패턴 ( 장식자 패턴 )으로 해결이 가능하다.
  • LoggedAudio 클래스는 다른 오디오 서비스 제공자를 래핑하는 동시에, 같은 인터페이스를 상속받는다.
  • 실제 기능 요청은 내부에 있는 서비스에 전달하고, 대신 사운드가 호출될 때마다 로그를 남긴다.

 

반응형

+ Recent posts