[CleanCode] 어댑터 패턴
들어가며
외부 라이브러리 활용시 사용하는 어댑터 패턴을 알아보기 위한 포스팅
어댑터 패턴
- 외부 라이브러리를 사용하는 클라이언트 입장에서는 외부 라이브러리를 뜯어보고 사용방법을 익혀야 한다.
- 만약 라이브러리가 교체되거나 구현체가 바뀐다면 클라이언트는 사용법을 처음부터 다시 공부해야 한다.
- 어댑터 패턴을 이용하면 위와 같은 문제를 해결할 수 있다. 라이브러리를 이용하는 클라이언트에게는 추상화된 인터페이스를 제공하고 해당 인터페이스를 구현한 어댑터를 통해 외부 라이브러리 호출 로직을 설계할 수 있다.
- 이를 통해 클라이언트는 제공되는 인터페이스의 메서드만을 보고 손쉽게 외부 라이브러리 사용이 가능해진다.
- 또한 라이브러리 변경이나 새로운 라이브러리가 추가되는 경우 인터페이스 구현체인 새로운 어댑터를 도입함으로써 클라이언트는 해당 라이브러리를 지원하는 어댑터를 찾아 해당 어댑터를 호출하기만 하면 된다.
- 즉 확장에는 열려있고 클라이언트 입장에서 변경에는 닫혀있는 좋은 설계가 가능해진다.
대표적인 예
Spring MVC
구조에서dispatcherservlet
이 위와 같은 구조로 이루어져 있다. 먼저 코드부터 보자.
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
- 먼저 핸들러 매핑 정보에서
request
에 대응되는 핸들러를 가져온다. 핸들러 매핑 정보는ApplicationContext
를 통한dispatcherservlet
초기화 시점에 설정되고AnnotationAwareOrderComparator.sort(this.handlerMappings)
를 통해 검색 우선순위가 결정된다.
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
...
}
- 이후 해당 핸들러를 처리할 수 있는 어댑터를 가져온다. 어댑터 정보와 우선순위는 위와 유사하게 설계되어 있다.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- 이후 클라이언트라고 할 수 있는
dispatcherservlet
는 인터페이스인HandlerAdapter
로 추상화된handle
메서드만 호출하여 애플리케이션의 결과인ModelAndView
를 결과로 반환받을 수 있게 된다.
-> 위와 같은 설계는 무엇이 장점일까?? 먼저 말 그대로 추상화이다. dispatcherservlet
는 어댑터를 찾고 handle
만 호출하면 원하는 결과를 얻을 수 있다. 그 다음 장점으로는 확장성이다. 만약 새로운 핸들러가 추가된다면 해당 핸들러를 처리할 수 있는 어댑터만 목록에 추가해 주면 된다. 대박
댓글남기기