Unity

[디자인 패턴] 구조 패턴 - 어댑터 패턴

CCS_Cheese 2025. 12. 1. 19:40

이번 게시글에서는 구조 패턴 중 하나인 어댑터 패턴에 대해서 이야기해보려고 합니다.

이번 어댑터 패턴을 공부하면서, 기존에 사용하고 있다고 생각했던 어댑터 패턴이 잘못되었다는 사실을 잘 알게 되는 계기가 된 것 같습니다. 아래 문제 상황을 가정해 보겠습니다.

 

 

문제 상황

소리를 재생하는 시스템 A와, B가 있다고 가정을 하였을 때, A의 소리를 재생하는 시점은 유니티 내부에서 특정 버튼을 클릭하였을 때 PlaySound()로 소리를 재생하고, B의 경우에 똑같은 시점 혹은 방법(유니티 내부에서 특정 버튼을 클릭하였을 때)으로 API를 통해 외부 디바이스에서 소리를 재생한다고 하는 경우를 문제 상황으로 제시해 보겠습니다. 이때 Client는 같은 방식으로 메서드를 실행하지만, 내부적으로 처리가 달라야 하는 경우 문제가 발생합니다. 

 

해결책

위 문제 상황의 제일 중요한 내용은, Client는 내부가 어떻게 구현되어 있는지 궁금하지 않고, 특정 상황에 맞추어 소리를 재생하고 싶은 것입니다. 따라서, 두 가지의 상황(Class)은 하나의 인터페이스를 상속받고, 내부 로직을 구현하여, 상황에 맞추어 변환(adapting) 한다고 이해하면 됩니다. 

 

클래스 다이어그램

전체 클래스 다이어그램

클래스 정의

Namespace Service_UnitySoundPlayer
public class UnitySoundPlayer : MonoBehaviour, ISoundPlayer
{
    public AudioSource AudioSource;
    public AudioClip Clip;

    private AudioClip FindClip(string soundId)
    {
        return Clip;
    }
    public void Play(string soundId)
    {
        Debug.Log("Play Unity PlayerSound");
        AudioSource.clip = FindClip(soundId);
        AudioSource.Play();
    }

    public void Stop()
    {
        AudioSource.Stop();
    }

    public void SetVolume(float volume)
    {
        AudioSource.volume = volume;
    }
}
첫 번째 ISoundPlayer를 상속받은 UnitySoundPlayer는 인게임 내에서 사운드를 재생하는 방식으로, Client는 사운드 재생을 인터페이스를 통해서만 진행하게 됩니다.

 

Namespace Server_ExternalSoundAPI
public class ExternalSoundAPI
{
    public void Connect(string ApiKey)
    {
        
    }

    public void RequestStop()
    {
        Debug.Log("Stop Sound API");
    }

    public void RequestPlay(string soundUrl, float volume)
    {
        Debug.Log($"Play Sound API{soundUrl} : {volume}");
    }

    public void SetMasterVolume(float volume)
    {
        Debug.Log($"Change Sound Volume{volume}");
    }
}

public class ExternalSoundAdapter : ISoundPlayer
{
    private ExternalSoundAPI _soundAPI;
    private string _soundUrl;
    private float _volume;

    public ExternalSoundAdapter(ExternalSoundAPI soundAPI)
    {
        _soundAPI = soundAPI;
    }
    public void Play(string soundId)
    { 
        Debug.Log("Play ExternalSound");
        _soundAPI.RequestPlay(FindSoundUrl(soundId), _volume);
    }

    private string FindSoundUrl(string soundId)
    {
        return _soundUrl;
    }

    public void Stop()
    {
        _soundAPI.RequestStop();        
    }

    public void SetVolume(float volume)
    {
        _volume = volume;
        _soundAPI.SetMasterVolume(volume);
    }
}

 

두 번째 ISoundPlayer를 상속받은 ExternalSoundAdapter는, 외부 라이브러리 또는, 내부 처리 방식이 다른 사운드 재생을 진행할 때, 해당 어댑터의 인터페이스를 통해서, Client는 사운드를 재생하게 됩니다. 

 

이때 A, B 둘 다 ISoundPlayer를 통해 재생을 진행하고, 볼륨을 조정할 수 있는데요. 내부적으로는 호환되지 않는 상황이지만, 어댑터를 통해 두 가지의 다른 사운드 재생을 진행할 수 있게 됩니다.

 

마치며

지금까지 학습해 본 여러 디자인 패턴들의 공통점이라고 하면, 결국 어떤 문제상황을 보다 효율적인 방식으로 해결하는지에 대한 초점이 맞추어져 있고, 여러 기능들을 사용하는 사용자(Client)에서는 최소한의 정보(인터페이스 및 파라미터) 만을 통해 기능을 실행하고, 최적화하는 것에 집중합니다. 그래서 공통 기능, 공통된 처리 방식을 인터페이스로 추상화하고, 이를 재정의하는 방식이 많이 보이게 되는 거 같습니다.

패턴 패키지

AdapterPattern.unitypackage
0.01MB