Menu
Coddy logo textTech

어댑터 패턴

Coddy Dart 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 110개 중 95번째.

어댑터 패턴(Adapter pattern)은 호환되지 않는 인터페이스들이 함께 작동할 수 있도록 해줍니다. 이 패턴은 서로 협력할 수 없는 두 클래스 사이에서 다리 역할을 하며, 기존 클래스를 클라이언트가 기대하는 새로운 인터페이스로 감쌉니다. 이는 서드파티 라이브러리나 레거시 코드를 애플리케이션에 통합할 때 특히 유용합니다.

JSON 데이터로 작동하는 시스템이 있지만, XML만 출력하는 구성 요소를 통합해야 한다고 가정해 봅시다. 두 클래스 중 어느 것도 수정하는 대신, 그 사이에서 번역 역할을 하는 어댑터를 만듭니다:

// 시스템이 기대하는 기존 인터페이스
abstract class JsonData {
  String getJson();
}

// 호환되지 않는 인터페이스를 가진 레거시 클래스
class XmlService {
  String getXml() {
    return '<data><name>John</name></data>';
  }
}

// 어댑터는 XmlService를 JsonData와 호환되도록 만듭니다.
class XmlToJsonAdapter implements JsonData {
  final XmlService _xmlService;

  XmlToJsonAdapter(this._xmlService);

  @override
  String getJson() {
    String xml = _xmlService.getXml();
    // XML을 JSON으로 변환 (단순화됨)
    return '{"name": "John"}';
  }
}

void main() {
  var xmlService = XmlService();
  var adapter = XmlToJsonAdapter(xmlService);

  print(adapter.getJson());  // {"name": "John"}
}

XmlToJsonAdapterXmlService를 래핑하고 JsonData 인터페이스를 구현합니다. 클라이언트 코드는 데이터가 원래 XML 소스에서 왔다는 사실을 모른 채 익숙한 JsonData 인터페이스를 사용하여 작동합니다. 어댑터는 내부적으로 모든 변환을 처리합니다.

기존 클래스를 사용하고 싶지만 해당 인터페이스가 필요한 인터페이스와 일치하지 않을 때, 또는 호환되지 않는 인터페이스를 가진 클래스들과 협력할 수 있는 재사용 가능한 클래스를 만들고자 할 때 어댑터 패턴을 사용하세요.

challenge icon

챌린지

쉬움

미디어 플레이어 어댑터 시스템을 구축해 봅시다! 모든 미디어 소스가 표준 인터페이스를 통해 오디오를 제공할 것으로 기대하는 현대적인 오디오 시스템이 있다고 가정해 보세요. 하지만 여러분은 소리를 내는 방식이 완전히 다른 구식 바이닐 레코드 플레이어를 통합해야 합니다.

어댑터 패턴 구현을 정리하기 위해 두 개의 파일을 생성할 것입니다:

  • media_adapter.dart: 이 파일에는 인터페이스와 어댑터 클래스가 포함됩니다. 현대적인 시스템이 기대하는 추상 클래스 AudioSource를 정의하세요. 이 클래스는 오디오 출력을 문자열로 반환하는 String playAudio() 메서드를 가져야 합니다. 그런 다음 레거시 장비를 나타내는 VinylPlayer 클래스를 만드세요. 이 클래스는 trackName 필드와 *crackle* Playing vinyl: [trackName] *crackle*를 반환하는 String spinRecord() 메서드를 가집니다. 마지막으로, AudioSource를 구현하고 VinylPlayer를 감싸는 VinylAdapter를 구축하세요. 어댑터의 playAudio() 메서드는 바이닐 플레이어의 spinRecord() 메서드를 호출하고 그 결과를 반환하여 호환되지 않는 인터페이스를 연결해야 합니다.
  • main.dart: 미디어 어댑터 파일을 임포트하고, 어댑터가 현대적인 AudioSource 인터페이스를 기대하는 코드와 이전 바이닐 플레이어를 어떻게 함께 작동시키는지 보여주세요. 트랙 이름이 Bohemian RhapsodyVinylPlayer를 생성하세요. 그런 다음 이 바이닐 플레이어를 감싸는 VinylAdapter를 생성합니다. 어댑터가 기대하는 인터페이스를 준수함을 증명하기 위해 AudioSource 타입의 변수에 어댑터를 저장하세요. 오디오 소스에서 playAudio()를 호출하고 결과를 출력하세요.

이 패턴의 장점은 메인 코드가 표준 AudioSource 인터페이스와 함께 작동한다는 것입니다. 배후에서 오래된 바이닐 플레이어가 실제 작업을 수행하고 있다는 사실을 알 필요가 없습니다!

예상 출력:

*crackle* Playing vinyl: Bohemian Rhapsody *crackle*

치트 시트

어댑터 패턴(Adapter pattern)은 서로 호환되지 않는 인터페이스를 가진 두 클래스가 함께 작동할 수 있도록 연결해 주는 브리지 역할을 합니다.

어댑터는 기존 클래스를 클라이언트가 기대하는 새로운 인터페이스로 감싸며, 이는 서드파티 라이브러리나 레거시 코드를 통합할 때 유용합니다.

기본 구조

// 클라이언트가 기대하는 타겟 인터페이스
abstract class JsonData {
  String getJson();
}

// 호환되지 않는 인터페이스를 가진 기존 클래스
class XmlService {
  String getXml() {
    return '<data><name>John</name></data>';
  }
}

// 어댑터는 XmlService를 JsonData와 호환되도록 만듭니다
class XmlToJsonAdapter implements JsonData {
  final XmlService _xmlService;

  XmlToJsonAdapter(this._xmlService);

  @override
  String getJson() {
    String xml = _xmlService.getXml();
    // XML을 JSON으로 변환
    return '{"name": "John"}';
  }
}

사용법

void main() {
  var xmlService = XmlService();
  var adapter = XmlToJsonAdapter(xmlService);

  print(adapter.getJson());  // {"name": "John"}
}

어댑터는 호환되지 않는 클래스를 감싸는 동시에 타겟 인터페이스를 구현합니다. 클라이언트 코드는 내부 구현을 알 필요 없이 익숙한 인터페이스를 통해 작업할 수 있습니다.

사용 시기: 기존 클래스를 사용해야 하지만 그 인터페이스가 필요한 것과 일치하지 않을 때, 또는 호환되지 않는 인터페이스를 가진 클래스들과 협력하는 재사용 가능한 클래스를 만들 때 사용합니다.

직접 해보기

import 'media_adapter.dart';

void main() {
  // TODO: 트랙 이름이 "Bohemian Rhapsody"인 VinylPlayer를 생성하세요
  
  // TODO: vinyl player를 감싸는 VinylAdapter를 생성하세요
  
  // TODO: 어댑터를 AudioSource 타입의 변수에 저장하세요
  
  // TODO: audio source에서 playAudio()를 호출하고 결과를 출력하세요
  
}
quiz icon실력 점검

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

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