目次
the Definition of Adapter Pattern
어댑터 패턴의 정의는 다음과 같습니다.
어댑터 패턴(Adapter Pattern) - 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환합니다. 어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있습니다.
이 패턴을 이용하면 호환되지 않는 인터페이스를 사용하는 클라이언트를 그대로 활용할 수 있습니다. 인터페이스를 변환해주는 어댑터를 만들면 되니까요. 이렇게 함으로써 클라이언트와 구현된 인터페이스를 분리시킬 수 있으며, 나중에 인터페이스가 바뀌더라도 그 변경 내역은 어댑터에 캡슐화되기 때문에 클라이언트는 바뀔 필요가 없습니다.
애플리케이션이 실행될 때 이 패턴이 어떤 식으로 작동하는지 살펴봤으니 이제 클래스 다이어그램도 살펴 보도록 할까요?
Class Diagram
어댑터 패턴에는 여러 객체지향 원칙이 반영되어 있습니다. 어댑터를 새로 바뀐 인터페이스로 감쌀 때는 객체 구성(composition)을 사용합니다. 이런 접근법을 쓰면 어댑터의 어떤 서브 클래스에 대해서도 어댑터를 쓸 수 있다는 장점이 있지요.
그리고 이 패턴에서는 클라이언트를 특정 구현이 아닌 인터페이스에 연결 시킵니다. 각각 서로 다른 백엔드 클래스들로 변환시키는 여러 어댑터를 사용할 수도 있습니다. 이렇게 인터페이스를 기준으로 코딩을 했기 때문에 타겟 인터페이스만 제대로 지킨다면 나중에 다른 구현을 추가하는 것도 가능합니다.
EnumerationIterator
실전에서 간단한 어댑터를 사용하는 예를 살표볼까요? 자바를 비교적 오래 전부터 써왔다면, Enumeration을 리턴하는 elements()메소드가 구현되어 있었던, 초기 컬렉션 형식(Vector, Stack, Hashtable)을 알고 있을 겁니다. Enumeration인터페이스를 이용하면 컬렉션 내에서 각 항목이 관리되는 방식에는 신경 쓸 필요 없이 컬레션의 모든 항목들에 접근할 수 있습니다.
썬에서 새로운 컬렉션 클래스를 출시하면서, Enumeration과 마찬가지로 컬렉션에 있는 일련의 항목들에 접근하게 해 주면서 항목을 제거할수도 있게 해 주는 Iterator라는 인터페이스를 이용하기 시작했습니다.
Enumeration 인터페이스를 사용하는 구형 코드를 사용해야 하는 경우가 종종 있지만, 새로 만든 코드에서는 Iterator만 사용할 계획입니다. 이런 경우에 어댑터 패턴을 적용하면 좋겠죠?
Adapter Design
클래스를 이런식으로 만들어야 할 것 같습니다. 우선 타겟 인터페이스를 구현하고, 어댑터 객체로 구성된 어댑터가 필요합니다. hasNext()와 next()메소드는 타겟에서 어댑너로 바로 연결됩니다. 하지만 remove()는 어떻게 처리할까요? 잠시 생각해 봅시다.
remove() method
Enumeration에서는 remove()에 해당하는 기능을 제공하지 않습니다. 읽기 전용 인터페이스라고 할 수 있죠. 어댑터의 차원에서 완벽하게 작동하는 remove()메소드를 구현할 수 있는 방법은 없습니다. 그나마 가장 좋은 방법으로 런타임 예외를 던지는 방법을 생각해 볼 수 있을 것 같습니다. 다행히도 Iterator 인터페이스를 디자인한 사람들은 이런 필요성을 미리 예견하고 remove()메소드를 구현할 때 UnsupportedOperationException 을 지원하도록 만들었습니다.
이런 경우는 어댑터가 완벽하게 적용될 수 없는 경우라고 할 수 있습니다. 클라이언트 쪽에서는 예외가 생길 수 있는 가능성을 염두에 두고 있어야 합니다. 하지만 클라이언트에서 충분히 주의를 기울이고, 어댑터 문서를 잘 만들어 두면 상당히 쓸만한 해결책이 될 수 있을 것입니다.
Snippet of EnumerationIterator Class
여전히 Enumeration을 만들어내는 구형 클래스에 대해서 사용할 수 있는 간단하지만 효과적인 코드입니다.
public class EnumerationIterator implements Iterator{ Enumeration enum; public EnumerationIterator(Enumeration enum){ this.enum = enum; } public boolean hasNext(){ return enum.hasMoreElements(); } public Object next(){ return enum.nextElement(); } public void remove(){ throw new UnsupportedOperationException(); } }