[구조 패턴] Chapter 6-1. Adapter Pattern : 패턴 소개
코드 설명은 이전 포스팅을 참조
✍️ 어댑터 패턴, 적용하기
본 포스팅에선 어댑티와 클라이언트의 간극을 어댑터로 메꾸는 코드를 작성해 소개할 생각입니다.
예시 코드에서 클라이언트 코드는 LoginHandler에 해당하고, 클라이언트가 사용하는 UserDetails와 UserDetailsService는 Target interface에 해당한다.
public class LoginHandler {
private UserDetailsService userDetailsService;
public LoginHandler(UserDetailsService userDetailsService){
this.userDetailsService = userDetailsService;
}
public String login(String username, String password){
UserDetails userDetails = userDetailsService.loadUser(username);
if(userDetails.getPassword().equals(password)){
return userDetails.getUsername();
}else{
throw new RuntimeException();
}
}
}
public interface UserDetails {
String getUsername();
String getPassword();
}
public interface UserDetailsService {
UserDetails loadUser(String username);
}
여기까지가 security 패키지에 해당한다.
이제부터의 관심사는 각각의 애플리케이션마다 따로 정의하는 Account와 AccountService를 각각 UserDetails와 UserDetailsService에 어떻게 연결할지에 관한 이슈이다. 대표적으로 두 가지 해결법이 있는 듯하다. 하나씩 살펴보자.
@Data
public class Account {
private String name;
private String password;
private String email;
}
public class AccountService {
public Account findAccountByUsername(String username){
Account account = new Account();
account.setName(username);
account.setPassword(username);
account.setEmail(username);
return account;
}
public Account createNewAccount(String username){
// TODO, blah blah blah
return new Account();
}
}
🍊 패턴 적용 # 1
첫 번째는 어댑터를 별도의 구현체로 정의하는 방법이다.
클라이언트가 사용하는 인터페이스의 규약을 만족하기 위해 Target interface의 구현체를 만들고 내부에 어댑티에 해당하는 인스턴스를 멤버로 갖는 방식이다.
Target interface를 구현한 어댑터 클래스 AccountUserDetails, AccountUserDetailsService
@AllArgsConstructor
public class AccountUserDetails implements UserDetails {
private Account account;
@Override
public String getUsername() {
return account.getName();
}
@Override
public String getPassword() {
return account.getPassword();
}
}
@AllArgsConstructor
public class AccountUserDetailsService implements UserDetailsService {
private AccountService accountService;
@Override
public UserDetails loadUser(String username) {
Account account = accountService.findAccountByUsername(username);
return new AccountUserDetails(account);
}
}
적용 코드
public class App {
public static void main(String[] args) {
AccountService accountService = new AccountService();
UserDetailsService userDetailsService = new AccountUserDetailsService(accountService);
LoginHandler loginHandler = new LoginHandler(userDetailsService);
String res = loginHandler.login("hello", "hello");
System.out.println(res);
}
}
// hello
어댑터에 해당하는 별도의 구현체를 정의하면 기존의 코드는 전혀 손대지 않고 패턴을 적용할 수 있는 장점이 있다. 가령 어댑티와 Target interface에 해당하는 코드를 수정할 수 없는 상태라면 이와 같이 별도의 구현체를 정의하는 것이 합리적인 선택일 것이다.
✨ 패턴 적용 # 2
반대로 어댑티에 해당하는 코드를 수정할 수 있는 상태라면 두 번째 방식이 더 간결하고 직관적일 수 있다.
두 번째 방법으로 어댑티 자체가 Target interface의 구현체가 되는 방법이다.
@Data
public class Account implements UserDetails {
private String name;
private String password;
private String email;
@Override
public String getUsername() {
return name;
}
@Override
public String getPassword() {
return name;
}
}
public class AccountService implements UserDetailsService {
public Account findAccountByUsername(String username) {
Account account = new Account();
account.setName(username);
account.setPassword(username);
account.setEmail(username);
return account;
}
public Account createNewAccount(String username) {
// TODO, blah blah blah
return new Account();
}
@Override
public UserDetails loadUser(String username) {
return findAccountByUsername(username);
}
}
적용 코드
public class App {
public static void main(String[] args) {
AccountService accountService = new AccountService();
LoginHandler loginHandler = new LoginHandler(accountService);
String res = loginHandler.login("hello", "hello");
System.out.println(res);
}
}
// hello
첫 번째 방법과 비교했을 때 단점은 기존 코드가 특정 인터페이스를 구현하도록 강제한다는 점이다. 반면 장점으로 별도의 어댑터 구현체를 두지 않아도 된다는 장점이 있다.
단일 책임 원칙 관점에서 보자면 어댑터 구현체를 두는 것이 조금 더 객체지향에 가깝지 않나 싶다. 하지만 항상 원칙을 고수하기보단 현재 상황에 맞는 실용적인 판단도 중요하니 상황을 보고 판단하면 좋을 듯하다.
인프런의 백기선님의 강의 코딩으로 학습하는 GoF의 디자인 패턴을 참고해서 작성했습니다.
'Java > Design Pattern with Java' 카테고리의 다른 글
[구조 패턴] Chapter 8. Composite Pattern (0) | 2023.12.10 |
---|---|
[구조 패턴] Chapter 7. Bridge Pattern (0) | 2023.11.12 |
[구조 패턴] Chapter 6-1. Adapter Pattern : 패턴 소개 (0) | 2022.06.04 |
[객체 생성 패턴] Chapter 5-4. Prototype Pattern : Java에서 찾아보는 프로토타입 패턴 (0) | 2022.06.04 |
[객체 생성 패턴] Chapter 5-3. Prototype Pattern : 장단점 (0) | 2022.05.30 |