클라이언트가 사용하는 인터페이스가 전혀 다르며 정해져 있고, 기존의 코드를 호환하여 재사용할 수 있도록 구현체로 변경해주는 패턴.
클라이언트는 타겟 인터페이스만으로 사용하고, 구현체를 담당하는 Adaptee
와 타겟 사이의 Adapter
를 활용한다.
Security package
는 다른 애플리케이션에서도 사용할 수 있지만, Account
및 Service
는 특정 애플리케이션에서만 사용한다. 즉, Security package
는 타겟
에 해당하며, Account와 관련한 특정 애플리케이션에서 사용하는 코드
는 Adaptee
에 해당한다. 따라서 두 코드들을 호환이 가능하도록 Adapter를 구현
해야하 한다.
public class LoginHandler {
//target
UserDetailService userDetailService;
public LoginHandler(UserDetailService userDetailService) {
this.userDetailService = userDetailService;
}
public String login(String username, String password) {
//target
UserDetails userDetails = userDetailService.loadUser(username);
if(userDetails.getPassword().equals(password)) {
return userDetails.getUsername();
} else {
throw new IllegarArgumentException();
}
}
}
어댑터를 만드는 방법은 각 인터페이스 구성요소에 임의로 수정할 수 없는 상황에 대해 별도의 클래스를 구현하거나, 타겟 인터페이스를 상속하여 직접 구현하는 방법으로 구분할 수 있다.
직접 구현하는 어댑터
장점 : 새로운 클래스를 만들지 않아 코드의 복잡도를 줄일 수 있다.
단점 : 단일 체계의 원칙의 관점에서는 맞지 않고, 기존의 코드를 변경한다.
Open-Closed Principal
에 가까운 패턴이다.List<String> strings = Arrays.asList("a", "b", "c");
배열을 리스트로 변경하는 경우, 상이한 인터페이스를 변환하는 경우에 적용된다.
파라미터를 Collection으로 받으며 enumeration으로 변경하는 동작을 수행한다.
Enumeration<String> enmeration = Collections.enumeration(string);
ArrayList<String> list = Collections.list(enumeration);
// String -> InputStream
// InputStream -> InputStreamReader
try (InputStream is = new FileInputStream("input.txt");
InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {
while(reader.ready()) {
System.out.println(reader.readLine());
}
}catch(IOException e) {
throw new RuntimeException(e);
}
}
예제와 비슷함
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hi";
}
}
DispatcherServlet
을 보면 Object
타입을 받아 Handler
로 보내 요청을 처리하고, 모델과 View가 나오며 데이터를 넣어 렌더링을 한다.
Handler
는 요청을 처리하며, 구성에 따라 다른 Handler Adapter
를 사용하게 된다. Handler
는 이벤트에 따라 다르게 처리하고, Handler Adapter
sms 다양한 형태의 Handler
를 지원할 수 있도록 Spring MVC가 고안한 interface
이다.