어댑터는 호환되지 않는 인터페이스를 가진 객체들이 협업할 수 있도록 하는 구조적 디자인 패턴이라고 합니다.
여러분들이 주식 데이터를 외부로 부터 XML
형태로 제공받고 이를 보여주는 앱을 구성하고 있다고 가정해봅시다.
이 때 분석 기능을 추가하려고, 외부 분석 라이브러리를 보니 전부 다 Json
만 받는 것입니다.
그림으로 표현하면 아래와 같은 문제가 있는 것입니다.
출처 : 리팩토링 구구
이 문제는 생각보다 간단하게 보일 수 있습니다.
그냥 현재의 XML
데이터를 전부 Json
으로 바꿔주거나, 혹은 분석 라이브러리의 코드를 XML
을 받도록 변경하면 됩니다.
하지만, 전자의 경우 다른 라이브러에서 XML
형태의 데이터를 활용한다면 불가능한 방법이고, 라이브러리의 코드는 대부분 변경이 불가능하기 때문이 후자의 경우도 불가능한 방법으로 보입니다.
생각보다 간단하지가 않죠?
이 때 어댑터 디자인 패턴을 활용하면 어떨까요?
출처 : 리팩토링 구구
위 그림과 같이 내부에 어댑터를 구현하여 XML
-> JSON
으로 변환해주어 분석 라이브러리에게 넘겨주면 되는 것입니다.
이렇게 되면 현재 데이터들을 전부 다 Json
으로 변경할 필요도 분석 라이브러리의 코드를 변경할 필요도 없습니다.
구조를 살펴보고 실제 구현까지도 살펴볼까요?
출처 : 리팩토링 구구
구조는 간단합니다.
client
가 사용하는 공통적인 프로토콜을 interface
혹은 상속할 클래스로 둔 뒤, Adapter
에서 호환되지 않는 객체를 가지고 호환되는 데이터의 형태로 client
에게 제공하면 되는 것입니다.
정사각형 못과 둥근 구멍으로 많은 예시를 든다고 합니다.
당신은, 둥근 구멍에 못이 들어가는지 알고 싶어요
근데 이 때, 둥근 구멍에 넣기 좋게, 알기 쉽게, 둥근 못이 있고, 정사각형 못이 있다고 가정해볼게요.
이 경우, 둥근 못은 쉽게 반지름을 통해 둥근 구멍에 들어가는 지 알 수 있지만, 정사각형 못은 그렇지 않아요.
그렇자니 정사각형 못의 코드를 바꾸자니 그것도 좀.. 그렇습니다
그렇기 때문에 Adapter
패턴을 사용하는 것 입니다.
public class RoundHole {
private final double radius;
public RoundHole(double radius) {
this.radius = raidus;
}
public double getRadius() {
return radius
}
public boolean isFits(RoundPeg roundPeg) {
return this.radius >= roundPeg.getRadius();
}
}
public class RoundPeg {
private final double radius;
public RoundPeg(double radius) {
this.radius = radius;
}
public double getRedius() {
return radius;
}
}
public class SquarePeg {
private final double width;
public SquarePeg(double width) {
this.width = width;
}
public double getWidth() {
return width;
}
public double getSquare() {
return width * width;
}
}
public class SquarePegAdater extends RoundPeg {
private final SquarePeg squarePeg;
public SquarePegAdater(SquarePeg squarePeg) {
this.squarePeg = squarePeg;
}
@Override
public double getRadius() {
return Math.sqrt(Math.pow(squarePeg.getWidth() / 2, 2) * 2);
}
}
SquarePeg
의 Width
를 통하여 반지름을 계산해 반환합니다.
또한, RoundPeg
을 상속받아 getRadius Override
하였기 때문에, roundHole
의 isFits
에서 성공적으로 사용이 가능하게 된 것입니다.
public class Main {
public static void main(String[] args) {
RoundHole roundHole = new RoundHole(5);
RoundPeg roundPeg = new RoundPeg(4);
SquarePeg squarePeg = new SquarePeg(3);
boolean result1 = roundHole.isFits(roundPeg);
// boolean result2 = roundHole.isFits(squarePeg); // 불가능
SquarePegAdater squarePegAdapter = new SquarePegAdapter(squarePeg);
boolean result2 = roundHole.isFits(squarePegAdapter);
System.out.println(result1); // true;
System.out.println(result2); // true;
}
}
이와 같은 형식으로 Adapter
패턴을 사용하여 roundHole
과 SquarePeg
이 맞는지 확인할 수 있습니다.
장점으로서는 기존의 코드를 변경하지 않고, 호환성을 유지할 수 있으며, 또한 단일 책임 원칙을 보장할 수 있다는 것입니다.
하지만, 대부분의 디자인 패턴의 단점과 동일하게, 많은 인터페이스, 클래스들의 도입으로 인해 프로그램이 거대해지고, 나아가 코드의 복잡성을 높인다는 단점이 존재합니다.
하지만, 저에게는 장점이 조금 더 와닿네요!
우리 다 같이 잘 활용해봅시다!