어댑터 패턴
Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 87개 중 71번째.
어댑터 패턴(Adapter Pattern)은 호환되지 않는 인터페이스들이 함께 작동할 수 있도록 해주는 구조적 디자인 패턴입니다. 이는 두 클래스 사이의 가교 역할을 하며, 한 클래스의 인터페이스를 클라이언트가 기대하는 인터페이스로 변환합니다. 유럽형 기기를 미국식 콘센트에 꽂을 수 있게 해주는 전원 어댑터와 같다고 생각하면 됩니다.
오디오 파일을 재생하는 기존 MediaPlayer 인터페이스가 있다고 가정해 봅시다. 하지만 비디오 재생을 위해 완전히 다른 인터페이스를 사용하는 서드파티 라이브러리를 통합해야 하는 상황입니다:
// 코드가 사용하는 기존 인터페이스
interface MediaPlayer {
void play(String filename);
}
// 호환되지 않는 인터페이스를 가진 서드파티 클래스
class AdvancedVideoPlayer {
public void playVideo(String filename) {
System.out.println("Playing video: " + filename);
}
}어댑터는 호환되지 않는 클래스를 감싸고 기대되는 인터페이스를 구현하여, 그들 사이의 호출을 변환합니다:
class VideoPlayerAdapter implements MediaPlayer {
private AdvancedVideoPlayer videoPlayer;
public VideoPlayerAdapter() {
this.videoPlayer = new AdvancedVideoPlayer();
}
public void play(String filename) {
videoPlayer.playVideo(filename);
}
}이제 클라이언트 코드는 익숙한 MediaPlayer 인터페이스를 통해 비디오 플레이어를 사용할 수 있습니다:
MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4"); // 출력: 비디오 재생 중: movie.mp4어댑터 패턴은 소스 코드를 수정하지 않고 레거시 코드나 서드파티 라이브러리를 통합할 때 매우 유용합니다. 클라이언트는 자신이 어댑터가 적용된 클래스와 작업하고 있다는 사실을 인지하지 못한 채, 단순히 자신이 알고 있는 인터페이스를 사용합니다.
챌린지
쉬움어댑터 패턴(Adapter Pattern)을 사용하여 온도 변환 시스템을 구축해 봅시다! 여러분은 섭씨 온도로 작동하는 기존 기상 애플리케이션을 가지고 있지만, 화씨로만 판독값을 출력하는 레거시 온도 센서를 통합해야 합니다. 두 시스템 중 어느 것도 수정하지 않고, 그 사이의 간극을 메워주는 어댑터를 만들 것입니다.
코드는 다음 네 개의 파일로 구성됩니다:
TemperatureProvider.java: 기상 애플리케이션이 기대하는 인터페이스를 정의합니다. 섭씨 온도를 나타내는 double 값을 반환하는getTemperatureCelsius()메서드를 선언해야 합니다.FahrenheitSensor.java: 호환되지 않는 인터페이스를 가진 레거시 센서를 나타냅니다. 생성자를 통해 설정되는 화씨 온도를 위한 private double 필드를 가진 클래스를 만듭니다. 저장된 화씨 값을 반환하는readFahrenheit()메서드가 있어야 합니다.SensorAdapter.java: 화씨 센서를 기상 애플리케이션과 호환되도록 만드는 어댑터를 만듭니다. 이 어댑터는TemperatureProvider를 구현하고FahrenheitSensor를 감싸야(wrap) 합니다.getTemperatureCelsius()가 호출되면 센서에서 값을 읽어와(fahrenheit - 32) * 5 / 9공식을 사용하여 섭씨로 변환해야 합니다.Main.java: 모든 것을 하나로 합칩니다! 하나의 입력으로 화씨 온도 판독값(double)을 받게 됩니다.입력값으로
FahrenheitSensor를 생성합니다. 그런 다음 이 센서를 감싸는SensorAdapter를 생성합니다. 기상 애플리케이션이 기대하는 인터페이스를 통해 이를 사용할 수 있음을 보여주기 위해 어댑터를TemperatureProvider변수에 저장합니다.결과를
Temperature: [celsius] C형식으로 출력하며, 여기서 celsius는 소수점 첫째 자리까지 형식화되어야 합니다.
하나의 입력을 받습니다: 화씨 온도 (double).
예를 들어, 입력이 98.6인 경우 출력은 다음과 같습니다:
Temperature: 37.0 CMain 클래스가 어떻게 TemperatureProvider 인터페이스만으로 완전히 작동하는지 주목하세요. 실제 온도가 배후에서 변환되고 있는 화씨 센서로부터 오고 있다는 사실을 Main 클래스는 전혀 알지 못합니다. 이것이 바로 어댑터 패턴의 우아함입니다!
치트 시트
어댑터 패턴(Adapter Pattern)은 호환되지 않는 인터페이스들이 함께 작동할 수 있도록 두 클래스 사이의 가교 역할을 하여, 하나의 인터페이스를 클라이언트가 기대하는 다른 인터페이스로 변환하는 구조적 디자인 패턴입니다.
주요 구성 요소
- Target Interface (타겟 인터페이스): 클라이언트가 사용하고자 하는 인터페이스
- Adaptee (어댑티): 호환되지 않는 인터페이스를 가진 기존 클래스
- Adapter (어댑터): 타겟 인터페이스를 구현하고 어댑티를 감싸서, 둘 사이의 호출을 변환하는 클래스
기본 구조
// 타겟 인터페이스
interface MediaPlayer {
void play(String filename);
}
// 어댑티 (호환되지 않는 클래스)
class AdvancedVideoPlayer {
public void playVideo(String filename) {
System.out.println("Playing video: " + filename);
}
}
// 어댑터
class VideoPlayerAdapter implements MediaPlayer {
private AdvancedVideoPlayer videoPlayer;
public VideoPlayerAdapter() {
this.videoPlayer = new AdvancedVideoPlayer();
}
public void play(String filename) {
videoPlayer.playVideo(filename);
}
}
// 클라이언트 사용법
MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4"); // 출력: Playing video: movie.mp4사용 시기
어댑터 패턴은 다음과 같은 경우에 유용합니다:
- 소스 코드를 수정하지 않고 레거시 코드나 서드파티 라이브러리를 통합할 때
- 호환되지 않는 인터페이스들이 함께 작동하도록 만들 때
- 클라이언트가 어댑팅된 구현체에 대해 알 필요가 없을 때
직접 해보기
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double fahrenheit = scanner.nextDouble();
// TODO: 입력값으로 FahrenheitSensor를 생성하세요
// TODO: 해당 센서를 감싸는 SensorAdapter를 생성하세요
// TODO: 어댑터를 TemperatureProvider 변수에 저장하세요
// TODO: 섭씨 온도를 가져와서 출력하세요
// 형식: 소수점 첫째 자리까지 "Temperature: [celsius] C"
// 힌트: 포맷팅을 위해 String.format("%.1f", value)를 사용하세요
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
4상속
상속의 기초 (extends)super 키워드메서드 오버라이딩 (@Override)생성자 체이닝Object 클래스단일 및 다중 레벨 상속다중 클래스 상속이 불가능한 이유요약 - 직원 계층 구조