Menu
Coddy logo textTech

어댑터 패턴

Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 70개 중 57번째.

어댑터 패턴(Adapter pattern)은 호환되지 않는 인터페이스들이 함께 작동할 수 있도록 해주는 구조적 패턴입니다. 이는 두 클래스 사이에서 브리지 역할을 하여, 한 클래스의 인터페이스를 클라이언트가 기대하는 인터페이스로 변환합니다. 유럽형 기기를 미국식 콘센트에 꽂을 수 있게 해주는 전원 어댑터와 같다고 생각하면 됩니다.

이 패턴은 기존 클래스를 사용하고 싶지만 그 인터페이스가 코드에서 필요로 하는 것과 일치하지 않을 때 특히 유용합니다. (직접 제어할 수 없을 수도 있는) 원본 클래스를 수정하는 대신, 이를 감싸는 어댑터를 생성합니다:

// 호환되지 않는 인터페이스를 가진 기존 클래스
public class OldPrinter
{
    public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}

// 코드가 기대하는 인터페이스
public interface IPrinter
{
    void Print(string text);
}

// 어댑터가 간극을 메웁니다
public class PrinterAdapter : IPrinter
{
    private OldPrinter _oldPrinter;
    
    public PrinterAdapter(OldPrinter oldPrinter)
    {
        _oldPrinter = oldPrinter;
    }
    
    public void Print(string text)
    {
        _oldPrinter.PrintMessage(text);  // 기존 인터페이스로 위임합니다
    }
}

이제 당신의 코드는 기대하는 인터페이스를 통해 기존 프린터와 함께 작동할 수 있습니다:

OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);

printer.Print("Hello");  // 기존: Hello

어댑터 패턴은 서드파티 라이브러리나 레거시 코드를 애플리케이션에 통합할 때 매우 유용합니다. 클라이언트 코드는 IPrinter 인터페이스와 함께 작동하며, 오래되고 호환되지 않는 클래스가 배후에서 실제 작업을 수행하고 있다는 사실을 전혀 알지 못합니다.

challenge icon

챌린지

쉬움

어댑터(Adapter) 패턴을 사용하여 미디어 플레이어 시스템을 구축해 보겠습니다. MP3 파일을 재생하는 기존 오디오 라이브러리가 있지만, 애플리케이션에는 다양한 미디어 유형을 처리할 수 있는 통합 인터페이스가 필요합니다. 이 간극을 메우기 위해 어댑터를 만들어 최신 미디어 플레이어가 레거시 오디오 라이브러리와 원활하게 작동하도록 할 것입니다.

코드는 다음 세 개의 파일로 구성됩니다:

  • LegacyAudio.cs: 수정할 수 없는 기존 라이브러리를 나타냅니다. MediaSystem 네임스페이스에 LegacyAudioPlayer 클래스를 생성하고, "Playing MP3: {filename}" 문자열을 반환하는 PlayMp3(string filename) 메서드를 만듭니다. 이것이 호환되지 않는 인터페이스를 가진 클래스인 "adaptee"입니다.
  • MediaAdapter.cs: 동일한 네임스페이스에 인터페이스와 어댑터를 생성합니다. 문자열을 반환하는 Play(string filename) 메서드가 있는 IMediaPlayer 인터페이스를 정의합니다. 그런 다음 IMediaPlayer를 구현하는 MediaPlayerAdapter 클래스를 만듭니다. 어댑터는 (생성자를 통해 전달된) LegacyAudioPlayer 인스턴스를 래핑하고, Play 호출을 레거시 플레이어의 PlayMp3 메서드로 변환해야 합니다.
  • Program.cs: 레거시 플레이어를 생성하고, 이를 어댑터로 래핑한 다음, IMediaPlayer 인터페이스를 통해 어댑터를 사용하여 모든 것을 하나로 묶습니다. 이는 클라이언트 코드가 최신 인터페이스로 작업하는 동안 어댑터가 배후에서 레거시 시스템과의 통신을 어떻게 처리하는지 보여줍니다.

하나의 입력을 받게 됩니다:

  • 재생할 파일 이름 (예: song.mp3)

LegacyAudioPlayer를 생성하고, 이를 MediaPlayerAdapter로 래핑한 다음, IMediaPlayer 인터페이스를 사용하여 어댑터의 Play를 호출하고 결과를 출력하세요.

예를 들어, 입력이 favorite_track.mp3라면 출력은 다음과 같아야 합니다:

Playing MP3: favorite_track.mp3

이 패턴의 장점은 메인 코드가 IMediaPlayer와만 상호작용한다는 것입니다. 실제 작업은 레거시 오디오 라이브러리가 수행하고 있다는 사실을 전혀 알 필요가 없습니다. 나중에 다른 레거시 라이브러리를 지원해야 하는 경우, 클라이언트 코드를 변경하지 않고 새 어댑터만 생성하면 됩니다!

치트 시트

어댑터 패턴(Adapter pattern)은 두 클래스 사이의 가교 역할을 하여 호환되지 않는 인터페이스들이 함께 작동할 수 있도록 하는 구조적 패턴입니다.

이 패턴은 한 클래스의 인터페이스를 클라이언트가 기대하는 인터페이스로 변환하며, 기존 클래스를 사용하고 싶지만 그 인터페이스가 코드의 요구 사항과 일치하지 않을 때 유용합니다.

기본 구조:

// 호환되지 않는 인터페이스를 가진 기존 클래스 (Adaptee)
public class OldPrinter
{
    public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}

// 코드가 기대하는 타겟 인터페이스
public interface IPrinter
{
    void Print(string text);
}

// 어댑터가 간극을 메웁니다
public class PrinterAdapter : IPrinter
{
    private OldPrinter _oldPrinter;
    
    public PrinterAdapter(OldPrinter oldPrinter)
    {
        _oldPrinter = oldPrinter;
    }
    
    public void Print(string text)
    {
        _oldPrinter.PrintMessage(text);  // 기존 인터페이스로 위임합니다
    }
}

사용법:

OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);

printer.Print("Hello");  // Old: Hello

클라이언트 코드는 타겟 인터페이스와 함께 작동하며, 배후에서 호환되지 않는 클래스가 실제 작업을 수행하고 있다는 사실을 알지 못합니다. 이 패턴은 서드파티 라이브러리나 레거시 코드를 통합할 때 특히 유용합니다.

직접 해보기

using System;
using MediaSystem;

class Program
{
    public static void Main(string[] args)
    {
        // 파일 이름 입력을 읽습니다
        string filename = Console.ReadLine();
        
        // TODO: `LegacyAudioPlayer` 인스턴스 생성
        
        // TODO: 이를 `MediaPlayerAdapter`로 래핑합니다
        
        // TODO: `IMediaPlayer` 인터페이스를 통해 어댑터를 사용합니다
        // 그리고 파일 이름으로 `Play`를 호출한 결과를 출력합니다
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨