# ⭐️유연한 컨트롤러2 - v5(어댑터 도입, 인터페이스3,4 처리)

핵심 - 어댑터 목록 조회

handlerAdapters는 핸들러어댑터목록이다!\
List\<MyHandlerAdapter>에는 MyHandlerAdapter 인터페이스를 구현한 V3 핸들러 어댑터, V4 핸들러 어댑터,...등이 있다!

**⭐️for(MyHandlerAdapter : List\<MyHandlerAdapter>)⭐️⭐️⭐️⭐️⭐️**\
부모 인터페이스는 자식 객체를 품을 수 있다!\
그러므로 인터페이스인 MyHandlerAdapter 타입의 리스트를 만들면 V3든 V4든 핸들러 어댑터 종류 상관없이 리스트에 저장이 가능하다!

```java
private MyHandlerAdapter getHandlerAdapter(Object handler) {
    //handler = MemberFormControllerV4
    for (MyHandlerAdapter adapter : handlerAdapters) {//리스트 다 뒤진다.
        if(adapter.supports(handler)){//Controller3Adapter 먼저 검사하고, 조건 불충족하면 다음 Controller4Adapter 조건 검사
            return adapter;//MyHandlerAdapter adapter를 리턴한다!
        }
    }
    throw new IllegalArgumentException("Handler Adapter를 찾을 수 없습니다.handler"+handler);
}
```

이제 컨트롤러4가 들어왔을 때도 어댑터로 처리할 수 있는 코드를 작성해본다.\
다음 3가지만 추가해서 수정해주면 메인 코드에서는 수정할 것이 없다!\
initHandlerMappingMap()과 initHandlerAdapters()은 설정을 하는 부분이기 때문에 이 부분도 메인에서 분리하고, **컨트롤러 핸들링하는 어댑터만 추가**해서 구현해주면 메인에는 수정할 것이 하나도 없어지게 된다!

**이렇게 인터페이스에 의존하도록 설계하면 OCP를 만족하면서, 확장성 좋게 추가 및 수정이 가능하고, 메인 코드는 전혀 수정할 필요가 없게 된다!**

* initHandlerMappingMap() : 컨트롤러4 uri가 들어와도 맵핑할 수 있도록 맵에 추가한다.
* initHandlerAdapters() : handlerAdapters 리스트에 컨트롤러4에 대한 처리할 수 있도록 ControllerV4HandlerAdapter를 추가한다!

```java
private void initHandlerMappingMap() {
    handlerMappingMap.put("/front-controller/v5/v32/members/new-form",new MemberFormControllerV32());
    handlerMappingMap.put("/front-controller/v5/v32/members/save",new MemberSaveControllerV32());
    handlerMappingMap.put("/front-controller/v5/v32/members",new MemberListControllerV32());
    //v4 추가
    handlerMappingMap.put("/front-controller/v5/v4/members/new-form",new MemberFormControllerV4());
    handlerMappingMap.put("/front-controller/v5/v4/members/save",new MemberSaveControllerV4());
    handlerMappingMap.put("/front-controller/v5/v4/members",new MemberListControllerV4());
}
private void initHandlerAdapters() {
    handlerAdapters.add(new ControllerV32HandlerAdapter());//MyHandlerAdapter 구현체로 ControllerV32 추가!
    handlerAdapters.add(new ControllerV4HandlerAdapter());//MyHandlerAdapter 구현체로 ControllerV4 추가!
}
```

* MyHandlerAdapter를 상속받는 ControllerV4HandlerAdapter 구현\
  **핵심⭐️❤️**\
  **원래 ControllerV4는 String 타입의 View name 만을 반환하지만, 전체 어댑터 구조를 ModelView를 반환하도록 설계하였기에 이 View name을 가지고 어댑터가 ModelView를 생성해서 리턴하도록 한다!**\
  **ControllerV4에서와 똑.같.이 빈 Map 객체 model 먼저 생성하고, 컨트롤러에 넣어주면서 컨트롤러 내부에서 비즈니스 로직 실행하며 model에 데이터 넣어준다.**

```java
package hello.servlet.web.frontcontroller.v5.adapter;

import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v4.ControllerV4;
import hello.servlet.web.frontcontroller.v5.MyHandlerAdapter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class ControllerV4HandlerAdapter implements MyHandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return (handler instanceof ControllerV4);
    }

    @Override
    public ModelView handle(HttpServletRequest req, HttpServletResponse res, Object handler) throws ServletException, IOException {
        //handler를 V4로 캐스팅한다!(전체 부모 객체 Object로 들어온 애를 그 하위 버전 인터페이스로 캐스팅하는 것이다~!)
        ControllerV4 controller = (ControllerV4) handler;
        
        HashMap<String, Object> model = new HashMap<>();
        Map<String, String> paramMap = createParamMap(req);

        String viewName = controller.process(paramMap, model);//Model과 paramMap 두개 파라미터를 받는다!

        //어댑터는 viewName가지고 ModelView를 생성해준다!!!
        //Model과 View를 셋팅해준다!
        ModelView mv = new ModelView(viewName);//뷰 이름 셋팅
        mv.setModel(model);//모델 셋팅

        return mv;
    }
    private Map<String, String> createParamMap(HttpServletRequest req) {
        //컨트롤러의 process에 Map<String,String> paramMap을 넘겨줘야한다!!!!!!
        Map<String,String> paramMap = new HashMap<>();
        req.getParameterNames().asIterator()
                .forEachRemaining(paramName->paramMap.put(paramName, req.getParameter(paramName)));
        return paramMap;
    }
}

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://heunnajo.gitbook.io/mvc/4.-mvc/2-v5.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
