본문 바로가기

Java

디자인 패턴2 - 어댑터 패턴(Adapter Pattern)

어댑터 패턴 (adapter pattern)이란?

항상 110V, 220V 변환기로 비유를 많이 든다. 하지만 나는 정확히 와닿지가 않았다.

좀더 코드단의 예로 들면, 프로젝트에서 검색엔진 A를 사용하고 있었다. A의 search()함수와 이를 이용하여 분석하는 비즈니스 코드가 있을 것이다.

하지만, 검색엔진 B가 더 좋다고 판단되어 수정을 한다면? ( 표준화는 되어있지 않을 것이기 때문에 A,B의 사용방법이나 함수는 무조건 다를 것이다 )


어댑터 패턴 예제1

음악 파일과 음악 재생기로 예제를 살펴보자.


interface MediaPlayer {
void play(String filename);
}

interface MediaPackage {
void playFile(String filename);
}

class MP3 implements MediaPlayer {

@Override
public void play(String filename) {
System.out.println("Playing mp3 file :" + filename);
}
}

class MP4 implements MediaPackage {

@Override
public void playFile(String filename) {
System.out.println("Playing mp4 file :" + filename);
}
}

class FormatAdapter implements MediaPlayer {

private MediaPackage media;

FormatAdapter(MediaPackage m) {
this.media = m;
}

@Override
public void play(String filename) {
media.playFile(filename);
}
}
class Main {
public static void main() {
MediaPlayer player = new MP3();
player.play("test.mp3");

player = new FormatAdapter(new MP4());
player.play("test.mp4");

/**
* Adapter pattern을 사용하지 않는 코드였다면? ( 아래처럼 )
* mp3 -> mp4 -> mkv 확장자가 바뀔 때마다
* mp3,mp4 클래스를 사용하는 클라이언트 코드에
* 모두 수정이 필요해 진다.
*
* 지금은 간단해보여서 와닿지 않을 수도 있다.
* 만약 재생목록이 있는데
* mp3는 plays(list) 함수가 있고,
* mp4는 play(str) 함수만 있어서,
* for문을 돌려서 plays와 같은 코드를 만들어 줘야 한다면?
*
* 유지보수 비용이 훨씬 높아질 것이다.
*/

MediaPlayer player2 = new MP3();
player2.play("test.mp3");

MediaPackage player2_mp4 = new MP4();
player2_mp4.playFile("test.mp4");
}
}


어댑터 패턴 예제 2. 포토엔진 교체

/* 기존 코드 */
main(){
	MyLib lib = new MyLib();
	String photoList = { "abc.jpg" , "def.jpg" };
	lib.printPhotoList(photoList);
	lib.printPhoto("abc.jpg");
	lib.deletePhoto("abc.jpg");
	lib.addPhoto("aaa.jpg");
}

/* 바뀐 코드 Ver.1 */
main(){
	ALib lib = new ALib(); // change
	String photoList = { "abc.jpg" , "def.jpg" };
	for(int i = 0 ; i < photoList.length; i++){ // for문 추가됨
		lib.printPhoto(photoList[i]);
	}
	lib.printPhoto("abc.jpg");
	lib.deletePhoto("abc.jpg");
	lib.addPhoto("aaa.jpg");
}
public interface PhotoEngine{
	public void printPhoto(String fileName);
	public void printPhotoList(String[] listName);
	public void deletePhoto(String fileName);
	public void addPhoto(String fileName);
}
/* 바뀐 코드 Ver.3 */
main(){
	MyLib myLib = new myLib();
	PhotoEngine lib = new MyLibAdapter( myLib );
	String photoList = { "abc.jpg" , "def.jpg" };
	lib.printPhotoList(photoList);
	lib.printPhoto("abc.jpg");
	lib.deletePhoto("abc.jpg");
	lib.addPhoto("aaa.jpg");
}
public class ALibAdapter implements PhotoEngine{
	ALib lib;
	public ALibAdapter( ALib lib ){
		this.lib =  lib;
	}
 	public void printPhoto(String fileName){
		lib.printPhoto( fileName );
	}
	public void printPhotoList(String[] listName){
		for(int i = 0 ; i < listName.length; i++){
			lib.printPhoto( listName[i] );
		}
	}
	public void deletePhoto(String fileName){
		lib.deletePhoto( fileName );
	}
	public void addPhoto(String fileName){
		lib.addPhoto( fileName );
	}
}


/* 바뀐 코드 Ver.2 */
main(){
	ALib alib = new ALib();
	// 아래 라인이 해석이 안되면 Java Upcasting 공부를 하셔야합니다.
	PhotoEngine lib = new ALibAdapter( alib );
	String photoList = { "abc.jpg" , "def.jpg" };
	lib.printPhotoList(photoList);
	lib.printPhoto("abc.jpg");
	lib.deletePhoto("abc.jpg");
	lib.addPhoto("aaa.jpg");
}