어댑터(Adapter) 패턴
어댑터 패턴은 호환되지 않는 인터페이스를 가진 클래스들 사이의 호환성을 제공하는 디자인 패턴입니다. 마치 어댑터 플러그가 서로 다른 형태의 전기 소켓을 연결해주는 것처럼, 클라이언트 코드가 기존 코드나 라이브러리를 수정하지 않고 사용할 수 있도록 합니다.
문제점: 호환되지 않는 인터페이스로 인한 통합 불가능
애플리케이션에서 기존 코드나 외부 라이브러리가 제공하는 인터페이스와 클라이언트의 요구가 다를 수 있습니다. 예를 들어, 이미지 뷰어 애플리케이션에서 다양한 이미지 형식을 지원해야 하지만 각 이미지 라이브러리가 서로 다른 인터페이스를 제공합니다.
// 기존 코드: PNG 이미지 로더
class PngImage {
public void loadPng(String filename) {
System.out.println("Loading PNG: " + filename);
}
}
// 클라이언트 코드: 이미지 로더 인터페이스 요구
interface ImageViewer {
void loadImage(String filename);
}
// 클라이언트는 PngImage를 직접 사용할 수 없음
public class Main {
public static void main(String[] args) {
// PngImage가 ImageViewer 인터페이스를 구현하지 않기 때문에 사용할 수 없음.
// ImageViewer viewer = new PngImage(); // 오류 발생
}
}
문제점
- 호환되지 않는 인터페이스: PngImage 클래스는 loadPng() 메서드를 사용하지만, 클라이언트는 loadImage() 메서드를 기대합니다.
- 유연성 부족: 여러 이미지 형식을 동일한 인터페이스로 처리하기 어렵습니다.
- 외부 라이브러리 코드 수정 불가: PngImage 클래스를 직접 수정하지 않고 통합해야 합니다.
해결 방법: 어댑터 패턴 적용
어댑터 패턴을 사용하면 기존 클래스의 인터페이스를 클라이언트가 기대하는 인터페이스로 변환할 수 있습니다.
// 기존 코드: PNG 이미지 로더
class PngImage {
public void loadPng(String filename) {
System.out.println("Loading PNG: " + filename);
}
}
// 클라이언트가 기대하는 인터페이스
interface ImageViewer {
void loadImage(String filename);
}
// 어댑터 클래스: PngImage를 ImageViewer로 변환
class PngImageAdapter implements ImageViewer {
private PngImage pngImage;
public PngImageAdapter(PngImage pngImage) {
this.pngImage = pngImage;
}
@Override
public void loadImage(String filename) {
// 기존 PngImage 클래스의 메서드를 호출
pngImage.loadPng(filename);
}
}
public class Main {
public static void main(String[] args) {
// 기존 PngImage 객체 생성
PngImage pngImage = new PngImage();
// 어댑터를 사용해 PngImage를 ImageViewer 인터페이스로 변환
ImageViewer viewer = new PngImageAdapter(pngImage);
// 클라이언트는 동일한 인터페이스로 이미지를 로드
viewer.loadImage("example.png"); // 출력: Loading PNG: example.png
}
}
어댑터 패턴의 장단점
장점
- 유연성 제공: 호환되지 않는 인터페이스를 연결해 코드를 재사용할 수 있습니다.
- 기존 코드 수정 불필요: 기존 클래스나 라이브러리를 수정하지 않고도 사용할 수 있습니다.
- 확장성 향상: 새로운 기능을 어댑터를 통해 쉽게 추가할 수 있습니다.
단점
- 복잡성 증가: 어댑터 클래스를 추가해야 하므로 코드가 복잡해질 수 있습니다.
- 성능 문제: 어댑터를 통한 호출은 직접 호출보다 약간의 성능 오버헤드가 발생합니다.
- 클래스 수 증가: 다양한 어댑터를 사용하면 클래스가 많아질 수 있습니다.
결론
어댑터 패턴은 기존 코드와 클라이언트 코드 간의 인터페이스 차이를 해소하는 데 유용한 패턴입니다. 이 패턴을 사용하면 서로 다른 인터페이스를 가진 객체들을 통합할 수 있어 재사용성이 높아지고, 기존 코드를 수정하지 않고도 새로운 기능을 추가할 수 있습니다. 다만, 코드 복잡성이 증가할 수 있으므로 필요에 따라 신중하게 사용해야 합니다.