기존 코드를 클라이언트가 사용하는 인터페이스의 구현체로 바꿔주는 패턴
클라이언트가 사용하는 인터페이스를 따르지 않는 기존 코드를 재사용할 수 있게 해준다
public class LoginHandler {
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 IllegalArgumentException();
}
}
}
먼저 클라이언트쪽인 LoginHandler 클래스이다. 이 클래스는 UserDetailsService 인터페이스로부터 userName으로 UserDetails를 받아와 로그인 처리를 하는 로직이 있다.
public interface UserDetails {
String getUsername();
String getPassword();
}
public interface UserDetailsService {
UserDetails loadUser(String username);
}
public class AccountService {
public Account findAccountByUsername(String username) {
Account account = new Account();
account.setName(username);
account.setPassword(username);
account.setEmail(username);
return account;
}
}
public class Account {
private String name;
private String password;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
UserDetailsService는 AccountService와는 현재 연관관계가 없다. 하지만 UserDetailsService의 loadUser 메서드를 구현하기 위해서 AccountService가 필요하다. 그럴때 두 인터페이스간 연관관계를 이어주기 위해 어댑터를 구현해주면 소스코드의 수정 없이 AccountService의 메서드를 호출할 수 있다.
UserDetails 인터페이스도 Account 클래스와 연관관계가 없는데 위와 마찬가지로 어탭터를 구현해 연결해 줄 수 있다.
public class AccountUserDetails implements UserDetails {
private Account account;
public AccountUserDetails(Account account) {
this.account = account;
}
@Override
public String getUsername() {
return account.getName();
}
@Override
public String getPassword() {
return account.getPassword();
}
}
AccountUserDetails가 UserDetails를 구현한 일종의 어댑터이다. Account를 인스턴스 변수로 갖고 사용해 UserDetails와 Account를 연결해 준다.
public class AccountUserDetailsService implements UserDetailsService {
private AccountService accountService;
public AccountUserDetailsService(AccountService accountService) {
this.accountService = accountService;
}
@Override
public UserDetails loadUser(String username) {
return new AccountUserDetails(accountService.findAccountByUsername(username));
}
}
AccountUserDetailsService가 UserDetailsService를 구현한 일종의 어댑터이다. AccountService를 인스턴스 변수로 갖고 사용해 UserDetailsService와 AccountService를 연결해 준다.
public class App {
public static void main(String[] args) {
AccountService accountService = new AccountService();
UserDetailsService userDetailsService = new AccountUserDetailsService(accountService);
LoginHandler loginHandler = new LoginHandler(userDetailsService);
String login = loginHandler.login("keesun", "keesun");
System.out.println(login);
}
}
- 장점
- 기존 코드를 변경하지 않고 원하는 인터페이스 구현체를 만들어 재사용할 수 있다
- 기존 코드가 하던 일과 특정 인터페이스 구현체로 변환하는 작업을 각기 다른 클래스로 분리하여 관리할 수 있다
- 단점
- 새 클래스가 생겨 복잡도가 증가할 수 있다. 경우에 따라서는 기존 코드가 해당 인터페이스를 구현하도록 수정하는 것이 좋은 선택이 될 수도 있다
'java' 카테고리의 다른 글
Design Patterns - Composite (0) | 2021.11.29 |
---|---|
Design Patterns - Bridge (0) | 2021.11.28 |
Design Patterns - Prototype (0) | 2021.11.22 |
Design Patterns - Builder (0) | 2021.11.21 |
Design Patterns - Abstract Factory (0) | 2021.11.20 |
댓글