오르카의 아틀리에

어댑터는 우리가 일상생활 속에서 잘 사용하고 있는 물건중 하나이다. 예를 들어 HDMI 케이블에 MiniHDMI 어댑터를 사용하여 HDMI to MiniHDMI 처럼 사용한다든지, 110V짜리 플러그를 어댑터를 사용하여 220V에 연결하는데 사용하고 있다. 어댑터는 이처럼 "제공되고 있는 것과 필요한 것 사이를 연결해주는 역할"을 하고 있다.

Adapter 패턴은 왜 사용할까?!

앞서 언급했듯이 어댑터는 제공되고 있는 것과 필요한 것 사이를 연결해주는 역할을 한다. 그러면 Adapter 패턴은 왜 사용해야 할까? 우리는 항상 처음부터 프로그래밍한다고 할 수 없다. 분명, 오픈소스 라이브러리를 사용하거나 프로젝트 중간에 합류해서 작업해야 하는 경우가 생긴다. 이럴 경우에 기존에 작업하던 코드들을 Adapter 패턴을 이용하여 간단하게 재사용할 수 있다.


"그냥, 지금까지 작업했던 것을 수정하면안되나?

조금만 바꾸면 될거같은데?"


물론, 그래도 되겠지…. 만? 그럴 경우에 개발자에게 조금 고통스러운 상황이 연출될 가능성이 크다. 예를 들어 은행 결제 시스템을 업그레이드하는 프로젝트에 합류했다고 하자, 은행 시스템은 다른 프로그램들보다 레거시한 코드들이 많은데 위의 생각처럼 지금까지 작업했던 코드들을 직접 고쳐서 기능을 추가한다고 생각해보자. 일단 내가 작업한 적이 없었던 코드들과 씨름해야 한다. (1) 코드들이 내부적으로 어떻게 동작하는지 대략적으로나마 흐름을 잡아야 작업을 시작할 수 있으므로 코드를 읽는데 시간을 들인다.


잘 수정해서 기능을 추가했다고 하자. 기존에 작성된 코드들은 지금까지 별 무리 없이 돌아갔던 (버그가 적었던) 검증된 코드들이었다. 여기서 (2) 내가 코드를 수정하면서 다시 한 번 전체적으로 테스팅을 해야 한다. 전체적으로 전부 다시 검증해야 하므로 시간도 오래 걸리고, (3)여기서 버그가 발생하면 아주 난감해진다. 소스를 중간중간 고쳤기 때문에 어디서 버그가 발생했는지 예측하기 힘들어진다. (프로젝트가 크면 클 수록 더 힘들어진다.)



어댑터 패턴을 사용하면 기존 코드를 수정하지 않고 재사용하기 때문에 스펙만 알고 있다면, 상세한 내부 코드 흐름을 몰라도 되고, 이미 검증된 코드들을 수정하지 않아서 어댑터 코드만 테스팅하면 된다. 또한, 버그가 발생해도 90%는 어댑터에서 발생한 오류일터이니 버그 헌팅을 하기도 쉬워진다. 이런 장점 때문에 어댑터 패턴을 사용해야 한다.

UML





어댑터의 UML은 구현 방법에 따라 2개로 나뉜다. 위임을 이용한 방법과 다중 상속을 이용하여 구현하는 방법이다. (Java에서 다중 상속이 지원되지 않으므로 Target은 implements, Adaptee는 extends하면 된다.)


간단하게 살펴보자. 그림에도 설명을 적어놓았지만, 위임을 이용하는 방법에서는 Target을 이용하여 필요한 메소드들을 명시하고, Adapter가 Adaptee 인스턴스를 이용하여 Target 에서 명시된 메소드들을 구현하는 역할을 한다.


다중 상속을 이용하는 경운, Adapter가 Adaptee 인스턴스를 가지고 있는 것이 아니라 Adaptee로부터 상속받아 사용한다.

소스 코드



다른 부분은 크게 다르지않다. RectangleAdapter를 보면 되는데 아래 코드는 다중 상속을 사용하지 않고, LegacyReactangle* 를 멤버 변수로 가지고있어 생성될 때 LegacyReactangle을 동적 할당하여 가지고 있는다.