[ Programming > Design Pattern ]
[디자인 패턴] 어댑터 패턴
어댑터 패턴(Adapter pattern)
1. 어댑터 패턴이란
어댑터 패턴은 호환되지 않는 인터페이스를 가진 클래스들이 함께 작동할 수 있도록 해주는 구조적 디자인 패턴이다. 실생활에서 전원 어댑터가 서로 다른 플러그 규격을 연결해주는 것처럼, 소프트웨어에서도 서로 다른 인터페이스를 가진 객체들을 연결해주는 역할을 한다.
어댑터 패턴은 주로 다음과 같은 경우 유용하다.
기존 코드를 수정할 수 없는 상황에서 새로운 라이브러리나 API를 사용해야 할 때
서드파티 라이브러리의 인터페이스가 기존 시스템과 맞지 않을 때
레거시 시스템과 새로운 시스템을 통합해야 할 때
데이터 형식이나 메서드 시그니처가 다른 클래스들을 통합해야 할 때
2. 어댑터 패턴 구조
Target: 클라이언트가 사용하는 인터페이스
Adapter: Target 인터페이스를 구현하고 Adaptee를 감싸는 클래스
Adaptee: 어댑터가 감싸는 기존 클래스 (호환되지 않는 인터페이스를 가짐)
Client: Target 인터페이스를 통해 객체와 협력하는 클래스
3. 어댑터 패턴 예시 코드
// Target 인터페이스 - 클라이언트가 기대하는 인터페이스interface MediaPlayer { void play(String audioType, String fileName);}// Adaptee - 기존에 존재하는 호환되지 않는 클래스class AdvancedMediaPlayer { void playVlc(String fileName) { System.out.println("Playing vlc file: " + fileName); } void playMp4(String fileName) { System.out.println("Playing mp4 file: " + fileName); }}// Adapter - Target 인터페이스와 Adaptee를 연결class MediaAdapter implements MediaPlayer { private AdvancedMediaPlayer advancedPlayer; public MediaAdapter(String audioType) { if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) { advancedPlayer = new AdvancedMediaPlayer(); } } @Override public void play(String audioType, String fileName) { if (audioType.equalsIgnoreCase("vlc")) { advancedPlayer.playVlc(fileName); } else if (audioType.equalsIgnoreCase("mp4")) { advancedPlayer.playMp4(fileName); } }}// Client 클래스class AudioPlayer implements MediaPlayer { private MediaAdapter mediaAdapter; @Override public void play(String audioType, String fileName) { if (audioType.equalsIgnoreCase("mp3")) { System.out.println("Playing mp3 file: " + fileName); } else 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"); } }}
// 다양한 데이터베이스 드라이버를 통일된 인터페이스로 사용interface DatabaseConnection { void connect(); void executeQuery(String query);}class MySQLAdapter implements DatabaseConnection { private MySQLDriver mysqlDriver; public MySQLAdapter(MySQLDriver driver) { this.mysqlDriver = driver; } @Override public void connect() { mysqlDriver.mysqlConnect(); } @Override public void executeQuery(String query) { mysqlDriver.mysqlQuery(query); }}
4. 마무리
어댑터 패턴은 소프트웨어 개발에서 호환성 문제를 우아하게 해결해주는 중요한 디자인 패턴이다. 특히 기존 시스템을 유지하면서 새로운 기능을 통합해야 하는 상황에서 매우 유용하다.
마지막으로 장단점을 정리하고 끝내겠다.
장점
개방-폐쇄 원칙 준수: 기존 코드를 수정하지 않고 새로운 기능 추가 가능
단일 책임 원칙 준수: 인터페이스 변환 로직을 별도 클래스로 분리
코드 재사용성 향상: 기존 코드를 새로운 환경에서 재사용 가능
유연성 증대: 런타임에 어댑터 교체 가능
단점
코드 복잡성 증가: 새로운 인터페이스와 클래스 추가 필요
성능 오버헤드: 추가적인 메서드 호출로 인한 미미한 성능 저하
디버깅 어려움: 호출 체인이 길어져 디버깅이 복잡해질 수 있음