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
- Adapter Pattern (C++)
- 디자인 패턴 - 6. Adapter Pattern
- Adapter Pattern
- [Design Pattern] Adapter 패턴
- 디자인패턴 - 어댑터 패턴 (adapter pattern)