본문 바로가기

취업과 기본기 튼튼/빽 투더 기본기

[디자인 패턴 6편] 구조 패턴, 어댑터(Adapter)

1. 개념

어댑터 패턴은 서로 다른 인터페이스를 가진 두 클래스를
어댑터 클래스로 인터페이스를 통일 시켜 사용하는 방법이다.

1.1. 장점

  • 기존 클라이언트 단의 코드 수정 최소화.
  • 클라이언트는 연동부분을 몰라도, 새로운 코드의 기능을 일관되게 사용가능.

1.2. 단점

  • 어댑터 클래스에서 통일 시켜주는 부분을 하나씩 구현해야 함.

1.3. 활용 상황

  • 기존의 코드에 새로운 코드(써드파티 라이브러리 등)을 연동하여 사용하고 싶은데, 두 코드의 인터페이스가 달라, 이를 하나로 통일하여 사용하고 싶을 때.
  • 아래 예의 경우, 기존의 클라이언트 단 코드에 맞춰 통일함.

2. 구조

출처 : https://www.tutorialspoint.com/design_pattern/adapter_pattern.htm

3. 코드

3.1. 적용 전/후 모습

먼저 사전에 mp3 포맷만 지원하는 AudioPlayer 가 정의되어있고,
메인에서는 다음과 같이 사용했다고 해보자.

public class AdapterPatternDemo {
  public static void main(String[] args) {
    AudioPlayer audioPlayer = new AudioPlayer();

    audioPlayer.play("mp3", "beyond the horizon.mp3");
  }
}

그런데, 앞으로는 mp3 뿐만 아니라 mp4vlc 도 지원해야 한다는 요구사항이 들어왔다.
이 포맷들을 실행시킬 수 있는 Mp4PlayerVlcPlayer 가 이미 구현되어 있다고 한다.
이미 구현되어 있는 두 개의 객체를 어떻게 하면 연동시켜서 사용할 수 있을까?

어댑터 패턴을 사용하면 클라이언트단에서는 단순히 다음만 추가해주면 된다.

public class AdapterPatternDemo {
  public static void main(String[] args) {
    AudioPlayer audioPlayer = new AudioPlayer();

    audioPlayer.play("mp3", "beyond the horizon.mp3");
    audioPlayer.play("mp4", "alone.mp4");
    audioPlayer.play("vlc", "far far away.vlc");
  }
}

AudioPlayer 의 쓰이는 모습이 전혀 달라진게 없이도, 추가 포맷을 지원하고 있다.
내부적으로 어떻게 되어있나 하나씩 살펴보자.

3.2. 적용 과정

먼저 기존의 AudioPlayer 의 코드는 다음과 같았다.

public interface MediaPlayer {
   public void play(String audioType, String fileName);
}
public class AudioPlayer implements MediaPlayer {

  @Override
  public void play(String audioType, String fileName) {
    if(audioType.equalsIgnoreCase("mp3"))
      System.out.println("Playing mp3 file. Name: " + fileName);    
  }
}

현재로썬 그냥 mp3 만 지원되고 있다.
한편, 우리가 가져다 쓰려는 VlcPlayerMp4Player 의 모습은 다음과 같다.

public interface AdvancedMediaPlayer {    
  public void playVlc(String fileName);
  public void playMp4(String fileName);
}
public class VlcPlayer implements AdvancedMediaPlayer{
  @Override
  public void playVlc(String fileName) {
    System.out.println("Playing vlc file. Name: "+ fileName);        
  }

  @Override
  public void playMp4(String fileName) {
  }
}
public class Mp4Player implements AdvancedMediaPlayer{

  @Override
  public void playVlc(String fileName) {
    //do nothing
  }

  @Override
  public void playMp4(String fileName) {
    System.out.println("Playing mp4 file. Name: "+ fileName);        
  }
}

vlcmp3 확장자의 추가 지원을 위해 AudioPlayer 에 다음을 추가한다.

public class AudioPlayer implements MediaPlayer {

  @Override
  public void play(String audioType, String fileName) {

    if(audioType.equalsIgnoreCase("mp3")){
      System.out.println("Playing mp3 file. Name: " + fileName);
    }

    // 'vlc' 나 'mp4' 포맷은 MediaAdapter 로 처리한다.
    else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){
      mediaAdapter = new MediaAdapter(audioType);
      mediaAdapter.play(audioType, fileName);
    }
  }
}

MediaAdapter 가 등장했다. 이는 다음과 같이 정의한다.

public class MediaAdapter implements MediaPlayer {

  AdvancedMediaPlayer advancedMusicPlayer;

  public MediaAdapter(String audioType){

    // 생성 시, vlc 인지 mp4 인지 구분.
    if(audioType.equalsIgnoreCase("vlc") ){
      advancedMusicPlayer = new VlcPlayer();            

    }else if (audioType.equalsIgnoreCase("mp4")){
      advancedMusicPlayer = new Mp4Player();
    }    
  }

  @Override
  public void play(String audioType, String fileName) {

    // play 호출 시, 구체적인 각 플레이어의 메쏘드를 호출.
    if(audioType.equalsIgnoreCase("vlc")){
      advancedMusicPlayer.playVlc(fileName);
    }
    else if(audioType.equalsIgnoreCase("mp4")){
      advancedMusicPlayer.playMp4(fileName);
    }
  }
}

자 이제 클라이언트 단을 다시 보자.
다음과 같이 잘 작동하게 된다.

Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc

4. 참고