일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 넘파이
- cpp
- 넘파이 배열
- 코딩테스트
- Design Pattern
- 디자인 패턴
- ack
- 네트워크 기초
- java
- 머신러닝
- 자바
- cpp class
- 파이썬
- 코테
- Machine Learning
- c++
- OOP
- 합성곱 신경망
- 코드트리
- python
- 데이터 마이닝
- 클러스터링
- 데이터 분석
- numpy 기초
- 차원축소
- 코딩테스트실력진단
- lambda
- 넘파이 기초
- NumPy
- 기계학습
- Today
- Total
준비하는 대학생
[Design Pattern] Adapter Pattern(어댑터 패턴) 본문
어댑터 패턴이란?
어댑터 패턴은 클래스의 인터페이스를 사용자가 기대하는 다른 인터페이스로 변환하는 디자인 패턴입니다. 이를 통해 인터페이스가 호환되지 않는 클래스들이 함께 작동할 수 있습니다. 실생활에서 어댑터의 예시를 들어보면, 여러분이 여행을 가서 전기 플러그가 다른 국가에서도 장비를 사용할 수 있게 해주는 전원 어댑터를 생각해 볼 수 있습니다. 이처럼 어댑터 패턴도 두 클래스 사이의 '번역기' 역할을 하며, 이들이 서로 상호 작용할 수 있게 해줍니다.
어댑터 패턴의 구성요소
어댑터 패턴에는 다음과 같은 구성요소가 있습니다.
- Target: 클라이언트가 호출하는 인터페이스를 정의합니다.
- Adaptee: 클라이언트가 사용하고자 하는, 하지만 현재 인터페이스가 호환되지 않는 클래스를 말합니다.
- Adapter: Adaptee의 인터페이스를 Target 인터페이스로 변환합니다.
어댑터 패턴의 사용 사례
1) 기존 시스템과 새 시스템의 호환성 문제
시스템을 업그레이드하거나 새로운 시스템으로 이동하려고 할 때 종종 인터페이스의 호환성 문제가 발생합니다. 이럴 때 어댑터 패턴을 사용하여 기존의 코드를 건드리지 않고 새로운 시스템에 맞게 재사용할 수 있습니다.
2) 라이브러리나 외부 시스템과의 연결
외부 라이브러리를 사용하거나 다른 시스템과 통신할 때도 어댑터 패턴이 유용합니다. 외부 시스템이 제공하는 인터페이스와 우리 시스템의 인터페이스가 항상 일치하지 않습니다. 어댑터 패턴을 이용하면 두 시스템 간의 차이를 보완하여 서로 상호작용하게 만들 수 있습니다.
예시
아래에는 어댑터 패턴을 활용한 간단한 자바 코드 예시를 준비했습니다. 이 예시에서는 두 가지 다른 인터페이스를 가진 오디오 플레이어와 미디어 플레이어를 함께 사용하는 상황을 상상해 봅시다.
우리의 목표는 AudioPlayer 클래스가 MediaPlayer 인터페이스를 통해 vlc와 mp4 파일도 재생할 수 있게 하는 것입니다.
1. MediaPlayer Interface
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
2. AdvancedMediaPlayer Interface
public interface AdvancedMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}
3. VlcPlayer와 Mp4Player 클래스(Adaptee)
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}
@Override
public void playMp4(String fileName) {
// Do nothing
}
}
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
// Do nothing
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: "+ fileName);
}
}
4. MediaAdapter 클래스(Adapter)
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
}else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}
else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
5. AudioPlayer 클래스(Target)
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
// Inbuilt support to play mp3 music files
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: " + fileName);
}
// MediaAdapter is providing support to play other file formats
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");
}
}
}
* equalsIgnoreCase: String 클래스의 메서드로 두 문자열을 비교하는 메서드(대소문자 무시)
이 코드를 실행하면 AudioPlayer는 mp3 파일을 직접 재생하고, vlc와 mp4 파일은 MediaAdapter를 통해 재생합니다. 이렇게 어댑터 패턴을 사용하면 기존 클래스의 코드를 변경하지 않고도 새로운 기능을 추가할 수 있게 됩니다. 이 코드를 실행하는 메인 클래스는 다음과 같습니다.
public class AdapterPatternDemo {
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");
}
}
출력
Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported
본 예제에서 볼 수 있듯이, 어댑터 패턴은 기존 시스템의 변경 없이 새로운 기능을 추가하거나 기존 시스템과 외부 시스템을 매끄럽게 통합하는 데 매우 유용합니다.
어댑터 패턴 종류
어댑터 패턴은 '클래스 어댑터 패턴'과 '객체 어댑터 패턴'의 두 가지 형태로 나눌 수 있습니다.
- 클래스 어댑터 패턴
: 이 패턴은 상속을 이용해서 Adaptee 클래스의 인터페이스를 Target 인터페이스로 변환합니다. 이 패턴의 문제점은 자바와 같은 단일 상속 언어에서는 하나의 클래스만 상속할 수 있기 때문에, 한 번에 여러 Adaptee를 대상으로 할 수 없습니다. - 객체 어댑터 패턴
: 이 패턴은 합성을 사용해서 여러 개의 Adaptee를 대상으로 할 수 있습니다. 객체 어댑터는 Target 인터페이스를 구현하고, Adaptee 인스턴스를 포함하는 클래스를 만듭니다. 이 방식은 클래스 어댑터의 제약 사항을 해결하며, 어떤 클래스가 Adaptee로 사용될지를 런타임에 결정할 수 있는 유연성을 제공합니다.
자바에서의 어댑터 패턴
자바는 단일 상속만 지원하기 때문에 클래스 어댑터 패턴을 직접 구현하는 것은 불가능합니다. 하지만, 인터페이스를 통한 다중 상속이 가능하므로 이를 통해 클래스 어댑터 패턴과 비슷한 효과를 낼 수 있습니다.
그럼에도 불구하고, 대부분의 경우에는 객체 어댑터 패턴이 더 권장됩니다. 객체 어댑터 패턴은 클래스 어댑터 패턴보다 더 유연하며, 어떤 클래스가 Adaptee로 사용될지를 런타임에 결정할 수 있기 때문입니다.
이전에 제공한 자바 코드 예제는 바로 객체 어댑터 패턴을 사용한 것입니다. MediaAdapter는 MediaPlayer 인터페이스를 구현하고, AdvancedMediaPlayer의 인스턴스를 내부에 포함하고 있습니다. 이로 인해 MediaAdapter는 AdvancedMediaPlayer의 메서드를 사용하여 MediaPlayer의 인터페이스를 충족시킬 수 있습니다.
따라서 자바에서는 상황에 따라 클래스 어댑터 패턴과 비슷한 효과를 내기도 하지만, 대부분의 경우 객체어댑터 패턴을 선호하는 편입니다.
어댑터 패턴의 장단점
장점
- 재사용성 증가: 기존에 작성된 코드나 클래스를 쉽게 재사용할 수 있습니다. 즉, 코드의 중복을 줄이고 유지 보수를 용이하게 합니다.
- 확장성: 새로운 클래스를 추가하거나 기존 클래스를 변경하는 것이 용이합니다. 이는 소프트웨어의 확장성을 향상시키는데 도움이 됩니다.
- 결합도 감소: 어댑터를 사용함으로써 클래스 간의 결합도를 낮출 수 있으며, 따라서 하나의 클래스가 변경되었을 때 그 영향을 최소화할 수 있습니다.
단점
- 코드 복잡성 증가: 어댑터 패턴을 사용하면 클래스의 수가 증가하고, 이로 인해 코드가 복잡해질 수 있습니다.
- 어댑터 오버헤드: 어댑터는 하나의 인터페이스를 다른 인터페이스로 변환하는 과정에서 약간의 성능 저하가 발생할 수 있습니다.
결론
어댑터 패턴은 객체 지향 프로그래밍에서 매우 유용한 디자인 패턴입니다. 서로 다른 클래스 또는 시스템 간의 상호 작용을 가능하게 하며, 기존 코드의 재사용성을 높이는 데 큰 도움이 됩니다. 그러나 모든 상황에서 어댑터 패턴이 최적의 해결책이 되는 것은 아닙니다. 실제 문제와 요구 사항에 따라 적절한 디자인 패턴을 선택하는 것이 중요합니다.
'Programming > Design pattern' 카테고리의 다른 글
[Design Pattern] Facade Pattern(퍼사드 패턴) (0) | 2023.06.08 |
---|---|
[Design Pattern] Command Pattern(커맨드 패턴) (0) | 2023.06.02 |
[Design Pattern] Singleton Pattern(싱글턴 패턴) (0) | 2023.06.02 |
[Design Pattern] 데코레이터 패턴(Decorator Pattern) (0) | 2023.04.10 |
[Design Pattern] Observer Pattern(옵저버 패턴) (0) | 2023.03.29 |