어댑터 패턴(Adapter Pattern)
* 이 포스팅은 네이버 블로그에서 작성(2013.03.28)한 내용을 옮겨온 것입니다.
음, 이번엔 어댑터 패턴에 대해 복습할겸 공부한 내용을 올려보겠습니다. 어댑터라는것은 주변에도 많이 볼 수 있지요. 대표적으로 110V 전용 가전제품에 220V 어댑터를 끼워 사용하는 것입니다. 지금은 110V가 우리나라에선 많이 사라지긴 했지만 미국에서는 아직 110V를 많이 쓴다고 언뜻 들은 것 같습니다.
중요한건 이 어댑터 패턴의 사용처일겁니다. 사용법보다도 중요한 사용처!
예를 들자면 다음과 같은 경우이죠. 이제부터 제가 가상의 상황을 만들어보겠습니다.
즉 "나"는 성능의 향상을 위해 포토 뷰어 엔진을 기존의 엔진에서 A 업체가 제공하는 엔진으로 바꿔야 하는 상황입니다.
그리고 다음은 명세표를 토대로 만든 A 엔진 및 기존 엔진의 구조입니다.
/* A 엔진 명세 (ALib) */ public class ALib { public void printPhoto(String fileName) { ... } public void deletePhoto(String fileName) { ... } public void addPhoto(String fileName) { ... } }
/* 내가 쓰는 엔진 명세 (MyLib) */ public class MyLib { public void printPhoto(String fileName){ ... } public void printPhotoList(String[] listName){ ... } public void deletePhoto(String fileName){ ... } public void addPhoto(String fileName){ ... } }
* 지금은 패턴을 공부하기 위해서니까 간단하게 양쪽이 모두 같은 기능을 하지만 "성능"만 다르다고 칩시다.
완전히 같다면 좋을텐데 미묘하게 다른 구석이 있습니다. 차이가 있는 것은 제공 받는 엔진에서 printPhotoList 메소드의 부재입니다. 어쨌든 엔진 교체를 위해 코드 분석에 들어가야할 시간입니다. 아래 코드는 기존 엔진을 사용한 코드 입니다. 기존 엔진의 이름은 MyLib 입니다.
/* 기존 코드 */ 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"); }
자, 이제 잘 돌아갈거라 생각이 됩니다. 그런데, 기본 로직에 큰 변경이 생겼습니다. 바로 전에 없었던 for 문이 등장한 것이죠. 물론 이 코드를 더 이상 유지 보수 할 것이 아니라면 이대로 둬도 됩니다. 하지만 그건 아니잖아요?
만약 다른 엔진을 도입하기로 했는데 그 엔진은 printPhotoList 메소드를 지원할 수도 있는데 말이죠.
따라서 기본 로직을 그대로 이용할 수 있게끔 만들어 봅시다. 그렇게 하기 위해 어댑터 패턴을 도입합시다.
다음과 같은 인터페이스를 먼저 만듭시다.
public interface PhotoEngine{ public void printPhoto(String fileName); public void printPhotoList(String[] listName); public void deletePhoto(String fileName); public void addPhoto(String fileName); }
이름은 포토엔진이고 이 인터페이스에서 필수로 정의해야할 메소드들이 존재합니다. 그리고 실제 적용할 어댑터 클래스를 만들어봅시다.
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 ); } }
어댑터 클래스까지 완성 되었습니다. 부가적인 코드 설명이 필요할까요? 개발자는 코드만 보면 마음이 통하는법이죠. 농담입니다.
아무튼, 이 어댑터 클래스가 하는 일은 ALib 클래스를 받아 PhotoEngine 인터페이스의 메소드를 구현하는 것뿐입니다.
단지 그 뿐인데 코드는 다음처럼 사용 할 수 있습니다.
/* 바뀐 코드 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"); }
첫부분을 제외한 나머지 로직은 그대로 사용이 가능하죠.
물론 ALib가 아닌 다른 포토 뷰어 엔진을 사용한다고 할때도 마찬가지로 PhotoEngine 인터페이스만 포함한 어댑터 클래스를 만들기만 하면 언제든 로직을 재사용할 수 있습니다.
어댑터 패턴을 사용하는 목적이기도 합니다.
만약 A 업체와 계약이 끝나서 ALib 엔진을 더 이상 쓸 수 없다면 어떻게 해야 할까요?
자, 어댑터 클래스를 하나 더 만들어 봅시다. 기존 엔진의 어댑터 클래스를 만드는겁니다.
public class MyLibAdapter implements PhotoEngine { MyLib lib; public MyLibAdapter( MyLib lib ){ this.lib = lib; } public void printPhoto(String fileName){ lib.printPhoto( fileName ); } public void printPhotoList(String[] listName){ lib.printPhotoList( listName ); } public void deletePhoto(String fileName){ lib.deletePhoto( fileName ); } public void addPhoto(String fileName){ lib.addPhoto( 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"); }
참 쉽죠?
만약 어댑터 패턴을 도입하지 않았다면 for 문을 다시 삭제하고 원래의 코드로 돌리는 과정이 필요 했을겁니다. 하지만 어댑터 패턴을 사용한 결과 기본 로직 변경 없이 엔진을 바꾸는데 성공했죠. 이렇게 어댑터 패턴에 대해 공부해봤습니다. 혹시 틀린점 있다면 댓글 부탁드립니다. :)
포스팅 이동후 느낀 소감
...그땐 뭔가 엄청 열심히 만든 느낌인데 지금 보니 영...(...)