어댑터 패턴은 무엇이고 어떻게 사용할 수 있을까?

김재연·2023년 12월 26일
1

어댑터 패턴의 정의

어댑터는 호환되지 않는 인터페이스를 가진 객체들이 협업할 수 있도록 하는 구조적 디자인 패턴이라고 합니다.

문제

여러분들이 주식 데이터를 외부로 부터 XML 형태로 제공받고 이를 보여주는 앱을 구성하고 있다고 가정해봅시다.

이 때 분석 기능을 추가하려고, 외부 분석 라이브러리를 보니 전부 다 Json 만 받는 것입니다.

그림으로 표현하면 아래와 같은 문제가 있는 것입니다.

출처 : 리팩토링 구구

어떻게 하면 해결할 수 있을까요?

이 문제는 생각보다 간단하게 보일 수 있습니다.

그냥 현재의 XML 데이터를 전부 Json 으로 바꿔주거나, 혹은 분석 라이브러리의 코드를 XML 을 받도록 변경하면 됩니다.

하지만, 전자의 경우 다른 라이브러에서 XML 형태의 데이터를 활용한다면 불가능한 방법이고, 라이브러리의 코드는 대부분 변경이 불가능하기 때문이 후자의 경우도 불가능한 방법으로 보입니다.

생각보다 간단하지가 않죠?

이 때 어댑터 디자인 패턴을 활용하면 어떨까요?

출처 : 리팩토링 구구

위 그림과 같이 내부에 어댑터를 구현하여 XML -> JSON 으로 변환해주어 분석 라이브러리에게 넘겨주면 되는 것입니다.

이렇게 되면 현재 데이터들을 전부 다 Json 으로 변경할 필요도 분석 라이브러리의 코드를 변경할 필요도 없습니다.

구조를 살펴보고 실제 구현까지도 살펴볼까요?

구조

출처 : 리팩토링 구구

구조는 간단합니다.

client 가 사용하는 공통적인 프로토콜을 interface 혹은 상속할 클래스로 둔 뒤, Adapter 에서 호환되지 않는 객체를 가지고 호환되는 데이터의 형태로 client 에게 제공하면 되는 것입니다.

가장 많이 드는 예시로 구현해볼까요?

정사각형 못과 둥근 구멍으로 많은 예시를 든다고 합니다.

당신은, 둥근 구멍에 못이 들어가는지 알고 싶어요

근데 이 때, 둥근 구멍에 넣기 좋게, 알기 쉽게, 둥근 못이 있고, 정사각형 못이 있다고 가정해볼게요.

이 경우, 둥근 못은 쉽게 반지름을 통해 둥근 구멍에 들어가는 지 알 수 있지만, 정사각형 못은 그렇지 않아요.

그렇자니 정사각형 못의 코드를 바꾸자니 그것도 좀.. 그렇습니다

그렇기 때문에 Adapter 패턴을 사용하는 것 입니다.

  • 사용되는 Class

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;
	}

}
  • Adapter
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);
	}

}

SquarePegWidth 를 통하여 반지름을 계산해 반환합니다.

또한, RoundPeg 을 상속받아 getRadius Override 하였기 때문에, roundHoleisFits 에서 성공적으로 사용이 가능하게 된 것입니다.

  • Main
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 패턴을 사용하여 roundHoleSquarePeg 이 맞는지 확인할 수 있습니다.

장점 및 단점

  • 장점
  1.  단일 책임 원칙. 프로그램의 기본 비즈니스 로직에서 인터페이스 또는 데이터 변환 코드를 분리할 수 있습니다.
  2.  개방/폐쇄 원칙. 클라이언트 코드가 클라이언트 인터페이스를 통해 어댑터와 작동하는 한, 기존의 클라이언트 코드를 손상시키지 않고 새로운 유형의 어댑터들을 프로그램에 도입할 수 있습니다.
  • 단점
  1.  다수의 새로운 인터페이스와 클래스들을 도입해야 하므로 코드의 전반적인 복잡성이 증가합니다. 때로는 코드의 나머지 부분과 작동하도록 서비스 클래스를 변경하는 것이 더 간단합니다.

장점으로서는 기존의 코드를 변경하지 않고, 호환성을 유지할 수 있으며, 또한 단일 책임 원칙을 보장할 수 있다는 것입니다.

하지만, 대부분의 디자인 패턴의 단점과 동일하게, 많은 인터페이스, 클래스들의 도입으로 인해 프로그램이 거대해지고, 나아가 코드의 복잡성을 높인다는 단점이 존재합니다.

하지만, 저에게는 장점이 조금 더 와닿네요!

우리 다 같이 잘 활용해봅시다!

참고 자료

어댑터 패턴 글

profile
끊임없이 '성장'하는 개발자 김재연입니다.

0개의 댓글