어댑터 패턴 (Adapter Pattern)

2021. 10. 6. 16:26컴퓨터 사이언스/디자인 패턴

어댑터 패턴 (Adapter Pattern: 적응자 패턴)

기존의 라이브러리나 프레임워크, 또는 프로젝트 내에서 공통적으로 사용하여 코드 수정이 쉽지 않은 클래스 등을

수정하지 않고 내가 원하는 기능을 추가할 수 있도록 변환(adapt)시킨다.

 

대표적인 예시로 돼지코 변환기가 있다.

한국에서는 220V를 사용하지만 일본에서는 110V를 사용한다. 따라서 일본에서 한국 전자기기를 쓰려면 콘센트와 전자기기 사이에 돼지코(변환장치)를 중간매개체로 써야한다.

 

 

구성요소

Client Target 인터페이스를 사용한다.
Target Client가 직접적으로 사용하려고 하는 인터페이스를 정의한다.
Adaptee가 지원하기를 바라는 인터페이스를 의미한다.
Adapter Target 인터페이스를 상속받아서 구현하는 클래스로,
Adaptee의 함수를 사용하게 된다.
Adaptee Adapter에서 사용하고자 하는 인터페이스를 정의한다.

 

 

구현

1. 객체 어댑터

Adapter를 객체(인스턴스)로 관리하는 패턴이다.

Adapter는 Adaptee 객체를 참조하고, 클라이언트는 Adapter 클래스를 통해 Adaptee의 동작을 실행할 수 있다.

 

 

2. 클래스 어댑터

다중상속이 가능해야 구현 가능한 패턴이다.

(C++은 다중상속을 지원하지만, Java는 다중상속을 지원하지 않는다.)

객체 어댑터와 차이점은, Adapter클래스가 사용하고 있는 Target 클래스와 제공받은 Adaptee 클래스를 모두 상속받는다.

 

 

장단점

1. 장점

  • 기존 코드를 변경하지 않아도 된다.
  • 위와 같은 이유로 클래스 재사용성이 증가된다.

 

2. 단점

  • 구성요소를 위해 클래스를 추가해야하기 때문에 복잡도가 증가한다.
  • 클래스 어댑터 패턴의 경우 상속을 사용하기 때문에 유연하지 않다.
  • 객체 어댑터 패턴의 경우 대부분의 코드를 다시 작성해야 하기 때문에 효율적이지 않다.

 

 

적용

내 경우는 길찾기 로직에서 국내/해외로 분리되어있는 부분에 적용했다. 로케이션 구분은 config 파일로 관리하며 프로그램 실행 초기에 이미 결정되어있고 중간에 변경되지 않는다. 따라서 Adapter와 Adaptee를 국내/해외로 분리해 2개로 만들었다.

기존에는 국내/해외 각 클래스에 중복되는 로직도 존재했고, 이로 인해 수정사항 발생시 둘 다 변경해야만 했다. 추후 유지보수의 용이성을 위해 공통된 부분은 하나로 관리하고, 상이한 부분만 따로 관리하는 방향으로 설계했다.

또한 Input 객체의 구조체나 버전 등이 국내/해외 메소드마다 아예 다른 케이스가 있기 때문에 단순 추상클래스 대신 어댑터 패턴을 사용했다.

Target *target= new Adapter_Kor();
target->request();	// Adapter_Kor::specificRequest()
Target *target= new Adapter_Abr();
target->request();	// Adapter_Abr::specificRequest()

C++이지만 객체 어댑터 패턴을 사용했고.. 추상클래스와 매우 헷갈렸다. 내가 이해한 추상클래스의 차이점을 간단하게 적어보자면 아래와 같다.

(혹시 잘못되었다면 댓글로 남겨주세요.)

 

어댑터 패턴

  • 하위 구조에 상관없이 상위에서 호출하는대로 roll up
  • 구현체가 return 객체에 박혀있음
  • top-bottom

추상클래스

  • 하위에서 인터페이스로 끌고 올라감.
  • 구현체와 리턴값이 같은 타입
  • bottom-up

 

 

마무리

  • 싱글턴, 팩토리 패턴 외에 처음으로 디자인 패턴을 실무에 적용해봤다. 이렇게 또 하나 알게되었다.
  • 실제로 일 할 때 설계를 제대로 해야 삽질을 덜하기 때문에, 클래스 다이어그램을 꽤 자주 그리는 것 같다.
    코드도 오래 보긴 했지만 구조를 여러 번 그렸다 지웠다.

 


Reference