Design Pattern/구조 디자인 패턴

어댑터 패턴(Adapter Pattern)이란?

슝슝이입니다 2024. 4. 14. 14:55
반응형

 어댑터 패턴은 서로 호환되지 않는 인터페이스를 가진 클래스들이 함께 작동할 수 있도록 하는 구조적 디자인 패턴입니다. 이 패턴은 기존 시스템과 새 시스템, 라이브러리 또는 애플리케이션 간의 인터페이스 차이를 극복하기 위해 중간에 어댑터 클래스를 사용하여 호환성 문제를 해결합니다.

1. 어댑터 패턴은 주로 어디에 쓰이나?

어댑터 패턴은 주로 다음과 같은 상황에서 사용됩니다:

  • 기존 시스템과 새 시스템 간의 통합: 다른 인터페이스를 가진 두 시스템이 서로 통신해야 할 때.
  • 라이브러리 교체: 기존 코드를 변경하지 않고 새로운 라이브러리 또는 도구를 기존 시스템에 통합할 때.
  • 플랫폼 독립적인 코드 작성: 다양한 플랫폼에서 작동하는 코드를 개발할 때.

2. Java와 Go로 어댑터 패턴을 구현할 때의 차이점

  • Java: Java에서는 클래스 상속 또는 인터페이스를 사용하여 어댑터 패턴을 구현할 수 있습니다. 일반적으로 인터페이스를 통한 구현이 선호됩니다.
  • Go: Go는 상속을 지원하지 않으며, 인터페이스만을 사용하여 어댑터 패턴을 구현합니다. 구조체와 인터페이스를 함께 사용하여 동일한 기능을 제공합니다.

3. 어댑터 패턴 예시

Java 예시:

// Target Interface
interface MediaPlayer {
    void play(String audioType, String fileName);
}

// Adaptee
class AdvancedMediaPlayer {
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file. Name: " + fileName);
    }
    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file. Name: " + fileName);
    }
}

// Adapter
class MediaAdapter implements MediaPlayer {
    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer = new AdvancedMediaPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer = new AdvancedMediaPlayer();
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer.playVlc(fileName);
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer.playMp4(fileName);
        }
    }
}

// Client
class AudioPlayer implements MediaPlayer {
    MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid media. " + audioType + " format not supported");
        }
    }
}

public class Main {
    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.play("avi", "mind me.avi");
    }
}
  1. 인터페이스 정의:
    • MediaPlayer 인터페이스는 play 메소드를 정의합니다. 이 메소드는 음악 파일의 타입과 파일 이름을 매개변수로 받습니다.
  2. Adaptee 클래스:
    • AdvancedMediaPlayer 클래스는 VLC와 MP4 파일을 재생하는 기능을 가집니다. 이 클래스는 playVlc와 playMp4 메소드를 포함하여 각각의 파일 타입에 대한 재생 로직을 구현합니다.
  3. Adapter 클래스:
    • MediaAdapter 클래스는 MediaPlayer 인터페이스를 구현하며, AdvancedMediaPlayer 객체를 사용하여 다양한 파일 형식을 재생합니다. 이 어댑터는 audioType에 따라 적절한 메소드를 호출하여 파일을 재생합니다.
  4. Client 클래스:
    • AudioPlayer 클래스는 기본적으로 MediaPlayer 인터페이스를 구현합니다. 특정 파일 타입을 재생할 때 MediaAdapter를 사용하여 필요한 형식에 맞게 파일을 재생할 수 있습니다. 지원하지 않는 파일 형식에 대해선 오류 메시지를 출력합니다.

 

Go 예시:

package main

import "fmt"

// Target interface
type MediaPlayer interface {
    Play(audioType, fileName string)
}

// Adaptee
type AdvancedMediaPlayer struct{}

func (amp *AdvancedMediaPlayer) PlayVLC(fileName string) {
    fmt.Println("Playing vlc file. Name:", fileName)
}

func (amp *AdvancedMediaPlayer) PlayMP4(fileName string) {
    fmt.Println("Playing mp4 file. Name:", fileName)
}

// Adapter
type MediaAdapter struct {
    advancedMusicPlayer *AdvancedMediaPlayer
}

func NewMediaAdapter(audioType string) *MediaAdapter {
    adapter := &MediaAdapter{
        advancedMusicPlayer: &AdvancedMediaPlayer{},
    }
    return adapter
}

func (adapter *MediaAdapter) Play(audioType, fileName string) {
    if audioType == "vlc" {
        adapter.advancedMusicPlayer.PlayVLC(fileName)
    } else if audioType == "mp4" {
        adapter.advancedMusicPlayer.PlayMP4(fileName)
    }
}

// Client
type AudioPlayer struct{}

func (player *AudioPlayer) Play(audioType, fileName string) {
    if audioType == "vlc" || audioType == "mp4" {
        adapter := NewMediaAdapter(audioType)
        adapter.Play(audioType, fileName)
    } else {
        fmt.Println("Invalid media. ", audioType, " format not supported")
    }
}

func main() {
    player := &AudioPlayer{}
    player.Play("mp4", "myVideo.mp4")
    player.Play("vlc", "myMusic.vlc")
}
  1. 인터페이스 정의:
    • Go에서는 MediaPlayer 인터페이스를 정의하여 Play 메소드를 포함합니다. 이 메소드는 오디오 타입과 파일 이름을 매개변수로 받습니다.
  2. Adaptee 구조체:
    • AdvancedMediaPlayer 구조체는 PlayVLC와 PlayMP4 메소드를 통해 VLC와 MP4 파일 재생을 지원합니다.
  3. Adapter 구조체:
    • MediaAdapter 구조체는 MediaPlayer 인터페이스를 구현합니다. 이 구조체는 AdvancedMediaPlayer를 포함하며, 생성자 NewMediaAdapter를 통해 초기화됩니다. Play 메소드를 통해 파일 타입에 맞는 재생 함수를 호출합니다.
  4. Client 구조체:
    • AudioPlayer 구조체는 MediaPlayer 인터페이스를 구현하고, 지원하는 파일 형식을 확인하여 MediaAdapter를 사용해 파일을 재생합니다. 지원하지 않는 형식일 경우 오류 메시지를 출력합니다.

 이러한 방식으로, Java와 Go 둘 다에서 어댑터 패턴을 활용하여 다양한 오디오 파일 타입을 처리하는 능력을 소프트웨어에 통합할 수 있습니다. 어댑터는 기존 코드와 새로운 코드 사이에서 호환성을 제공하는 중간자 역할을 수행하여 코드의 재사용성과 유지보수성을 높여줍니다.

4. 어댑터 패턴의 장단점

장점:

  • 기존 코드 변경 없이 새로운 인터페이스를 소프트웨어에 통합할 수 있습니다.
  • 기존 클래스가 예상하지 못한 환경에서 재사용될 수 있습니다.

단점:

  • 전체 시스템의 복잡성이 증가할 수 있습니다.
  • 때때로 많은 추가 객체의 생성이 필요하여, 시스템의 오버헤드가 늘어날 수 있습니다.

 

반응형