본문 바로가기

Coding Note

[Design pattern] Adapter

Adapter pattern : 서로 다른 인터페이스를 하나의 호환가능한 인터페이스로 통일시키는 구조패턴이다. 주로 이미 만들어진 라이브러리에 대해서 적용할 클라이언트 프로그램에 인터페이스가 맞지 않을 때 인터페이스를 맞추기 위해 사용되며 110v - 220v 돼지코 어뎁터와 사용 의의가 같다.

아래 클래스 관계도 처럼 인터페이스가 다른 adaptee를 기존 인터페이스로 맞춰줄 수 있다.

 


하드웨어적으로 본다면 돼지코 어뎁터, SDCard-USB컨버터 등 서로다른 구멍모양을 맞춰주는 용도(ㅡㅡ?) 이다.

구멍의 모양이 interface가 되겠고, 구멍 모양만이 아니라 내부 동작구조까지 변경되야한다면 조금 더 복잡한 어뎁터가 되겠다.


소프트웨어 개발의 관점에서 본다면 이미 존재하는 library를 사용하려는데 데이터 타입이라던가 기존의 클래스인터페이스와의 호환성을 맞추기 위해 Adapter pattern을 사용할 수 있다.


애초에 모든 클래스와 기능을 같이 설계했다면 Adapter pattern이 적용될 부분은 흔치않을것이다. 하지만 오히려 이런경우가 흔치않은 케이스인만큼 Adapter pattern을 이해하는것은 중요하다.(물론 너무나도 직관적인 패턴이라 이해고자시고 그냥 쓰면된다)


예제로는 오로지 이해만을 위해 USB슬롯에 디바이스를 꽂아 데이터를 쓰고, 표시해주는 상황을 표현하였다.



/* USBConnectable.java */

public interface USBConnectable {

public String read();

public void write(String data);

}


/* USBSlot.java */

public class USBSlot {

private USBConnectable usbConnectable;


public void setUsbConnectable(USBConnectable usbConnectable){

this.usbConnectable = usbConnectable;

}


public void writeData(String data){

usbConnectable.write(data);

}

public void printData(){

System.out.println(usbConnectable.read());

}

}

USB슬롯에 꽂을 수 있는 무언가를 꽂은 후, String의 데이터를 쓰거나 콘솔에 뿌려볼 수 있다.

/* USBDevice.java */
public class USBDevice implements USBConnectable {
private String data;

@Override
public String read() {
return data;
}

@Override
public void write(String data) {
this.data = data;
}
}

/* SDCard.java */
public class SDCard {
private final int size = 512;
private char[] hexData;

public SDCard(){
hexData = new char[size];
}

public char[] readData(){
return hexData;
}
public void writeData(char[] data){
int len = data.length;
for(int i=0; i<len && i<size; i++)
hexData[i] = data[i];
}
}
이 SDCard의 구현을 이미 만들어진 라이브러라리 하자. USBDevice와 다르게 char[] 타입의 데이터로 통신한다.
타입이 다르지만 클라이언트(USBSlot) 에서의 호환을 맞추기 위해 아래 Adapter를 만든다

/* SDCardToUSBAdapter.java */
public class SDCardToUSBAdapter implements USBConnectable {
private SDCard sdCard;
public SDCardToUSBAdapter(SDCard sdCard){
this.sdCard = sdCard;
}

@Override
public String read() {
return new String(sdCard.readData());
}

@Override
public void write(String data) {
sdCard.writeData(data.toCharArray());
}
}

/* main */
public static void main(String[] args){
USBSlot usbSlot = new USBSlot();

usbSlot.setUsbConnectable(new USBDevice());
usbSlot.writeData("test");
usbSlot.printData();

usbSlot.setUsbConnectable(new SDCardToUSBAdapter(new SDCard()));
usbSlot.writeData("to sdcard");
usbSlot.printData();
}

Adapter pattern을 적용하여 다른 인터페이스를 가지는 라이브러리를 클라이언트에 호환성을 가지도록 적용하였다.

예제에는 객체를 adapt하여 Adapter클래스를 구현하였는데, 이를 Object adapter pattern이라 한다.
조금 다르게 클래스를 상속하여 adapt한다면 Class adapter pattern이라 하며 기존 인터페이스 및 클라이언트에서의 인터페이스를 모두 상속해야 하기 때문에 클라이언트 언어에 따라, 라이브러리의 클래스 설계에 따라 적용이 불가능할 수 있다.
C++와 같은 다중상속이 가능한 언어라면 상관이 없지만, JAVA나 C#같은 다중상속이 불가능한 언어는 라이브러리의 설계가 인터페이스를 잘 정의해두지 않았다면 Object adapter pattern을 적용해야한다.

어떻게 adapt하던 그 결과는 충분한 호환성을 가진 클래스이다. 이 패턴의 형태가 기존의 무언가를 '감싸서' 내것처럼 사용하기 때문에 Wrapper pattern이라고도 한다.

한줄요약 : 남에것을 쓰려면 포장이라도 내것처럼 이쁘게 해야지

'Coding Note' 카테고리의 다른 글

[Design pattern] Composite  (0) 2013.03.03
[Design pattern] Bridge  (0) 2013.02.20
[Design pattern] Iterator  (0) 2013.02.04
[Design pattern] Command  (0) 2013.02.04
[Design pattern] Chain of Responsibliity  (1) 2013.02.02